【Windows编程】-I/O模型-01.Select模型实现
2015-10-10 11:07
375 查看
1.传统阻塞的Socket中存在的问题
每个Socket连接在Recv过程中,存在无数据一直阻塞问题;(当然,这个可以通过ioctlsocket()函数修改Socket属性为非阻塞,但会产生WSAEWOULDBLOCK错误。)
多个Socket管理不方便问题,不能在一个线程中同时监视多个Socket;
(注意,我们可以通过为每个Socket都创建一个线程的办法实现同步监视(即一Socket一线程的方法),但这样过于消耗系统资源。)
基于传统的Socket中存在的问题,我们就要就要设计出相应的解决方案。在Windows下,主要有以下5种I/O模型:
Select模型;
异步选择(WSAAsyncSelect)模型;
事件选择(WSAEventSelect)模型;
重叠I/O(Overlapped I/O);
完成端口(Completion Port);
本文主要介绍最基本的Select模型。
2.Select模型设计思路
Select模型是Winsock中最常见的 I/O模型。核心便是利用 select 函数,实现对 I/O的管理。
每次调用Select之前,需将需要监听的Socket放入一个fd_set结构中,利用 select 函数来判断某Socket上是否有数据可读/可写,其(Select)内部通过轮询的方式,实现了同时等待多个套接字,当某个或者多个套接字满足可读写条件时,Select函数返回。这样,同时防止了在套接字处于非阻塞模式中时,产生WSAEWOULDBLOCK错误。
//fd_set 结构的定义如下: typedef struct fd_set { u_int fd_count; SOCKET fd_array[FD_SETSIZE]; // 其实是将Socket放入数组中 } fd_set;
// Select模型实现 - 核心代码如下: //(因与Linux下的Select相似,此处直接借用Linux下的C代码) // C语言代码实现 while (1) { // 定义fd_set集合 fd_set client; FD_ZERO(&client); /* fill client 将所有需要监视的Socket填充进fd_set集合(内含Socket数组) * 变量说明:client_count为client_set中所包含的Socket数量。 * 变量说明:client_set数组中是客户端已连接的Socket。 */ for (i = 0; i < *(p->client_count);++i) { FD_SET(p->client_set[i] , &client); } /* call select * 如果有Socket发生了事件,就将那些没有发生事件的Socket从fd_set集合中清除 (注意这里是把集合的地址传入的 &client) * * Q:Select内部是如何实现这种监视的?是通过Socket主动发送信号相应信号么?还是轮询? * 这里留了这么一个疑问,如果哪位伙伴知道,请在下方留言 ~~ 感激 ~~ */ ret = select(get_max(p->client_set , *(p->client_count)) + 1, &client , NULL , NULL , &time_val); if ( SOCKET_ERROR == ret) { if (errno == EINVAL) { usleep(10); continue; } printf("select fail![%s]\n" , strerror(errno)); break; } // check 查看返回的集合中,有哪些Socket还在,就说明有关于ta的事件发生。 for (i = 0 ; i < *(p->client_count); ++i) { if (FD_ISSET(p->client_set[i] , &client) > 0) { // recv ret = recv(p->client_set[i] , buf , sizeof(buf) , 0); if (DEF_STD_ERROR == ret || 0 == ret) { // TODO:clear current client continue; } printf("recv data:%s\n" , buf); } } // 处理完毕,下次循环将会重新填充fd_set集合,并再次轮询。 }
select 函数返回值:
select 成功完成后,会在 fdset 结构中,返回刚好有未完成的 I/O操作的所有套接字句柄的总量。
若超过 timeval 设定的时间,便会返回0。若 select 调用失败,都会返回 SOCKET_ERROR,应该调用 WSAGetLastError 获取错误码!
用 select 对套接字进行监视之前,必须将套接字句柄分配给一个fdset的结构集合,之后再来调用 select,便可知道一个套接字上是否正在发生上述的 I/O 活动。
Winsock 提供了下列宏操作,可用来针对 I/O活动,对 fdset 进行处理与检查:
● FD_CLR(s, *set):从set中删除套接字s。
● FD_ISSET(s, *set):检查s是否set集合的一名成员;如答案是肯定的是,则返回TRUE。
● FD_SET(s, *set):将套接字s加入集合set。
● FD_ZERO( * set):将set初始化成空集合。
3.select模式的优势和不足
优势:1.可以同时对多个建立起来的套接字进行有序的管理。
不足:
1.每次调用Select之前都需要将所有Socket放入fd_set结构集合中,这样每次都需要遍历一次数组,但可能每次这个数组都没有变,或只有少部分变化;
2.Select函数返回时,并不知道是哪个Socket发生了事件,所以需要再次遍历Socket数组,找到发生事件的Socket,这个过程中会对没有发生事件的Socket也进行判断,而这个判断是无效的;
(所以,一次Select过程需要轮询遍历两次Socket数组,这样大大降低了CPU的有效效率。)
PS:以上内容如有不足,请多多指正! –netosoul
相关文章推荐
- 深入理解Java内存模型(六)——final
- PHP安全配置
- C++primer第五版笔记-第十五章面向对象程序设计
- C#控制台程序,运行完窗口不退出的方法
- NFS、FTP、SAMBA服务搭建
- C#中List的排序
- 安装django
- 深入理解Java内存模型(五)——锁
- C++primer第五版笔记-第十四章重载运算与类型转换
- 深入理解Java内存模型(四)——volatile
- 深入理解Java内存模型(三)——顺序一致性
- ASP基础入门第四篇(脚本变量、函数、过程和条件语句)
- C/C++中extern关键字详解
- 深入理解Java内存模型(二)——重排序
- 10个使用Java最广泛的现实领域
- Python资料收集
- C++primer第五版笔记-第十三章拷贝控制
- C语言宏定义作用、使用方法小结(2)
- 写让别人能读懂的代码
- python得爬虫关键词