目录

多路复用实例

服务端实例1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 这是多线程编程,这个函数只负责接受消息,不处理。
fd_set  fdread;                 //创建一个文件集合
SOCKET  SocketArr[60];          //保存每个链接用户的 socket 的一个数组
SOCKET  server_socket;          //是保存服务器监听端口的 socket
... // 对服务器网络的初始化,包括初始化 WSA,绑定 ip等等,网上很多,这里不多说,推荐文章: https://www.cnblogs.com/churi/archive/2013/02/27/2935427.html
while (true)
{
    FD_ZERO(&fdread);                            //清除监听文件集中的所有文件
    FD_SET(server_socket,&fdread);               //将 server_socket 加入监听文件集

    for (i = 0; i < 60; i++)     //循环将所有已经链接的客户 socket 加入文件集。
        if (SocketArr[i] !=-1)
            FD_SET(SocketArr[i],&fdread);

    //阻塞式监听,这里只要有一个文件收到信号就不会阻塞
    if ((Ret = select(0,&fdread, NULL, NULL, NULL)) == SOCKET_ERROR)
    {
        printf("selecterrror");
        break;
    }

    if (Ret > 0)
    {
        //如果服务器端的 socket 发生改变,即收到新建立的连接
        if(FD_ISSET(server_socket, &fdread))
        {
            printf("新连接\n");
            //找到一个没有分配的客户socket来保存客户端连接
            for (i = 0;i <60; i++)
                if(SocketArr[i] == -1)
                    break;
            //用上面找到的没有用的socket来保存客户端连接
            if((SocketArr[i] = accept(server_socket, NULL, NULL)) == INVALID_SOCKET)
            {
                printf("Acceptsocket failed! ");
                break;
            }
        }
        printf("消息处理\n");
        //如果客户端的 SocketArr 有变化,即受到客户端发来的数据。
        for (i = 0; i < 60;i++)
            if((SocketArr[i] != -1) && (FD_ISSET(SocketArr[i], &fdread)))
            {
                //在这里进行消息处理,处理链接为 SocketArr[i],我的处理是将该信息加入到消息队列,让专门处理信息的线程读队列来处理。
            }
    }
}

服务端实例2

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <stdio.h>
#include<WinSock2.h>
#include <WS2tcpip.h>
 
#pragma comment(lib,"ws2_32.lib")	//加载ws2_32.lib库
 
int main()
{
	WSADATA wsadata;
	WORD SockVersion = MAKEWORD(2,2);//初始化socker的版本
	if (WSAStartup(SockVersion,&wsadata)!=0)//WSAStartup是绑定socket版本号,指定操作系统调用那个版本的方法
	{
		printf("WSAStartup fail\n");
		return -1;
	}
 
	//创建scoket
	SOCKET ScoketFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (ScoketFd == INVALID_SOCKET)
	{
		printf("socket error !");
		return -1;
	}
 
	//绑定IP和端口
	struct sockaddr_in SockAddr;
	SockAddr.sin_family = AF_INET;
	SockAddr.sin_port = htons(1111);
	SockAddr.sin_addr.S_un.S_addr = INADDR_ANY;
	if (bind(ScoketFd, (SOCKADDR*)&SockAddr, sizeof(SOCKADDR)) == SOCKET_ERROR)
	{
		printf("bind error !\n");
		return -1;
	}
 
	//开始监听
	if (listen(ScoketFd, 10) == SOCKET_ERROR)
	{
		printf("listen error !\n");
		return -1;
	}
	
	// select模型  
	fd_set AllSocketSet;	//创建一个文件集合
	FD_ZERO(&AllSocketSet);	//清空集合
	FD_SET(ScoketFd,&AllSocketSet);//将ScoketFd加入sock集合
	int ret = 0;
 
 
	//接收数据
	SOCKET ClientConFd;
	struct sockaddr_in ClientAddr;
	int nSize = sizeof(ClientAddr);
	char RevData[1024] = { 0 };
	while (1)
	{
		//设置监听fd可读事件
		fd_set ReadFd;
		fd_set WriteFd;
		FD_ZERO(&ReadFd);
		FD_ZERO(&WriteFd);
		ReadFd = AllSocketSet;
		WriteFd = AllSocketSet;
 
		ret = select(0, &ReadFd, &WriteFd, NULL, NULL);//第一个参数nfds被忽略,是为了兼容版本
		if (ret == SOCKET_ERROR)
		{
			printf("select error\n");
			return -1;
		}
		
		if (FD_ISSET(ScoketFd, &ReadFd))	//检查ScoketFd是否在这个集合里面, 
		{					//select将更新这个集合,把其中不可读的套节字去掉 
							//只保留符合条件的套节字在这个集合里面 
											
			printf("start accept new connect\n");
			ClientConFd = accept(ScoketFd, (SOCKADDR*)&ClientAddr, &nSize);//等待连接,连接后会产生一个行的fd
			if (ClientConFd == INVALID_SOCKET)
			{
				printf("accept error !\n");
				return -1;
			}
			else
			{
				FD_SET(ClientConFd, &AllSocketSet);//将新产生的fd加入到原先的集合中
			}
 
		}
 
		for (u_int i = 1; i < AllSocketSet.fd_count; ++i)//第一个fd是监听连接的fd,第二个开始是连接上的fd
		{
 
			if (FD_ISSET(AllSocketSet.fd_array[i], &ReadFd))//通过轮询原来的集合,检查ScoketFd是否在ReadFd集合中
			{
				printf("接收一个连接:%s \n", inet_ntoa(ClientAddr.sin_addr));
				//接收数据
				ret = recv(AllSocketSet.fd_array[i], RevData, 1024, 0);
				if (ret == SOCKET_ERROR)
				{
					DWORD err = WSAGetLastError();
					if (err == WSAECONNRESET)// 客户端的socket没有被正常关闭,即没有调用closesocket
					{
						printf("Client is forced to close\n");
						closesocket(AllSocketSet.fd_array[i]);
						FD_CLR(AllSocketSet.fd_array[i], &AllSocketSet);//把AllSocketSet.fd_array[i]从AllSocketSet集合中删除
						break;
					}
					else
					{
						printf("recv fail\n");
					}
					
				}
				else if (ret == 0)//客户端的socket正常关闭
				{
					closesocket(AllSocketSet.fd_array[i]);
					FD_CLR(AllSocketSet.fd_array[i], &AllSocketSet);
					printf("Client closes normally");
				}
				else
				{
					printf("%s\n", RevData);//打印读取的数据
				}
				
			}
 
			if (FD_ISSET(AllSocketSet.fd_array[i], &WriteFd))
			{
				//发送数据
				char * SendData = "hello,TcpClient";
				send(AllSocketSet.fd_array[i], SendData, strlen(SendData), 0);
				
			}
		}	
		
	}
 
	for (u_int j = 0; j < AllSocketSet.fd_count; ++j)
	{
		SOCKET socket = AllSocketSet.fd_array[j];
		closesocket(socket);
	}
	WSACleanup();//终止 DLL 的使用
	
	return 0;
}

对应客户端2

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <WINSOCK2.H>
#include <STDIO.H>
 
#pragma  comment(lib, "ws2_32.lib")
 
 
int main()
{
	WSADATA wsadata;
	WORD SockVersion = MAKEWORD(2, 2);//初始化socker的版本
	if (WSAStartup(SockVersion, &wsadata) != 0)//WSAStartup是绑定socket版本号,指定操作系统调用那个版本的方法
	{
		printf("WSAStartup fail\n");
		return -1;
	}
 
	//创建scoket
	SOCKET ScoketFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (ScoketFd == INVALID_SOCKET)
	{
		printf("socket error !");
		return -1;
	}
 
	//连接服务端
	struct sockaddr_in ServerAddr;
	ServerAddr.sin_family = AF_INET;
	ServerAddr.sin_port = htons(1111);
	ServerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	if (connect(ScoketFd, (SOCKADDR *)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)
	{
		printf("connect error !\n");
		closesocket(ScoketFd);
		return 0;
	}
	//发送数据到服务端
	char * sendData = "hello,Tcp Server,i'm client";
	send(ScoketFd, sendData, strlen(sendData), 0);
 
	//读取服务端数据
	char revData[1024] = {0};
	int ret = recv(ScoketFd, revData, 1024, 0);
	if (ret > 0)
	{
		printf("%s\n", revData);
	}
	getchar();
	closesocket(ScoketFd);
	WSACleanup();
	return 0;
}

https://cdn.jsdelivr.net/gh/xinqinew/pic@main/img/20180424230138766.png