基于select模型的tcp服务器------一个服务器如何与多个客户端进行通信?
2015-03-17 22:00
711 查看
很多时候, 服务器都需要同时与多个客户端进行通信, 服务嘛, 就是这样。 下面, 我们用select模型来简要模拟一下这种情形。代码是最好的解释, 所以, 还是上代码吧:
服务端程序:
再看客户端程序:
socketArray[i] is 136, 1
socketArray[i] is 152, 2
socketArray[i] is 164, 3
socketArray[i] is 176, 4
socketArray[i] is 188, 5
socketArray[i] is 200, 6
socketArray[i] is 212, 7
socketArray[i] is 224, 8
socketArray[i] is 136, 111
可以看到, 服务端可以同时与各个客户端进行通信, 而且还不会混淆他们。 另外,从log.txt日中中可以看出, 尽管有8个客户端, 但服务端的监听socket是唯一的。有一个小小的问题值得我们注意: 在服务端, 我们listen函数的第二个参数是5, 很多人会认为是服务端最多允许5个客户端同时连接, 其实, 不是酱紫的。看看, 我们实战中就有8个客户端同时连接上了呢, 我们后续会对listen的第二个参数的具体含义进行更加详细的讨论。
最后要说明的是, 上述程序还是比较简单的, 没有考虑很多异常的情况, 比如, 某个客户端突然退出对服务器的影响。 如果以后玩程序的时候需要考虑更复杂的情况, 我们再完善。
服务端程序:
#include <stdio.h> #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") int totalSockets = 0; // socket的总数 SOCKET socketArray[100]; // socket组成的数组, 假设最多有100个socket吧 // 日志打印 void log(const char *pStr) { FILE *fp = fopen("log.txt", "a"); fprintf(fp, "log:%s\n", pStr); fclose(fp); } // 创建socket void addToSocketArr(SOCKET s) { socketArray[totalSockets] = s; totalSockets++; } // 启动服务器 int main() { log("into main"); // 网络初始化 WSADATA wsaData; WSAStartup(MAKEWORD(1, 1), &wsaData); // 创建socket SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, 0); addToSocketArr(listenSocket); // 服务地信息 SOCKADDR_IN srvAddr; srvAddr.sin_family = AF_INET; srvAddr.sin_addr.s_addr = htonl(INADDR_ANY); srvAddr.sin_port = htons(8888); // 绑定 bind(listenSocket, (SOCKADDR*)&srvAddr, sizeof(srvAddr)); // 监听 listen(listenSocket, 5); // 设置socket为非阻塞模式 unsigned long nonBlock = 1; ioctlsocket(listenSocket, FIONBIO, &nonBlock); while(1) { // 读集, 要记得清零初始化 FD_SET readSet; FD_ZERO(&readSet); // 将每个socket都塞入读集, 便于让内核来监测这些socket int i = 0; for(i = 0; i < totalSockets; i++) { FD_SET(socketArray[i], &readSet); } // 应用程序通知内核来监测读集中的socket, 最后的NULL表示超时时间无限长 int total = select(0, &readSet, NULL, NULL, NULL); // 我们不考虑select失败, 那么程序到这里, 说明读集中必有socket处于"就绪状态" for(i = 0; i < totalSockets; i++) { char szTmp[20] = {0}; sprintf(szTmp, "%d", i); log(szTmp); if(socketArray[i] == listenSocket) // 对监听的socket进行判断 { log("socketArray[i] == listenSocket"); if(FD_ISSET(listenSocket, &readSet)) // 如果该socket在可读集中, 则表明有客户端来连接 { log("listenSocket, socketArray[i] == listenSocket"); // 接收来自于客户端的connect请求 SOCKADDR_IN addrClient; int len = sizeof(SOCKADDR); SOCKET acceptSocket = 0; acceptSocket = accept(listenSocket,(SOCKADDR*)&addrClient, &len); // 设置为非阻塞模式 nonBlock = 1; ioctlsocket(acceptSocket, FIONBIO, &nonBlock); // 添加到socket数组中 addToSocketArr(acceptSocket); } continue; } // 注意:上面的listenSocket是不负责通信的, 下面的一些socket都是负责通信的socket // 如果通信socket处于读就绪状态 if (FD_ISSET(socketArray[i], &readSet)) { log("to receive"); char szRecvBuf[1024] = {0}; recv(socketArray[i], szRecvBuf, sizeof(szRecvBuf) - 1, 0); printf("socketArray[i] is %d, %s\n", socketArray[i], szRecvBuf); } } } // 省略了关闭socket等后续操作 return 0; }启动服务端。
再看客户端程序:
#include <winsock2.h> #include <stdio.h> #pragma comment(lib, "ws2_32.lib") int main() { WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(1, 1); WSAStartup( wVersionRequested, &wsaData ); SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0); SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(8888); connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); while(1) { char szSendBuf[100] = {0}; scanf("%s", szSendBuf); send(sockClient, szSendBuf, strlen(szSendBuf) + 1, 0); } closesocket(sockClient); WSACleanup(); return 0; }在此处, 我们编译并链接, 然后连续运行8次客户端(不要关闭), 也就产生了8个客户端进程, 并在8个客户端进程中依次输入1, 2, 3, 4, 5, 6, 7, 8后分别按Enter发送, 然后在第一个客户端进程中再次输入111, 并按Enter发送。 我们看看服务端的结果:
socketArray[i] is 136, 1
socketArray[i] is 152, 2
socketArray[i] is 164, 3
socketArray[i] is 176, 4
socketArray[i] is 188, 5
socketArray[i] is 200, 6
socketArray[i] is 212, 7
socketArray[i] is 224, 8
socketArray[i] is 136, 111
可以看到, 服务端可以同时与各个客户端进行通信, 而且还不会混淆他们。 另外,从log.txt日中中可以看出, 尽管有8个客户端, 但服务端的监听socket是唯一的。有一个小小的问题值得我们注意: 在服务端, 我们listen函数的第二个参数是5, 很多人会认为是服务端最多允许5个客户端同时连接, 其实, 不是酱紫的。看看, 我们实战中就有8个客户端同时连接上了呢, 我们后续会对listen的第二个参数的具体含义进行更加详细的讨论。
最后要说明的是, 上述程序还是比较简单的, 没有考虑很多异常的情况, 比如, 某个客户端突然退出对服务器的影响。 如果以后玩程序的时候需要考虑更复杂的情况, 我们再完善。
相关文章推荐
- 基于linux poll模型的tcp服务器------一个服务器如何与多个客户端进行通信?
- 基于非阻塞socket的多线程服务器的实现------一个服务器如何与多个客户端进行通信?
- 如何使用TCP/IP与服务器进行通信-一个简单的聊天程序
- 如何使用TCP/IP与服务器进行通信-一个简单的聊天程序
- 初玩linux epoll------一个服务器如何与多个客户端进行通信? (笔试面试常考)
- Select I/O模型来实现一个并发处理多个客户端的TCP服务器 <转>
- Android_TCP/IP开发——聊天室模型(客户端与服务器进行通信)
- 一个简单的基于node.js的TCP服务器和基于C++的TCP客户端通信示例程序
- 使用Select I/O模型来实现一个并发处理多个客户端的TCP服务器
- Select I/O模型来实现一个并发处理多个客户端的TCP服务器
- 基于TCP网络通信的自动升级程序源码分析-客户端连接服务器
- 手机客户端和服务器通信时如何安全高效的进行身份验证?
- C++基于TCP/IP简单的客户端、服务器通信程序实例
- 某实例+修改 Winsock实现基于TCP的客户端/服务器通信
- java Socket 一个服务器与多个客户端进行通信
- C++基于TCP/IP简单的客户端、服务器通信程序实例
- 基于TCP网络通信的自动升级程序源码分析-客户端请求服务器上的升级信息
- 一个基于线程池的WSAEventSelect模型服务器设计
- 初探基于TCP的服务器/客户端结构的聊天系统(二)之应用层通信协议设计
- 一个基于线程池的WSAEventSelect模型服务器设计