非阻塞模式select模型
要同时接收多个客户端就不能单纯用accept了,因为accept会阻塞程序的运行,用多线程也不能实现,因为客户端断开时情况十分复杂,可能一个线程开出来就再也关不掉了,导致电脑线程开太多死机,所以被逼无奈得用非阻塞模式的select模型,这样就算连接客户端程序也不会阻塞住,能干很多其他的事情。
首先介绍设为非阻塞模式的函数
int ioctlsocket(SOCKET s,long cmd,u_long* argp);
// cmd 为发给套接字的i/o控制命令,取值如下
// FIONBIO:设置或清除阻塞模式命令,当argp非0时将s设为非阻塞模式。WSAAsynSelect会讲套接字自动设为非阻塞模式,这时再调用这个函数设为非阻塞模式会报错
// FIONREAD: 用于确定套接字s自动读入数据量的命令,若s是流套接字类型,argp得到函数recv一次调用时可读入的数据量,是数据报套接字就得到第一个数据包的大小。
// FIOASYNC:设置或清除异步i/o的命令。
这个函数是在客户端上用的。
然后介绍select这个系列的函数。
// 套接字集合
typedef struct fd_set {
        u_int fd_count;               /* how many are SET? */
        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;
// 初始化 FD_ZERO(fd_set*); // 增 FD_SET(SOCKET,fd_set*); // 删 FD_CLR(SOCKET,fd_set*); // 查 FD_ISSET(SOCKET,fd_set*); // 貌似没有改的操作,总之这系列的函数就是简单的动态数组的实现,而且还是内存固定的动态数组
int select(int nfds,fd_set* readfds,fd_set* writefds,fd_set* exceptfds,const struct timeval * timeout); // 第一个参数被忽略,传0即可 // 第二个参数是可读的套接字集合 // 第三个参数是可写的套接字集合 // 第四个参数是出错的套接字集合 // 最后一个参数是等待时间的指针,传NULL则无限等待
select其实是个阻塞函数,但它把accept,send,recv的阻塞时间放到一起来等待的,并且是所有套接字集合里的套接字的等待时间都放一起了,因此才显得方便。
服务器端:
#include<WinSock2.h> #include<Windows.h> #include<strsafe.h> #pragma comment(lib,"Ws2_32.lib") int main() { WSAData wsa; WSAStartup(MAKEWORD(2, 2), &wsa); SOCKET serveSocket = socket(AF_INET, SOCK_STREAM, 0); sockaddr_in serveAddress; serveAddress.sin_addr.S_un.S_addr = htonl(ADDR_ANY); serveAddress.sin_family = AF_INET; serveAddress.sin_port = htons(6000); bind(serveSocket, (sockaddr*)&serveAddress, sizeof(serveAddress));// 绑定 listen(serveSocket, 5);// 监听 fd_set fdSocket; FD_ZERO(&fdSocket); FD_SET(serveSocket, &fdSocket); while (true) { fd_set fdRead = fdSocket; int iRet = select(0, &fdRead, NULL, NULL, NULL); if (iRet > 0) { for (size_t i = 0;i < fdSocket.fd_count;i++) { if (FD_ISSET(fdSocket.fd_array[i], &fdRead))// 如果连到服务器的套接字在可读的区域里 { if (fdSocket.fd_array[i] == serveSocket)// 如果可读的是自己 { if (fdSocket.fd_count < FD_SETSIZE) { sockaddr_in clientAddress; int clientlen = sizeof(clientAddress); SOCKET clientSocket = accept(serveSocket, (sockaddr*)&clientAddress, &clientlen);// 接受 FD_SET(clientSocket, &fdSocket); printf("接收到连接:%s\n", inet_ntoa(clientAddress.sin_addr)); } else { printf("连接太多"); } } else { char szText[256]; int iRecv = recv(fdSocket.fd_array[i], szText, 128, 0); if (iRecv > 0) { szText[iRecv] = '\0'; printf("接收到数据:%s\n", szText); } else { closesocket(fdSocket.fd_array[i]); FD_CLR(fdSocket.fd_array[i], &fdSocket); } } } } } else { printf("故障了:%d\n", WSAGetLastError()); closesocket(serveSocket); WSACleanup(); break; } } shutdown(serveSocket, SD_RECEIVE); WSACleanup(); return 0; }
客户端:
#include<WinSock2.h> #include<strsafe.h> #pragma comment(lib,"Ws2_32.lib") int main() { WSAData wsa; WSAStartup(MAKEWORD(2, 2), &wsa); SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in serveAddress; serveAddress.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); serveAddress.sin_port = htons(6000); serveAddress.sin_family = AF_INET; if (SOCKET_ERROR == connect(clientSocket, (sockaddr*)&serveAddress, sizeof(serveAddress))) { printf("连接到服务器失败"); getchar(); return 0; } int iMode = 1; int retVal = ioctlsocket(clientSocket, FIONBIO, (u_long*)&iMode); if (retVal == SOCKET_ERROR) { printf("ioctlsocket failed!"); WSACleanup(); return -1; } printf("clinet is running...\n"); while (true) { retVal = connect(clientSocket, (sockaddr*)&serveAddress, sizeof(serveAddress)); if (SOCKET_ERROR == retVal) { int err = WSAGetLastError(); if (err == WSAEWOULDBLOCK || err == WSAEINVAL)// windows异步系统将要阻塞 { Sleep(1); printf("check connect\n"); continue; } else if (err == WSAEISCONN)break; else { printf("connection failed\n"); closesocket(clientSocket); WSACleanup(); return -1; } } } while (true) { printf("请输入要发送的内容:"); char sendBuf[128]; scanf("%s", sendBuf); if (strcmp(sendBuf, "quit") == 0)break; while (true) { retVal=send(clientSocket, sendBuf, strlen(sendBuf), 0); if (SOCKET_ERROR == retVal) { int err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) { Sleep(5); continue; } else { printf("send failed\n"); closesocket(clientSocket); WSACleanup(); return -1; } } break; } } getchar(); getchar(); closesocket(clientSocket); WSACleanup(); return 0; }
                    
                
                
            
        
浙公网安备 33010602011771号