浅谈WinSock异步I/O模型之select模型

Windows操作系统提供了五种I/O模型,分别是:

1.选择(select)

2.异步选择(WSAAsyncSelect)

3.事件选择(WSAEventSelect)

4.重叠I/O(Overlapped I/O)

5.完成端口(Completion Port)

下面我们来谈一谈最简单的选择模型:

选择(select)模型是Winsock中最常见的 I/O模型。

用途如果我们想接受多个SOCKET的数据,该怎么处理呢?

由于当前socket是阻塞的,直接处理是一定完成不了要求的

a.我们会想到多线程,的确可以解决线程的阻塞问题,但开辟大量的线程并不是什么好的选择; 

b我们可以想到用ioctlsocket()函数把socket设置成非阻塞的,然后用循环逐个socket查看当前套接字是否有数据,轮询进行。

这种是可以解决问题的,但是会导致频繁切换状态到内核去查看是否有数据到达,浪费时间。

c.于是想办法用只切换一次状态就知道所有socket的接受缓冲区是否有数据,于是有了select模型,select是阻塞的,Select的好处是可以同时处理若干个Socket,

Select工作原理

 利用 select 函数来判断某Socket上是否有数据可读,或者能否向一个套接字写入数据,防止程序在Socket处于阻塞模式中时,在一次 I/O 调用(如send或recv、accept等)过程中,被迫进入“锁定”状态;

可以同时等待多个套接字,当某个或者多个套接字满足可读写条件时,通知应用程序调用输入或者输出函数进行读写。

同时防止在套接字处于非阻塞模式中时,产生WSAEWOULDBLOCK错误。它意味着请求的操作在调用期间没有时间完成。



select 的函数原型如下:
int select(
  __in          int nfds,
  __in_out      fd_set* readfds,
  __in_out      fd_set* writefds,
  __in_out      fd_set* exceptfds,
  __in          const struct timeval* timeout
);
其中,第一个参数nfds会被忽略。之所以仍然要提供这个参数,只是为了保持与Berkeley套接字兼容。
后面看到有三个 fd_set类型的参数:
一个用于检查可读性(readfds),
一个用于检查可写性(writefds),

一个用于例外数据(exceptfds)。

fd_set 结构的定义如下:
typedef struct fd_set { 
 u_int fd_count;
 SOCKET fd_array[FD_SETSIZE];
} fd_set;

#define FD_SETSIZE      64
所以 fd_set 结构中最多只能监视64个套接字。

fdset 代表着一系列特定套接字的集合。

对 timeval 结构的定义如下:
tv_sec 字段以秒为单位指定等待时间;
tv_usec 字段则以毫秒为单位指定等待时间。
1秒 = 1000毫秒

若将超时值设置为(0 , 0),表明 select 会立即返回,出于对性能方面的考虑,应避免这样的设置。

select 函数返回值:
select 成功完成后,会在 fdset 结构中,返回刚好有未完成的 I/O操作的所有套接字句柄的总量。

若超过 timeval 设定的时间,便会返回0。若 select 调用失败,都会返回 SOCKET_ERROR,应该调用 WSAGetLastError 获取错误码!

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初始化成空集合。

采用下述步骤,便可完成用 select 操作一个或多个套接字句柄的全过程:
1) 使用FDZERO宏,初始化一个fdset对象;
2) 使用FDSET宏,将套接字句柄加入到fdset集合中;
3) 调用 select 函数,等待其返回……select 完成后,会返回在所有 fdset 集
   合中设置的套接字句柄总数,并对每个集合进行相应的更新。
4) 根据 select的返回值和 FDISSET宏,对 fdset 集合进行检查。
5) 知道了每个集合中“待决”的 I/O操作之后,对 I/O进行处理,然后返回步骤

   1 ),继续进行 select 处理。

select的优势:
1.可以同时对多个建立起来的套接字进行有序的管理。可以防止应用程序在一次I/O调用过程中,使阻塞模式套接字被迫进入阻塞状态;使非阻塞套接字产生WASEWOUBDBLOCK错误。
2.selcet()函数好像就是一个消息中心,当消息到来时,通知应用程序接收和发送数据。

猜你喜欢

转载自blog.csdn.net/aaa_cainiao_66666/article/details/80492031