Socket概述及TCP/IP的C++实现
网络通信实际是应用进程之间的通信,而要完整的描述一个应用进程在网络中的位置必须用 IP+端口;
Socket就是一种在网络中进行数据通信的一种抽象描述。它是一种协议,本地地址,本地端口的抽象。
Socket它是面向C/S模型而设计的。
Windows Sockets 规范,又称为WinSock,是微软联合其他几家公司推出的Windows 操作系统环境下的网络编程接口。它继承了UNIX下的Socket,是Windows下标准、通用的TCP/IP编程接口。
目前推出了1.1版本,这个版本只支持TCP/IP协议;2.x以后,支持更多的网络与协议规范。比如无线网络通信等。
版本 1.1 头文件 WINSOCK.h 链接库文件 wsock32.lib 动态库文件 Winsock.dll
版本 2.2 头文件 WINSOCK2.h 链接库文件 ws2_32.lib 动态库文件 WS2_32.dll
Winsock 提供了两种形式的Socket:流式套接字(stream socket)和 数据报套接字(datagram socket)。其中,流式套接字只TCP协议,数据报套接字支持UDP协议。
以下是基于套接字的TCP协议实现过程:
基于流式套接字的编程模式如下:
基于数据报套接字的编程模式如下:
下面这个是一个特例,也就是说在UDP中也可以由connect(),通过connect()将本地的UDP 的socket与远程的socket连接起来。
4、“有连接”的UDP
虽然UDP是无连接的,但是也可以通过调用connect()将本地的UDP socket FD与一个远程的UDP socket FD连接起来——只需要指定这个远程sockFD的地址,假设这个地址是sockaddr_in remoteSockAddr,代码如下:
复制代码 建立连接后的UDP RecvQ就不会将非来自remoteSockAddr的数据包收入。 请注意UDP的connect()与TCP的 connect()很不相同,TCP是连接服务器的监听socket,并且会阻塞直到服务器调用accept()。一般的说法,UDP的连接并不会改变 UDP的各种特点,比如,即使连接,UDP也不知道远程主机是否在线连接或者是否断开——但是,我个人认为,改变了本机的RecvQ接收数据包的过滤机制,也就改变了UDP原本可以接收来自任何地址信息的属性。 如果希望断开UDP的连接,需要使用一个特定的“断开”地址,代码如下:
复制代码 请注意这里的地址族AF_UNSPEC直接赋值给了一个sockaddr结构。我试过,使用sockaddr_in也是可以的,但是无论是哪个结构,首先都得将整个结构对象清零,否则可能报错。 |
注:我们可以利用WinSock API函数也可以利用MFC提供的WinSock封装类。
有一点小疑惑,网络编程中为何不需要客户端IP地址及端口号即:不需要客户端套接字地址。
因为,我们在计算机网络中,我们在传数据时,它会自动带上本机的IP地址。比如,客户端向服务器端发送某个
数据报时,它肯定会在报头加上源IP地址,而目的IP地址及端口号这个就需要自己手动加入数据报中,不然,当路由器进行解析
的时候,就不知道该往哪个网络里面的主机里面传送了。
根据上面的介绍,我想大家一定想练练手,好,下面我将自己的demo提供给大家。最后会提供给大家一个链接的。
废话不多说,直接看结果:以下是UDP传输结果,上面是服务器端程序,显示的数据是从客户端发过去的。
第二个界面与上面相反,客户端程序,显示的数据从服务器端发过来的。当然,我们在实际编程时,可以有的放矢,
有时候不需要回传数据进行验证之类的。不是双工模式,而是单工模式。
以下是TCP的执行结果:
先打开服务器端,显示如下:
再开启客户端,显示如下:
不多解释,一切全在代码中。
socketserver.cpp文件:
// socketserver.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include "conio.h"
#include "windows.h"
//socket头文件
#include "winsock.h"
//socket库的lib
#pragma comment(lib,"ws2_32.lib") void TCPServer()
{
/***************创建服务器端套接字SOCKET*******************/
/*******socket()函数解释:IP协议族,数据流方式,TCP协议****/
SOCKET socksvr=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(INVALID_SOCKET == socksvr)
{
return;
}
/*************建立服务器端套接字地址***********************/
/********************绑定IP和端口号******************/
struct sockaddr_in svraddr = {};
svraddr.sin_family = AF_INET;//代表internet协议族
/**htons()函数解释:是将u_short型变量从主机字节顺序变换为TCP/IP网络字节顺序**/
/**这里涉及大小端系统问题。intel处理器是低位字节在****************/
/**较低地址存放,而高位字节在较高地址存放,与网络字节顺序相反,故需要调换过来****/
svraddr.sin_port = htons();
//htonl()函数是将u_long型变量从主机字节顺序变为TCP/IP网络字节顺序。
svraddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//此宏为0,当前机器上任意IP地址,也可以指定当前机的ip和端口。
//绑定,将服务器端套接字与服务器端套接字地址绑定
bind(socksvr,(struct sockaddr *)&svraddr,sizeof(svraddr));//指定名字,类型,长度。绑定套接字。
//侦听
listen(socksvr,SOMAXCONN);//第一个参数是套接字,第二个参数是等待连接队列的最大长度。
//等候客户端建立连接
printf("等候客户端.......\n");
//建立客户端套接字地址,主要是为了接收客户端返回参数之用
struct sockaddr_in clientaddr = {};
int nLen = sizeof(clientaddr);
//以下是建立客户端套接字并建立连接函数。有一个确认的过程。
//注:后面填的是客户端地址长度的地址。
SOCKET sockclient = accept(socksvr,(struct sockaddr*)&clientaddr,&nLen);//建立连接函数
printf("客户端已连接\n");
/********以下是数据收发部分*********/
//先接收后发送,由上面知,数据已在sockclient中,我们只需读此结构便可知晓数据
CHAR szText[] = {};
//接收缓冲区数据
recv(sockclient,szText,,); //接收函数,一直处于侦听模式,等待服务器端发送数据的到来。
printf("%s\n",szText);
CHAR szSend[] = "Hello Client";
send(sockclient,szSend,sizeof(szSend),);//发送函数。
/****accept/recv/send 都是堵塞函数,需要把所以的数据都接收完或发送完才可以工作。*****/
// getch();//暂停一下
//关闭socket
closesocket(sockclient);
closesocket(socksvr); } void UDPServer()
{
//创建socket
SOCKET socksvr = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(INVALID_SOCKET == socksvr)
{
return ;
}
//服务器套接字地址
//绑定ip与端口,先定义端口等一些信息。
struct sockaddr_in svraddr = {};
svraddr.sin_family = AF_INET;
svraddr.sin_port = htons();
svraddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
bind(socksvr,(struct sockaddr*)&svraddr,sizeof(svraddr)); /********以下是数据收发部分*********/
//客户端套接字地址,接收客户端数据时需要用,数据都在套接字里面。
CHAR szRecv[] = {};
struct sockaddr_in clientaddr = {};
int nLen = sizeof(clientaddr);
/*下面函数前四个参数同TCP接收数据函数recv()一样,后两个中,一个是返回发送*******/
/*数据地址的主机的地址,包括IP地址以及端口号,最后一个为地址长度的地址。*******/
/*此函数中,先是服务器端的套接字,后是客户端的地址*/
//从后往前读此函数
recvfrom(socksvr,szRecv,,,(struct sockaddr*)&clientaddr,&nLen);//构造ip地址
printf("%s\n",szRecv); //注1:该程序也可以向客户端发送数据。
//注2:服务器端中,必须也是先接收后发送,不然,我们无法知道客户端的地址。下面函数中clientaddr已知晓
CHAR szSend[] = "hello udp client";
//从前往后读此函数
sendto(socksvr,szSend,,,(struct sockaddr*)&clientaddr,nLen);//发送时构造ip地址和端口。 // getch();//可以暂停显示,这个很重要。 //关闭socket
closesocket(socksvr); } int main(int argc, _TCHAR* argv[])
{
//初始化socket库
WSADATA wsa = {}; //WinSockApi 取WSA+DATA组成套接字结构体
WSAStartup(MAKEWORD(,),&wsa);
//服务器
TCPServer();
//UDPServer();
//清理套接字资源
WSACleanup();
getch();//暂停一下 return ;
}
socketclient.cpp 文件:
// socketclint.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include "conio.h"
#include "windows.h"
#include "winsock.h"
#pragma comment(lib,"ws2_32.lib") void TCPClient()
{
//创建socket
SOCKET sockclient = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(INVALID_SOCKET == sockclient)
{
return;
}
//连接服务器,建立服务器端套接字地址
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons();
//对于inet_addr()函数,它是把“xxx.xxx.xxx.xxx”形式表示的IPV4地址,转换为IN_ADDR结构体能够
//接收的形式(unsigned long型,因为IN_ADDR结构体中的负责接收的S_addr成员变量的类型是unsigned long型)
addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//本机ip //向服务器发出连接请求,当然我们也可以通过connet函数的返回值判断到底有无连接成功。
int iRetVal = connect(sockclient,(struct sockaddr*)&addr,sizeof(addr));
if(SOCKET_ERROR == iRetVal)
{
printf("服务器连接失败!");
closesocket(sockclient);
return;
}
printf("服务器连接成功!\n");
//数据收发
CHAR szSend[] = "hello server"; //客户端 先发后收
send(sockclient,szSend,sizeof(szSend),); //发送函数,可以通过返回值判断发送成功与否。 //接收服务器回传的数据
CHAR szRecv[] = {};
recv(sockclient,szRecv,,); //接收函数
printf("%s\n",szRecv);//表示以字符串的格式输出服务器端发送的内容。 // getch();//暂停一下
//关闭socket
closesocket(sockclient);
}
void UDPClient()
{
//创建SOCKET ,ip协议族,数据报方式,udp协议。
SOCKET sockclient = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(INVALID_SOCKET == sockclient)
{
return ;
}
//数据收发,服务器端套接字地址
struct sockaddr_in svraddr = {};
svraddr.sin_family = AF_INET;
svraddr.sin_port = htons();
svraddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//指定服务器端的IP与端口。
CHAR szSend[] = "hello udp server";
/*此函数先是客户端的套接字,然后是服务器端地址*/
//简单理解为:从函数前面的客户端套接字的发送数据缓存区中将数发送给服务器端地址
sendto(sockclient,szSend,,,(struct sockaddr*)&svraddr,sizeof(svraddr));//发送时构造ip地址和端口。 //注:该程序也可以接收服务器端回传的数据。
CHAR szRecv[];
//简单理解为:从函数后面的服务器端地址中取数到客户端套接字的接收缓冲区szRecv中
int len = sizeof(svraddr);
recvfrom(sockclient,szRecv,,,(struct sockaddr*)&svraddr,&len);
printf("%s \n",szRecv);
//关闭socket
closesocket(sockclient);
} int main(int argc, _TCHAR* argv[])
{
//初始化socket库
WSADATA wsa = {};
WSAStartup(MAKEWORD(,),&wsa);
//tcp客户端
TCPClient();
//UDPClient();
//清理套接字资源
WSACleanup();
getch(); return ;
}
已传至PUDN,文件名1_1-socket,用户可自行下载。
Socket概述及TCP/IP的C++实现的更多相关文章
- Socket网络编程(TCP/IP/端口/类)和实例
Socket网络编程(TCP/IP/端口/类)和实例 原文:C# Socket网络编程精华篇 转自:微冷的雨 我们在讲解Socket编程前,先看几个和Socket编程紧密相关的概念: TCP/IP层次 ...
- Linux系统编程(30)—— socket编程之TCP/IP协议
在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别.就好像圣经中上帝打乱了各地人的口音,让他们无法合作一样.计算机使用者意识到,计算机 ...
- 第一章 概述——1.TCP/IP设计遵循的两个原则
1.端到端原则(end-to-end principle) 当我们设计一个大的系统(如操作系统或协议族)时,随之而来的一个问题通常是在什么位置实现某个功能.影响TCP/IP协议族设计的一个重要原则是端 ...
- Python中的socket网络编程(TCP/IP,UDP)讲解
在网络编程中的一个基本组件就是套接字(socket).套接字基本上是两个端点的程序之间的"信息通道".程序可能分布在不同的计算机上,通过套接字互相发送信息.套接字包括两个:服务器套 ...
- tcp/ip详解 卷1 -- 协议概述
第一章 概述 分层 TCP/IP 通常被认为是一个四层协议系统. 每一层负责不同的功能. 链路层, 也成为数据链路层或者网络接口层. 通常包括 操作系统中的设备驱动程序和计算机中对应的网络接口卡. 主 ...
- socket http tcp ip 区别联系
功能是实现继承复用.刚才做了一个简要的概述,里面有一些常用的概念,这里做个简短的概念普及介绍:(1),TCP/IP------TPC/IP协议是传输层协议,主要解决数据如何在网络中传输.(2),Soc ...
- 门面模式的典型应用 Socket 和 Http(post,get)、TCP/IP 协议的关系总结
门面模式的一个典型应用:Socket 套接字(Socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元.它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息: 连接使用的 ...
- TCP/IP、Http、Socket、XMPP-从入门到深入
TCP/IP.Http.Socket.XMPP-从入门到深入 终极iOS程序猿 2016-12-29 18:27 为了便于大家理解和记忆,我们先对这几个概念进行的介绍,然后分析他们的不同,再进行详细的 ...
- ios开发网络知识 TCP,IP,HTTP,SOCKET区别和联系
TCP,IP,HTTP,SOCKET区别和联系 网络由下往上分为: 对应 物理层-- 数据链路层-- 网络层-- IP协议 传输层-- ...
随机推荐
- tmux使用备忘
创建新的session tmux 查看已有session tmux ls 进入tmux后 默认快捷键前缀为Ctrl+b,可以通过配置文件来修改 从session中断开 C-b d 给session改名 ...
- FreeBSD查看带宽占用情况,CPU,硬盘IO 虚拟内存等命令
FreeBSD查看带宽占用情况,CPU,硬盘IO 虚拟内存等命令 来源 https://www.liurongxing.com/freebsd-tips.html 来源 http://blog.51c ...
- 如何在 Windows 7 上安装 TeX Live 2018
$\color{red}{\mathsf{UPDATE}}$:见李阿玲在知乎专栏 All about TeXnique 发布的安装教程 关于 TeX Live:http://tug.org/texli ...
- 洛谷4438 [Hnoi2018]道路 【树形dp】
题目 题目太长懒得打 题解 HNOI2018惊现普及+/提高? 由最长路径很短,设\(f[i][x][y]\)表示\(i\)号点到根有\(x\)条未修公路,\(y\)条未修铁路,子树所有乡村不便利值的 ...
- react当中子组件改变父组件的状态
子组件直接改变父组件传入的props值是不被允许的, 当需要在子组件当中改变父组件的某一个状态, 父组件传入一个改变状态的函数,然后在子组件当中调用函数即可
- H5 语义化、基本事件 浅析 (含file对象、drag拖拽等)
1.语义化标签 帮助搜索引擎,盲人设备等程序,辨识网页内容信息,明确网页区域分布,不体现任何样式,但存在浏览器兼容性问题,如IE8下无<header>标签. ① H5基本语义标签: < ...
- 最长k可重区间集(cogs 743)
«问题描述:«编程任务:对于给定的开区间集合I和正整数k,计算开区间集合I的最长k可重区间集的长度.«数据输入:由文件interv.in提供输入数据.文件的第1 行有2 个正整数n和k,分别表示开区间 ...
- Connect(bzoj 1948)
Description 给定一个R*C大小的迷宫,其中R,C均为奇数 迷宫中坐标为两个奇数的点不能通过,称为障碍,迷宫中其他不能通过的点统称为墙壁 坐标为两个偶数的点可以通过,称为房间,迷宫中其他可通 ...
- Springboot - 在启动完成后执行特定方法
1.实现方式 实现ApplicationRunner接口 实现CommandLineRunner接口 @Component @Slf4j public class AfterServiceStarte ...
- SPOJ QTREE4 SPOJ Query on a tree IV
You are given a tree (an acyclic undirected connected graph) with N nodes, and nodes numbered 1,2,3. ...