select函数总结
来源:互联网 发布:淘宝古装知乎 编辑:程序博客网 时间:2024/06/04 18:30
select原型:
参数说明:
3> write写fd集合
4> error错误fd集合
5> 超时时间
timeval原型:
struct timeval {
long tv_sec;
long tv_usec;
};
实测:
客户端关闭时:
超时!
错误!
错误!
错误!
。。。
客户端连接时:
两种情况:
A(客户端在超时时间内连接)
连接数:1
连接数:1
连接数:1
。。。
B(客户端在超时时间外连接)
超时!
错误!
错误!
错误!
。。。
这里返回错误代码:10022
10022:返回了一个无效的参数
后查实,发现是FD_SET(listfd,&readfd);没有放在while里面。
加了一段测试代码:
1
0
超时
0
0
错误
。。。
造成结果的原因:
select(0,NULL,NULL,NULL,wait_timeval);
这种方式在Linux下就相当于sleep,而Windows下却之间返回了-1,认为你传递的参数错误。查询了一下MSDN发现有如下说明。
以上说明了为什么会返回-1,至于为什么cout打印的值从1变成了0,请看下面:
fd_set的原型:
----------------------------------------------------------------
linux下的处理方法:
windows下的处理方法:
以linux为例:
理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。
(1)执行fd_set set;FD_ZERO(&set);则set用位表示是0000,0000。
(2)若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)
(3)若再加入fd=2,fd=1,则set变为0001,0011
(4)执行select(6,&set,0,0,0)阻塞等待
(5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。
基于上面的讨论,可以轻松得出select模型的特点:
(1)可监控的文件描述符个数取决与sizeof(fd_set)的值。我这边服务器上sizeof(fd_set)=512,每bit表示一个文件描述符,则我服务器上支持的最大文件描述符是512*8=4096。据说可调,另有说虽然可调,但调整上限受于编译内核时的变量值。
(2)将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd,一是用于再select返回后,array作为源数据和fd_set进行FD_ISSET判断。二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始 select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个参数。
(3)可见select模型必须在select前循环array(加fd,取maxfd),select返回后循环array(FD_ISSET判断是否有时间发生)。
参考文章:http://blog.csdn.net/lingfengtengfei/article/details/12392449
[windows]WINSOCK_API_LINKAGE int PASCAL select(int nfds,fd_set*,fd_set*,fd_set*,const struct timeval*);[linux]int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
参数说明:
1> fd集合里面最大的+1(因为是从0开始)
windows下填0就好
3> write写fd集合
4> error错误fd集合
5> 超时时间
timeval原型:
struct timeval {
long tv_sec;
long tv_usec;
};
实测:
while(true) { //cout << "1" << endl; nRet=select(0,&readfd,NULL,NULL,&vl); if(nRet==0) puts("超时!"); if(nRet==-1) puts("错误!"); if(nRet > 0) { cout << "连接数:" << nRet << endl; } //Sleep(1); }
客户端关闭时:
超时!
错误!
错误!
错误!
。。。
客户端连接时:
两种情况:
A(客户端在超时时间内连接)
连接数:1
连接数:1
连接数:1
。。。
B(客户端在超时时间外连接)
超时!
错误!
错误!
错误!
。。。
这里返回错误代码:10022
10022:返回了一个无效的参数
后查实,发现是FD_SET(listfd,&readfd);没有放在while里面。
加了一段测试代码:
cout << readfd.fd_count << endl; nRet=select(0,&readfd,NULL,NULL,&vl); cout << readfd.fd_count << endl;结果:
1
0
超时
0
0
错误
。。。
造成结果的原因:
select(0,NULL,NULL,NULL,wait_timeval);
这种方式在Linux下就相当于sleep,而Windows下却之间返回了-1,认为你传递的参数错误。查询了一下MSDN发现有如下说明。
Microsoft MSDN:Any two of the parameters, readfds, writefds, or exceptfds, can be given as null. At least one must be non-null,and any non-null descriptor set must contain at least one handle to a socket.参考文章:http://www.cnblogs.com/fullsail/archive/2012/08/12/2634336.html
以上说明了为什么会返回-1,至于为什么cout打印的值从1变成了0,请看下面:
fd_set的原型:
----------------------------------------------------------------
[windows,down]typedef struct fd_set { u_int fd_count; SOCKET fd_array[FD_SETSIZE];} fd_set;----------------------------------------------------------------
[linux,down]typedef struct { unsigned long fds_bits [__FDSET_LONGS]; //用ulong数组来表示bitmap} __kernel_fd_set;typedef __kernel_fd_set fd_set;----------------------------------------------------------------
linux下的处理方法:
//每个ulong为32位,可以表示32个bit。//fd >> 5 即 fd / 32,找到对应的ulong下标i;fd & 31 即fd % 32,找到在ulong[i]内部的位置 #define __FD_SET(fd, fdsetp) (((fd_set *)(fdsetp))->fds_bits[(fd) >> 5] |= (1<<((fd) & 31))) //设置对应的bit#define __FD_CLR(fd, fdsetp) (((fd_set *)(fdsetp))->fds_bits[(fd) >> 5] &= ~(1<<((fd) & 31))) //清除对应的bit#define __FD_ISSET(fd, fdsetp) ((((fd_set *)(fdsetp))->fds_bits[(fd) >> 5] & (1<<((fd) & 31))) != 0) //判断对应的bit是否为1#define __FD_ZERO(fdsetp) (memset (fdsetp, 0, sizeof (*(fd_set *)(fdsetp)))) //memset bitmap参考文章:linux中fd_set的内部实现
windows下的处理方法:
#define FD_SET(fd, set) do { u_int __i;\for (__i = 0; __i < ((fd_set *)(set))->fd_count ; __i++) {\ if (((fd_set *)(set))->fd_array[__i] == (fd)) {\ break;\ }\}\if (__i == ((fd_set *)(set))->fd_count) {\ if (((fd_set *)(set))->fd_count < FD_SETSIZE) {\ ((fd_set *)(set))->fd_array[__i] = (fd);\ ((fd_set *)(set))->fd_count++;\ }\}\} while(0)#define FD_ZERO(set) (((fd_set *)(set))->fd_count=0)#define FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)(fd), (fd_set *)(set))
以linux为例:
理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。
(1)执行fd_set set;FD_ZERO(&set);则set用位表示是0000,0000。
(2)若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)
(3)若再加入fd=2,fd=1,则set变为0001,0011
(4)执行select(6,&set,0,0,0)阻塞等待
(5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。
基于上面的讨论,可以轻松得出select模型的特点:
(1)可监控的文件描述符个数取决与sizeof(fd_set)的值。我这边服务器上sizeof(fd_set)=512,每bit表示一个文件描述符,则我服务器上支持的最大文件描述符是512*8=4096。据说可调,另有说虽然可调,但调整上限受于编译内核时的变量值。
(2)将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd,一是用于再select返回后,array作为源数据和fd_set进行FD_ISSET判断。二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始 select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个参数。
(3)可见select模型必须在select前循环array(加fd,取maxfd),select返回后循环array(FD_ISSET判断是否有时间发生)。
参考文章:http://blog.csdn.net/lingfengtengfei/article/details/12392449
windows下select代码(服务端):
#include <iostream>#include <cstdlib>#include <winsock2.h>#include<cstdio>#include "fcntl.h"using namespace std;int main(){ WSADATA wd; WSAStartup(0x0202,&wd); fd_set readfd; FD_ZERO(&readfd); timeval vl; vl.tv_sec=3; vl.tv_usec=0; SOCKET listfd=socket(AF_INET,SOCK_STREAM,0); if(listfd==INVALID_SOCKET) { cout << "创建套接字失败,err:" << GetLastError() << endl; } //linux下的阻塞模型调整// int flag=fcntl(listfd,F_GETFL,0);// fcntl(listfd,F_SETFL,flag | O_NONBLOCK); u_long non_blk = 1; //1:非阻塞 0:阻塞 ioctlsocket(listfd, FIONBIO, &non_blk); //FIONBIO:socket阻塞模式 sockaddr_in sa= {AF_INET}; sa.sin_addr.s_addr=inet_addr("0.0.0.0"); sa.sin_port=htons(8444); int nRet=bind(listfd,(sockaddr*)&sa,sizeof(sa)); if(nRet==SOCKET_ERROR) { cout << "绑定端口失败,error:" << GetLastError() <<endl; } listen(listfd,5); fd_set sockfd; FD_ZERO(&sockfd); FD_SET(listfd,&sockfd); char sBuf[256]; while(true) { memcpy(&readfd,&sockfd,sizeof(fd_set)); nRet=select(0,&readfd,NULL,NULL,&vl); if(nRet==0) puts("超时!"); if(nRet==-1) cout << "错误:" << GetLastError() << endl; if(nRet > 0) { cout << "it is coming" << endl; for(int i=0; i < readfd.fd_count; i++) { if(FD_ISSET(readfd.fd_array[i],&readfd) ) { if(readfd.fd_array[i] == listfd) { SOCKET winsock=accept(listfd,NULL,NULL); FD_SET(winsock,&sockfd); FD_SET(winsock,&readfd); } else { memset(sBuf,0x00,sizeof(sBuf)); int n=recv(readfd.fd_array[i],sBuf,sizeof(sBuf),0); if(n < 0 ) { if(GetLastError() == 10054) //10054:远程主机强迫关闭了一个现有的连接。 { cout << "quit!" << endl; closesocket(readfd.fd_array[i]); FD_CLR(readfd.fd_array[i],&sockfd); } else { cout << "recv error" << GetLastError() << endl; //10035:无法立即完成一个非阻止性套接字操作。 } //阻塞模式下,只要n<0,就执行FD_CLR //FD_CLR(readfd.fd_array[i],&sockfd); } else { cout << sBuf << endl; //int nnn=EWOULDBLOCK; } } } } } //Sleep(200); } WSACleanup(); //不要忘了 return 0;}
先看这张图,区分下linux与windows
linux下的实现(客户端):
#include <iostream>#include <cstdlib>#include<cstdio>#include <cstring>#include<sys/socket.h> #include<arpa/inet.h> #include <unistd.h>#include "fcntl.h"#include <error.h>#define INVALID_SOCKET -1#define SOCKET_ERROR -1using namespace std;int main(){ // WSADATA wd; //WSAStartup(0x0202,&wd); int listfd=socket(AF_INET,SOCK_STREAM,0); if(listfd==INVALID_SOCKET) { cout << "创建套接字失败,err:" << endl; } sockaddr_in sa={AF_INET}; sa.sin_addr.s_addr=inet_addr("127.0.0.1"); sa.sin_port=htons(8444); int n = connect(listfd,(sockaddr*)&sa,sizeof(sa)); if(n != 0) { cout << "connect failed!" << endl; return 0; }cout << "sock=" << listfd << endl; char sBuf[256] ={0}; while(1) { memset(sBuf,0x00,sizeof(sBuf)); cout << "input any ..." << endl; cin >> sBuf ; n = send(listfd,sBuf,strlen(sBuf),0); if(n > 0) { cout << "send success!" << endl; } } //EWOULDBLOCK return 0;}
linux下的服务端:
#include <iostream>#include <cstdlib>#include<cstdio>#include <cstring>#include<sys/socket.h> #include<arpa/inet.h> #include <unistd.h>#include "fcntl.h"#include <error.h>#define INVALID_SOCKET -1#define SOCKET_ERROR -1using namespace std;int g_nCount=0;int g_max=0;int main(){ //WSADATA wd; //WSAStartup(0x0202,&wd); fd_set readfd; FD_ZERO(&readfd); timeval vl; int listfd=socket(AF_INET,SOCK_STREAM,0); if(listfd==INVALID_SOCKET) { cout << "创建套接字失败,err:" << endl; } //linux下的阻塞模型调整int flag=fcntl(listfd,F_GETFL,0);fcntl(listfd,F_SETFL,flag | O_NONBLOCK);//windows//unsigned long flag=1;//ioctlsocket(listfd,FIONBIO,&flag); sockaddr_in sa= {AF_INET}; sa.sin_addr.s_addr=inet_addr("0.0.0.0"); sa.sin_port=htons(8444); int nRet=bind(listfd,(sockaddr*)&sa,sizeof(sa)); if(nRet==SOCKET_ERROR) { cout << "绑定端口失败,error:" <<endl; } listen(listfd,5); int sockfd[1000]={0}; sockfd[0]=listfd; char sBuf[256]; g_nCount=1; g_max=listfd+1; while(true) { FD_ZERO(&readfd); //一定不能忘 memset(&vl,0x00,sizeof(vl)); vl.tv_sec=4; vl.tv_usec=0; for(int i=0;i < g_nCount;i++) { FD_SET(sockfd[i],&readfd); cout << sockfd[i] << endl; } nRet=select(g_max,&readfd,NULL,NULL,&vl); if(nRet==0) puts("超时!"); if(nRet==-1) cout << "错误!" << endl; if(nRet > 0) { cout << "it is coming" << endl; //for(i= sock+1; i <max_fd+1; ++i) ,也是一个思路 for(int i=0; i < g_nCount; i++) { if(FD_ISSET(sockfd[i],&readfd) ) { g_max=(sockfd[i] >= g_max ? (sockfd[i]+1) : g_max); cout << "coming,and sockfd[i]=" << sockfd[i] << endl; if(sockfd[i] == listfd) { int unixsock=accept(listfd,NULL,NULL); sockfd[g_nCount++]=unixsock; FD_SET(unixsock,&readfd); } else { memset(sBuf,0x00,sizeof(sBuf)); cout << "test,i=" << i << ",sock="<< sockfd[i] << ",g_nCount=" << g_nCount << endl; int n=recv(sockfd[i],sBuf,sizeof(sBuf),0); if(n < 0 ) { cout << "recv error" << endl; //阻塞模式下,只要n<0,就执行FD_CLR //FD_CLR(readfd.fd_array[i],&sockfd); } else if(n==0) { cout << "quit!" << endl; close(sockfd[i]); int delete_fd=sockfd[i]; //memcpy(sockfd,sockfd+i+1,g_nCount-i); 效率比较低 if(i < g_nCount-1) //如果i=g_nCount,这个数就抹不掉了 { sockfd[i--]=sockfd[--g_nCount]; //很不错的思路 } else { sockfd[i]=0; g_nCount--; } //max的值可能已经发生了变化,这里暂不考虑修改max,可以再while后更新max //影响不大,无非select多遍历了几个数 } else { cout << sBuf << endl; //int nnn=EWOULDBLOCK; } } } } } //Sleep(200); //sleep(1); } return 0;}//linux下select后,描述符集合,timeval,都会清零//windows下,只有描述集清零.//linux下,第一个参数不能为0.是最大值+1,+n(n > 1)就行//windows下,本参数忽略,仅起到兼容作用,设为0即可
阅读全文
0 0
- select函数总结
- select函数总结
- select函数总结
- select函数总结
- select函数总结
- select函数总结
- select函数使用总结
- select函数总结
- select函数总结
- select函数使用总结
- select函数总结
- select函数总结
- select函数总结
- select函数总结
- select函数总结
- select函数总结
- select函数总结
- select函数总结
- 参考ethtool写了个Linux设置、获取网卡模式的接口
- IPv4协议笔记
- [Unity基础]Unity本地存储--Sqlite数据库
- USB小票打印解决办法
- CCPC.2017 哈尔滨站-重现赛-H(暴力+贪心)
- select函数总结
- python爬虫浏览器伪装
- 关于cin和getchar()读取数据的方式
- MVP注册登录模块,结合数据库查询和添加数据
- Linux教程:如何查找并移除Ubuntu上陈旧的PPA仓库
- PHP使用Curl请求Https地址时需要注意
- 学习 Shell —— 括号、引号
- Linux翻页命令more和less
- Vue小技巧