windows Socket编程之select网络模型
2016-08-08 23:08
225 查看
在此之前呢,介绍了TCP/UDP的服务端的实现。但是,它们有很大的缺点,比如说,效率很低,开销太大等。因此,接下来我们先介绍select网络模型。
我们在TCP的服务端里边,接收一个客户端的时候,我们调用accept函数,这个函数会返回一个客户端的socket,我们在主线程里边不停的接收客户的连接,每当有客户连接时,我们就会在开一个线程,用于对客户的服务。因此,如果有N个的客户进行连接的话,那么线程数量就会有N+1个(N个服务线程+主线程),若N比较大,则线程就会非常多,以至于将整个电脑都给拖垮掉。而我们的select模型呢,就是为了解决这个问题而设计的。接下来看下它是如何实现的。
首先,TCP的服务端一样,它需要初始化环境,然后执行绑定,监听等操作。但是之后,我们会直接开一个线程来对客户进行服务,然后才是我们原来的一个循环,来接待客户的连接。接下来看一个结构体,其声明如下:
我们把服务客户的那个线程叫做工作者线程,在里边我们会调用一个函数叫做select,其声明如下:
这个函数会检查fd_array这个数组里边所有的socket是否有信号到来,如果有就成功返回,否则会阻塞在这里,不过我们在最后一个参数那里,传一个等待时间。
调用完select之后,我们可以在调用FD_ISSET这个宏来判断是fd_array这个数组里边的那个socket有信号了。之后我们就可以进行数据收发了。
以下是select的示例代码:
我们在TCP的服务端里边,接收一个客户端的时候,我们调用accept函数,这个函数会返回一个客户端的socket,我们在主线程里边不停的接收客户的连接,每当有客户连接时,我们就会在开一个线程,用于对客户的服务。因此,如果有N个的客户进行连接的话,那么线程数量就会有N+1个(N个服务线程+主线程),若N比较大,则线程就会非常多,以至于将整个电脑都给拖垮掉。而我们的select模型呢,就是为了解决这个问题而设计的。接下来看下它是如何实现的。
首先,TCP的服务端一样,它需要初始化环境,然后执行绑定,监听等操作。但是之后,我们会直接开一个线程来对客户进行服务,然后才是我们原来的一个循环,来接待客户的连接。接下来看一个结构体,其声明如下:
typedef struct fd_set { u_int fd_count; // 有多少个socket SOCKET fd_array[FD_SETSIZE]; // 客户端socket数组,FD_SETSIZE为64 } fd_set;当我们调用accept来获取客户端的连接之后,会调用FD_SET这个宏,它实际上是会将我们的客户那个socket保存到fd_array这个数组里边去,因为这个数组最大为64个,所以最多只能有64个客户端进行连接。
我们把服务客户的那个线程叫做工作者线程,在里边我们会调用一个函数叫做select,其声明如下:
int select( int nfds, //已经忽略了 fd_set FAR *readfds, //可读fd_set的地址 fd_set FAR *writefds, //可写fd_set地址 fd_set FAR *exceptfds, //异常错误fd_set地址 const struct timeval FAR *timeout //timeval结构 );
这个函数会检查fd_array这个数组里边所有的socket是否有信号到来,如果有就成功返回,否则会阻塞在这里,不过我们在最后一个参数那里,传一个等待时间。
调用完select之后,我们可以在调用FD_ISSET这个宏来判断是fd_array这个数组里边的那个socket有信号了。之后我们就可以进行数据收发了。
以下是select的示例代码:
#include <winsock2.h> #include <stdio.h> #define PORT 6000 #pragma comment (lib, "Ws2_32.lib") fd_set g_fdClientSock; int clientNum = 0; BOOL WinSockInit() { WSADATA data = {0}; if(WSAStartup(MAKEWORD(2, 2), &data)) return FALSE; if ( LOBYTE(data.wVersion) !=2 || HIBYTE(data.wVersion) != 2 ){ WSACleanup(); return FALSE; } return TRUE; } // 工作者线程 DWORD WINAPI WorkThreadProc(LPARAM lparam) { fd_set fdRead; FD_ZERO( &fdRead ); int nRet = 0; char* recvBuffer =(char*)malloc( sizeof(char) * 1024 ); if ( recvBuffer == NULL ) return -1; memset( recvBuffer, 0, sizeof(char) * 1024 ); while ( true ) { fdRead = g_fdClientSock; timeval tv; tv.tv_sec = 0; tv.tv_usec = 10; //检查fd_arrray数组里边是否有信号到来 nRet = select( 0, &fdRead, NULL, NULL, &tv ); if ( nRet != SOCKET_ERROR ) { for ( int i = 0; i < g_fdClientSock.fd_count; i++ ) { // 遍历出来哪些SOCKET有信号 if ( FD_ISSET(g_fdClientSock.fd_array[i],&fdRead) ) { // 下面是数据的收发 memset( recvBuffer, 0, sizeof(char) * 1024 ); nRet = recv( g_fdClientSock.fd_array[i], recvBuffer, 1024, 0); if ( nRet == SOCKET_ERROR ) { closesocket( g_fdClientSock.fd_array[i]); clientNum--; FD_CLR( g_fdClientSock.fd_array[i], &g_fdClientSock ); } else if ( nRet == 0 ) { closesocket( g_fdClientSock.fd_array[i]); clientNum--; FD_CLR( g_fdClientSock.fd_array[i], &g_fdClientSock ); } else { printf("Recv msg:%s\n",recvBuffer); send(g_fdClientSock.fd_array[i], recvBuffer, strlen(recvBuffer), 0); } } } } } if ( recvBuffer != NULL ) free( recvBuffer ); return 0; } int main() { //初始化环境 WinSockInit(); SOCKET listenSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); struct sockaddr_in server; server.sin_family = AF_INET; server.sin_addr.s_addr = htonl(INADDR_ANY); server.sin_port = htons(PORT); //绑定 int ret = bind(listenSock, (sockaddr*)&server, sizeof(server)); //监听 ret = listen(listenSock, 4); sockaddr_in clientAddr; int nameLen = sizeof( clientAddr ); // 先把工作者线程创建起来 CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)WorkThreadProc, NULL, NULL, NULL); while( clientNum < FD_SETSIZE )//FD_SETSIZE==64 { // 当有一个客户端进行连接时,主线程的accept会进行返回 SOCKET clientSock = accept( listenSock, (sockaddr*)&clientAddr, &nameLen ); FD_SET(clientSock, &g_fdClientSock); clientNum++; } closesocket(listenSock); WSACleanup(); return 0; }
相关文章推荐
- Lua编程示例(一):select、debug、可变参数、table操作、error
- jquery 获取select数组与name数组长度的实现代码
- SQL学习笔记三 select语句的各种形式小结
- 一条select语句引起的瓶颈问题思考
- SQL Select语句完整的执行顺序
- mysql SELECT语句去除某个字段的重复信息
- 点击按钮后 文本框变为Select下拉列表框
- javascript 模拟select下拉列表特效
- javascript select options 排序(保持option 对象完整性)
- 用javascript和css模拟select的脚本
- js select常用操作控制代码
- mysql中insert与select的嵌套使用方法
- jquery的clone方法应用于textarea和select的bug修复
- SQLServer中SELECT语句的执行顺序
- MySQL进阶SELECT语法篇
- asp中 select top 问题!~
- ASP中获得Select Count语句返回值的方法
- 探讨select in 在postgresql的效率问题
- 数据库插入数据之select into from与insert into select区别详解
- Javascript select控件操作大全(新增、修改、删除、选中、清空、判断存在等)