网络编程之TCP server
基本概念
socket:连接应用程序与网络驱动的桥梁
面向连接的套接字:即TCP,按顺序传输数据,数据不会丢失,不存在数据边界
面向消息的套接字:即UDP,强调快速传输,不一定按顺序传输,数据可能会丢失,存在数据边界
数据边界:会把数据拆分成多个进行传输
C/S模式:服务器先启动,根据请求提供相应的服务,打开一个通信通道,在某一地址和端口上接受请求,等待客户的请求,接受到请求后,发送应答信息,完成后,又打开通信通道,等待另外的请求,最后关闭服务器
客户端:打开通信通道,连接主机,向主机发送请求,等待并接受应答,继续发送请求,最后关闭通道
套接字:
套接字类型 | 代表的协议 | VS中查看定义 | 特点 |
SOCK_STREAM | TCP | #define 1 | 面向连接、不支持广播、多播 |
SOCK_DGRAM | IP | #define 2 | 无连接,支持广播、多播 |
SOCK_RAW | #define 3 |
基本函数
socket:创建套接字 ======== bind: 套接字绑定地址和端口
connect: 请求连接 ======== listen:监听
accept:接受请求 ======== close:关闭连接
send:用于TCP,发送数据 ======= recv:用于TCP,接受数据
sendto:用于UDP,发送数据 ====== recvfrom:用于UDP,发送数据
数据结构:
sockaddr
typedef struct sockaddr
{
u_short sa_family; /指定IPV4还是IPV6
CHAR sa_data[14]; /IP+port
}
这个是给系统用的,包含了16个字节,
typedef struct sockaddr_in
{
short sin_family; /指定IPV4还是IPV6 2字节
USHORT sin_port; /16位port号 2字节
IN_ADDR sin_addr; /32位ip地址 4字节
CHAR sin_zero[8]; /8位字节填充 8字节
}
这个是给程序员用的,因为可以将IP和port分开
代码
首先要包含头文件winsock2.h,还要引入库文件
#pragma comment(lib, "ws2_32.lib")
最开始,要记得初始化网络库,如果没有初始化,就会导致socket建立失败,如果初始化了,就会把消息阻塞在第五步,accept那里
...代码太长,不写了 /初始化套接字
第一步,创建套接字sockSrv
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
/AF_INET:协议族,sock-stream:TCP ,第三个默认为0
为创建增加容错机制
if (INVALID_SOCKET == sockSrv)
{
printf("socket errorNum = %dn", GetLastError());
return -1;
}
INVALID_SOCKET表示无效的socket
第二步,确定网络地址
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
/h-host,n-netework,l-long,将主机转换成网络长字节
/都是固定写法,而INADDR-ANY表示任意地址
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
/h-host,n-netework,s-short,将主机转换成网络短字节
/6000表示6000端口
定义一个程序员用的结构体addrSrv确定它的网络地址
第三步,绑定、监听,监听的第二个参数为5,表示等待连接的客户端为5个,而不是能同时连接5个客户端,顺序为先近先出,第6个只有等到第一个关闭了,它才能连接上,从左往右,先进先出
<<<======1 | 2 | 3 | 4 | 5 <<<===6 |
bind(sockSrv,(SOCKADDR *) &addrSrv, sizeof(SOCKADDR));
listen(sockSrv, 5);
绑定上面创建的套接字sockSrv和网络地址addrSrv,监听套接字sockSrv,为绑定和监听增加容错机制
if (SOCKET_ERROR ==bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)))
{
printf("bind errorNum = %dn", GetLastError());
return -1;
}
.../监听类似
第四步,定义一个系统用的网络地址
SOCKADDR_IN addrCli;
int len = sizeof( SOCKADDR );
第五步,定义一台分机处理客户端
while (true)
{
SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrCli, &len);
/准备发数据
char sendBuff[100] = { 0 };
/打印hello
sprintf_s(sendBuff, 100, "hello");
/发数据
int iLen = send(sockConn, sendBuff, strlen(sendBuff),0);
第六步,接收数据
char recvBuff[100] = { 0 };
iLen = recv(sockConn, recvBuff, 100, 0);
printf("%s", recvBuff);
最后,关闭分机,并清理socket
closesocket(sockConn);
WSACleanup();
}
closesocket(sockSrv);
WSACleanup();