网络编程之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();