Linux select网络模型
Posted 缄默先生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux select网络模型相关的知识,希望对你有一定的参考价值。
想比较Windows环境下的select,Linux真的是有点不省事,对于select调用之后的readfds,windwos可以直接获取大小并遍历,但是Linux却没有这么人性化,还需要自己添加一个数组,把所有连接服务器的客户端放进去,然后一个一个遍历。
////////////////////////////////////////////////////////////////////分割线//////////////////////////////////////////////////////////////////////////////////////////////////////////
select是用于I/O多路转接的一个系统调用函数。
在C程序中,该系统调用在 sys/select.h 或 unistd.h中声明,语法如下:
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct timeval* timeout);
参数 | 描述 |
---|---|
nfds | sets的文件描述符的最大值 |
readfds | fd_set 类型,包含了需要检查是否可读的描述符,输出时表示哪些描述符可读。可为 NULL。 |
writefds | fd_set 类型,包含了需要检查是否可写的描述符,输出时表示哪些描述符可写。可为 NULL。 |
errorfds | fd_set 类型,包含了需要检查是否出错的描述符,输出时表示哪些描述符出错。可为 NULL。 |
timeout | struct timeval 类型的结构体,表示等待检查完成的最长时间。 |
注:上述来自维基百科
////////////////////////////////////////////////////////////////////分割线//////////////////////////////////////////////////////////////////////////////////////////////////////////
对于select函数,在Linux环境下需要注意的是第一个参数nfds,因为在linux默认的文件描述符最大值为1024,请参看:
同时0,1,2分别被输出输入错误占用,所以在当新的客户端来的时候只能是从3开始往上增加。
同时nfds为所监听的所有文件描述符中,最大的文件描述符+1.
这点我有点疑惑,所以,如果你也有疑惑,请百度一下。
接下来就是我写的代码了,使用的是vector存储文件客户端的socket,但是我的代码可能有缺陷和漏洞,你可以参考并修补一下。
ps:由于qt中不能使用谷歌输入法,只能用ibus,所以我的注释没有中文,同时注释也很少。
//select #include <sys/select.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> #include <vector> #include <iostream> #include <string.h> using namespace std; #define SERVER_IP "127.0.0.1" #define SERVER_PORT 6663 #define CLIENT_NUM 100 int main(){ int sockCli, sockSer; sockaddr_in addrCli, addrSer; vector<int> allsock; //init socket sockSer = socket(AF_INET,SOCK_STREAM,0); if(sockSer == -1){ perror("socket init error:"); return -1; } //init sockaddr_in addrSer.sin_port = htons(SERVER_PORT); addrSer.sin_family = AF_INET; inet_pton(AF_INET,SERVER_IP,&addrSer.sin_addr.s_addr); //bind if( -1 == bind(sockSer,(sockaddr*)&addrSer,sizeof(sockaddr))) { perror("bind error: "); return -1; } if(-1 == listen(sockSer,CLIENT_NUM)){ perror("listen error: "); return -1; } //select fd_set readFds, tempReadfds;; FD_ZERO(&readFds); FD_SET(sockSer,&readFds); timeval time; time.tv_sec = 0; time.tv_usec = 3; while(1){ FD_ZERO(&tempReadfds); memcpy(&tempReadfds,&readFds,sizeof(fd_set)); int rect = select(allsock.size()+4,&tempReadfds,NULL,NULL,&time); if(rect == -1){ perror("select error: "); return -1; } else if(rect == 0){ continue; } else if(rect > 0){ char buf[1024]; bzero(buf,sizeof(buf)); for(long unsigned int i = 0; i < allsock.size();i++){ if(FD_ISSET(allsock[i],&tempReadfds)){ //client seng msg int n = read(allsock[i],buf,1024); if(n > 0){ cout << "client send msg " << buf << endl; for(int i = 0; i < n; i++){ buf[i] = toupper(buf[i]); } write(allsock[i],buf,n); } else { cout << "client exit" << endl; FD_CLR(allsock[i],&readFds); vector<int>::iterator it = allsock.begin()+i ; allsock.erase(it); i--; } } } if(FD_ISSET(sockSer,&tempReadfds)){ //new client connect socklen_t len = sizeof(sockaddr); sockCli = accept(sockSer,(sockaddr*)&addrCli,&len); if(sockCli == -1){ perror("accept error:"); continue; } else{ printf("new client\'s %s has connect, port: %d\\n", inet_ntoa(addrCli.sin_addr),htons(addrCli.sin_port)); FD_SET(sockCli,&readFds); FD_SET(sockCli,&tempReadfds); allsock.push_back(sockCli); memset(&addrCli,\'0\',sizeof(sockaddr)); sockCli = 0; } } } } }
客户端请使用nc命令:
以上是关于Linux select网络模型的主要内容,如果未能解决你的问题,请参考以下文章
IO模型之二-linux网络IO模式select,poll,epoll