socket何时处于”读就绪状态“?---通过“应用程序大爷"和"内核孙子"对话再看重要的select函数的使用方法
2017-04-19 14:20
375 查看
前面。 我已经陆续介绍过select函数的一些零碎知识, 在本文中,我们来讨论这样一个问题:socket何时处于读就绪状态?
事实上主要讨论select函数, 毕竟socket的读就绪状态会导致select函数马上返回。select函数的重要性不言而喻, 我在这里就不再强调了。 须要注意的是: Windows环境下的select和Linux环境下的select大同小异, 为了便于叙述和实战, 我们以Windows下的select函数为例。
我们再次来复习一下select函数的原型吧:int PASCAL FAR select( int nfds, fd_set FAR* readfds, fd_set FAR* writefds, fd_set FAR* exceptfds, const struct timeval FAR* timeout);
nfds:是一个整数值。是指集合中全部文件描写叙述符的范围,在Linux中必须是全部文件描写叙述符的最大值加1,不能错!
在Windows中这个參数的值无所谓。会被忽略。
readfds:(可选)指针。指向一组等待可读性检查的套接口。
writefds:(可选)指针,指向一组等待可写性检查的套接口。
exceptfds:(可选)指针。指向一组等待错误检查的套接口。
timeout:select()最多等待时间,对堵塞操作则为NULL。
那么。 select函数有什么作用呢?何时返回呢? 以下是应用程序(程序猿写的, 比方你。 我)和操作系统内核(微软的一些人写的)之间的精彩对话
![](https://oscdn.geek-share.com/Uploads/Images/Content/201707/33c29e3268d66ef5c09858efe40a3635.gif)
内核
![](https://oscdn.geek-share.com/Uploads/Images/Content/201707/6bb3f2a2eb81f326445a31770403c7f1.gif)
: 应用程序大爷, 您好, 我提供了一个名叫select的函数, 供你用, 你爱咋调用就咋调用, 详细怎么用, 请见select函数原型说明。 大爷, 您仅仅须要负责下发命令就可以, 我负责运行。
应用程序
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/899229cfab2c02d614490485cabb781b.gif)
: 喂。 内核孙子。 我要调用你提供的select函数了。 听说你能够帮我去监測一些我感兴趣的描写叙述符的状态? 一旦处于所谓的就绪状态(比方读就绪状态), 你能够报告给我, 对么?
内核
![](https://oscdn.geek-share.com/Uploads/Images/Content/201707/6bb3f2a2eb81f326445a31770403c7f1.gif)
: 是的。 大爷。
应用程序
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/899229cfab2c02d614490485cabb781b.gif)
: 那好。 我要你去监測一些事件(比方读就绪状态)。 等待某个事件发生, 假设这个事件发生。 那你就好通知我。 否则,有你好受的。
内核
![](https://oscdn.geek-share.com/Uploads/Images/Content/201707/6bb3f2a2eb81f326445a31770403c7f1.gif)
: 好的, 大爷。 那要是大爷您定义的这个事件(比方读就绪状态)一直不发生呢? 我建议大爷您再定义一个超时时间, 假设在这个时间内没有发生, 我给大爷您报一个超时的信息。 你看看我提供的select函数。 最后一个时间參数就是特别为您准备的。
应用程序
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/899229cfab2c02d614490485cabb781b.gif)
: 你这个孙子非常乖啊, 知道大爷我没有耐心, 主动给我报超时, 好吧, 那我就给你下发一个超时时间。 假设在超时时间内我定义的事件(比方读就绪状态)没有发生, 你务必通知到我, 否则, 没有否则!
内核
![](https://oscdn.geek-share.com/Uploads/Images/Content/201707/6bb3f2a2eb81f326445a31770403c7f1.gif)
: 一定一定。哦, 对了, 大爷, 既然我们已经沟通好了, 那我们就尝试着实战交流沟通一下吧。
应用程序
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/899229cfab2c02d614490485cabb781b.gif)
: 好的。 好孙子。
好吧, 精彩对话已经对完了, 我们来看看socket何时处于读就绪状态?(当socket处于读就绪状态时, select函数会马上返回)
server1.cpp为:
实战開始啦:
1. 开启服务端, 再开启client。 然后啥也不干。 过约20秒后, client的select才返回0. 看来。 内核没有撒谎, 没有事件发生的时候, 超过20秒后, 确实如实把情况反馈给了应用程序。 真的非常乖。 好, 我们关闭服务端和client。
2. 开启服务端, 再开启client。 我们在服务端让flag为1, 发送数据。 制造客服端的可读就绪状态。 我们看到, client的select函数马上返回了1. 看来, 内核确实如实反馈了socket的可读就绪状态。 好。 我们关闭服务端和client。 (备注: 在这样的情况下。 一旦select检測到读就绪状态, 此时调用recv函数能马上返回)
3. 开启服务端, 再开启client。 我们在服务端让flag为2, 关闭通信的socket, 我们看到。 client的select函数马上返回了1. 好。 我们关闭服务端和client。
4. 开启服务端, 再开启client。
我们在服务端让flag为3, 关闭非通信的socket, 我们看到, client没有啥变化, 20秒后。 select函数才返回0, 实际就是1中描写叙述的情况。 好,我们关闭服务端和client。
5. 开启服务端。 再开启client。 我们在服务端让flag为4。 调用WSACleanup, 我们看到, client的select函数马上返回了1. 好, 我们关闭服务端和client。
6. 开启服务端, 再开启client。 然后又关掉服务端(关掉这个服务进程), 我们看到, client的select函数马上返回了1. 好。 我们把client也关了吧。
别急, 另一种情况的读就绪状态, 请直接參考我之前的博文:http://blog.csdn.net/stpeace/article/details/21129637。 我来把这个总结为第7点。
7. 假设某socket处于监听状态(当然啦。 这肯定是服务端的某socket),且设置了readfs, 那么一旦client发起了connect连接, 服务端的select函数会马上返回。 由此可知, 一旦服务端的select函数返回了非负值(比方1), 则表明肯定有client来connect连接, 此时服务端调用accept函数会马上成功。 详细实战请參考之前的博文。
总结一下: 1和4是超时返回。 2, 3, 5, 6, 7都相当于client套接字处于读就绪状态, 内核检測到这个读就绪状态后。 select函数马上返回, 将信息报告给应用程序。
当中仅有7是特别针对服务端的socket.
眼下来讲, 我主要接触到的就是读就绪状态或者超时后select函数的返回。 至于写就绪状态下select的返回以及异常就绪状态下select函数的返回。 事实上也是非常easy的, 以后假设有须要, 再学学, 那也不是什么难事, 毕竟网上资料一大堆啊。 OK, 本文到此为止。
事实上主要讨论select函数, 毕竟socket的读就绪状态会导致select函数马上返回。select函数的重要性不言而喻, 我在这里就不再强调了。 须要注意的是: Windows环境下的select和Linux环境下的select大同小异, 为了便于叙述和实战, 我们以Windows下的select函数为例。
我们再次来复习一下select函数的原型吧:int PASCAL FAR select( int nfds, fd_set FAR* readfds, fd_set FAR* writefds, fd_set FAR* exceptfds, const struct timeval FAR* timeout);
nfds:是一个整数值。是指集合中全部文件描写叙述符的范围,在Linux中必须是全部文件描写叙述符的最大值加1,不能错!
在Windows中这个參数的值无所谓。会被忽略。
readfds:(可选)指针。指向一组等待可读性检查的套接口。
writefds:(可选)指针,指向一组等待可写性检查的套接口。
exceptfds:(可选)指针。指向一组等待错误检查的套接口。
timeout:select()最多等待时间,对堵塞操作则为NULL。
那么。 select函数有什么作用呢?何时返回呢? 以下是应用程序(程序猿写的, 比方你。 我)和操作系统内核(微软的一些人写的)之间的精彩对话
![](https://oscdn.geek-share.com/Uploads/Images/Content/201707/33c29e3268d66ef5c09858efe40a3635.gif)
内核
![](https://oscdn.geek-share.com/Uploads/Images/Content/201707/6bb3f2a2eb81f326445a31770403c7f1.gif)
: 应用程序大爷, 您好, 我提供了一个名叫select的函数, 供你用, 你爱咋调用就咋调用, 详细怎么用, 请见select函数原型说明。 大爷, 您仅仅须要负责下发命令就可以, 我负责运行。
应用程序
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/899229cfab2c02d614490485cabb781b.gif)
: 喂。 内核孙子。 我要调用你提供的select函数了。 听说你能够帮我去监測一些我感兴趣的描写叙述符的状态? 一旦处于所谓的就绪状态(比方读就绪状态), 你能够报告给我, 对么?
内核
![](https://oscdn.geek-share.com/Uploads/Images/Content/201707/6bb3f2a2eb81f326445a31770403c7f1.gif)
: 是的。 大爷。
应用程序
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/899229cfab2c02d614490485cabb781b.gif)
: 那好。 我要你去监測一些事件(比方读就绪状态)。 等待某个事件发生, 假设这个事件发生。 那你就好通知我。 否则,有你好受的。
内核
![](https://oscdn.geek-share.com/Uploads/Images/Content/201707/6bb3f2a2eb81f326445a31770403c7f1.gif)
: 好的, 大爷。 那要是大爷您定义的这个事件(比方读就绪状态)一直不发生呢? 我建议大爷您再定义一个超时时间, 假设在这个时间内没有发生, 我给大爷您报一个超时的信息。 你看看我提供的select函数。 最后一个时间參数就是特别为您准备的。
应用程序
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/899229cfab2c02d614490485cabb781b.gif)
: 你这个孙子非常乖啊, 知道大爷我没有耐心, 主动给我报超时, 好吧, 那我就给你下发一个超时时间。 假设在超时时间内我定义的事件(比方读就绪状态)没有发生, 你务必通知到我, 否则, 没有否则!
内核
![](https://oscdn.geek-share.com/Uploads/Images/Content/201707/6bb3f2a2eb81f326445a31770403c7f1.gif)
: 一定一定。哦, 对了, 大爷, 既然我们已经沟通好了, 那我们就尝试着实战交流沟通一下吧。
应用程序
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/899229cfab2c02d614490485cabb781b.gif)
: 好的。 好孙子。
好吧, 精彩对话已经对完了, 我们来看看socket何时处于读就绪状态?(当socket处于读就绪状态时, select函数会马上返回)
server1.cpp为:
#include <stdio.h> #include <winsock2.h> // winsock接口 #pragma comment(lib, "ws2_32.lib") // winsock实现 int main() { WORD wVersionRequested; // 双字节,winsock库的版本号 WSADATA wsaData; // winsock库版本号的相关信息 wVersionRequested = MAKEWORD(1, 1); // 0x0101 即:257 // 载入winsock库并确定winsock版本号,系统会把数据填入wsaData中 WSAStartup( wVersionRequested, &wsaData ); // AF_INET 表示採用TCP/IP协议族 // SOCK_STREAM 表示採用TCP协议 // 0是通常的默认情况 unsigned int sockSrv = socket(AF_INET, SOCK_STREAM, 0); SOCKADDR_IN addrSrv; addrSrv.sin_family = AF_INET; // TCP/IP协议族 addrSrv.sin_addr.S_un.S_addr = INADDR_ANY; addrSrv.sin_port = htons(8888); // socket相应的port // 将socket绑定到某个IP和port(IP标识主机。port标识通信进程) bind(sockSrv,(SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); // 将socket设置为监听模式,5表示等待连接队列的最大长度 listen(sockSrv, 5); SOCKADDR_IN addrClient; int len = sizeof(SOCKADDR); unsigned int sockConn = accept(sockSrv,(SOCKADDR*)&addrClient, &len); int flag = 0; scanf("%d", &flag); if(1 == flag) { char sendBuf[100] = "hello"; send(sockConn, sendBuf, strlen(sendBuf) + 1, 0); // 发送数据到客户端,最后一个參数一般设置为0 } if(2 == flag) { closesocket(sockConn); } if(3 == flag) { closesocket(sockSrv); } if(4 == flag) { WSACleanup(); } while(1); return 0; }client1.cpp为:
#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); int ret = connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); fd_set read_set; struct timeval t; FD_ZERO(&read_set); FD_SET(sockClient, &read_set); t.tv_sec = 20; t.tv_usec = 0; while(1) { ret = select(-1, &read_set, NULL, NULL, &t); printf("ret is %d\n", ret); Sleep(1000); } closesocket(sockClient); WSACleanup(); return 0; }
实战開始啦:
1. 开启服务端, 再开启client。 然后啥也不干。 过约20秒后, client的select才返回0. 看来。 内核没有撒谎, 没有事件发生的时候, 超过20秒后, 确实如实把情况反馈给了应用程序。 真的非常乖。 好, 我们关闭服务端和client。
2. 开启服务端, 再开启client。 我们在服务端让flag为1, 发送数据。 制造客服端的可读就绪状态。 我们看到, client的select函数马上返回了1. 看来, 内核确实如实反馈了socket的可读就绪状态。 好。 我们关闭服务端和client。 (备注: 在这样的情况下。 一旦select检測到读就绪状态, 此时调用recv函数能马上返回)
3. 开启服务端, 再开启client。 我们在服务端让flag为2, 关闭通信的socket, 我们看到。 client的select函数马上返回了1. 好。 我们关闭服务端和client。
4. 开启服务端, 再开启client。
我们在服务端让flag为3, 关闭非通信的socket, 我们看到, client没有啥变化, 20秒后。 select函数才返回0, 实际就是1中描写叙述的情况。 好,我们关闭服务端和client。
5. 开启服务端。 再开启client。 我们在服务端让flag为4。 调用WSACleanup, 我们看到, client的select函数马上返回了1. 好, 我们关闭服务端和client。
6. 开启服务端, 再开启client。 然后又关掉服务端(关掉这个服务进程), 我们看到, client的select函数马上返回了1. 好。 我们把client也关了吧。
别急, 另一种情况的读就绪状态, 请直接參考我之前的博文:http://blog.csdn.net/stpeace/article/details/21129637。 我来把这个总结为第7点。
7. 假设某socket处于监听状态(当然啦。 这肯定是服务端的某socket),且设置了readfs, 那么一旦client发起了connect连接, 服务端的select函数会马上返回。 由此可知, 一旦服务端的select函数返回了非负值(比方1), 则表明肯定有client来connect连接, 此时服务端调用accept函数会马上成功。 详细实战请參考之前的博文。
总结一下: 1和4是超时返回。 2, 3, 5, 6, 7都相当于client套接字处于读就绪状态, 内核检測到这个读就绪状态后。 select函数马上返回, 将信息报告给应用程序。
当中仅有7是特别针对服务端的socket.
眼下来讲, 我主要接触到的就是读就绪状态或者超时后select函数的返回。 至于写就绪状态下select的返回以及异常就绪状态下select函数的返回。 事实上也是非常easy的, 以后假设有须要, 再学学, 那也不是什么难事, 毕竟网上资料一大堆啊。 OK, 本文到此为止。
相关文章推荐
- socket何时处于”读就绪状态“?---通过“应用程序大爷"和"内核孙子"对话再看重要的select函数的用法
- "在应用程序级别之外使用注册为 allowDefinition='MachineToApplication' 的节是错误的。如果在 IIS 中没有将虚拟目录配置为应用程序,则可能导致此错误"的解决方法
- iOS内支付总结__详解iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程!【2012-12-11日更新获取"产品付费数量等于0的问题"】
- C++ "#"的作用和使用方法
- c# 读取其他程序正打开的文件的时“正由另一进程使用,因此该进程无法访问该文件。"的问题解决方法
- "单击以激活及使用该控件"的解决方法
- 企业版应用程序出现此时无法安装"xxx"的解决方法
- vmware安装CentOS " Intel VT-x 处于禁用状态"
- 使用ASIHTTPRequest 编译提示找不到"libxml/HTMLparser.h"的解决方法
- 使用OGG"Loading data from file to Replicat"的方法应该注意的问题:replicat进程是前台进程
- 使用JVM管理工具jvisualvm,系统提示"无法检测到本地java应用程序"的原因和解决办法
- "单击以激活及使用该控件"的解决方法
- 关于使用genymotion"创建 VirtualBoxClient COM 对象失败. 应用程序将被中断“的解决方案
- vs2003 提示"确保已安装项目类型(.vcproj)的应用程序" 的解决方法
- jQuery.validator.addMethod自定义验证方法【在表单验证中的使用 $("#appEdit_Form").validate({rules : {},messages:{}】
- 使用html <a href=""/>标签连接action的方法
- 【iOS开发必收藏】详解iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程!【2012-12-11日更新获取"产品付费数量等于0的问题"】
- split("|")正确使用方法split("\\|")
- 只有在配置文件或 Page 指令中将 enableSessionState 设置为 true 时,才能使用会话状态。还请确保在应用程序配置的 <configuration>\<system.web>\