WSAEventSelect IO复用模型

1 今天帮一学习WSAEventSelect的网友排查一个测试用服务器端recv返回0的问题,出现这个问题直观判断一般是客户端socket关闭了,可是他的代码很简单并且是本机测试,通过wireshark抓包也没有发现客户端发送了FIN分节,错误码为0,一切看起来都是正常的。正当无思路时,突然想到会不会是将recv的缓冲区长度参数给设置为0了呢,后发现果然如此,代码如下:

    else if(event.lNetworkEvents & FD_READ)   // 处理FD_READ通知消息
    {
     if(event.iErrorCode[FD_READ_BIT] == 0)
     {
      char szText[256];
      int nRecv = ::recv(sockArray[i], szText, strlen(szText), 0);
      if(nRecv > 0)
      {
       szText[nRecv] = ‘\0‘;
       printf("接收到数据:%s \n", szText);
      }
     }  

szText是栈的一个没有初始化的字符数组,strlen(szText)返回值就有很大随机性了,这里要改为sizeof(szText)-1。

2 WSAEventSelect模型最多复用64个socket,这个说法是片面的,这是由于将每一个socket描述符对应一个事件句柄,从而达到了WSAWaitForMultipleEvents这个函数所要求的等待事件的上限造成的,实际中也可以通过WSAEventSelect调用将多个socket关联到一个事件句柄上,WSAWaitForMultipleEvents只等待一件事件即可,这样可以复用多于64个socket。

3 在用WSAEnumNetworkEvents返回的event的lNetworkEvents与FD_xxxx相与判断相应事件时不应该if...else if...else if,要if ...  if ... if ...,因为有时候一个socket会返回多个事件。

4 WSAEventSelect模型时如何发送数据,首先创建socket(服务器端通过accept) fd,然后调用ioctlsocket这个将fd设为非阻塞模式,接下来调用WSAEventSelect将该fd同一个事件event相关联,关联事件属性之一为FD_WRITE,这样在接下来通过WSAWaitForMultipleEvents等待前述event时或直接调用WSAEnumNetworkEvents,一定会有第一个FD_WRITE事件可用,此时若有数据可写可在此事件中写,若无可跳过,接下来在收到FD_CLOSE事件之前都是可以直接send数据并判断返回值,若返回值为-1且错误码为WSAEWOULDBLOCK,说明低层socket发送缓存区满了,此时应用程序要记忆已发送位置,并在下一个FD_WRITE事件到来时继续发送余下的或更新的数据。

时间: 2025-01-06 15:21:09

WSAEventSelect IO复用模型的相关文章

Nginx 高效核心 - IO 复用模型

1. 图示 Nginx 服务处理请求简略过程 2. I/O 模型概述 2.1 同步/异步       同步:Synchronous,调用者等待被调用者返回消息,才能继续执行. 异步:Asynchronous,被调用者通过状态.通知或回调机制主动通知调用者自己的运行状态. 2.2 阻塞/非阻塞       阻塞:Blocking,指 IO 操作需要彻底完成之后才返回到用户空间,调用结果返回之前,调用者被挂起. 非阻塞:Noblocking,指 IO 操作被调用后立即返回给用户一个状态值,无需等到

线程和IO复用模型简述

一.进程&&线程 1.进程模型 (1) 单进程模型:一个进程响应多个请求 容易造成请求的阻塞 (2) 多线程模型:每个进程响应一个请求 进程量大,进切换次数过多 每个进程地址空间独立,很多空间是重复的数据,所以内存使用率低 2.线程模型(thread) LWP:light Weight Process 在Linux上称为轻量级进程:是进程内部的自运行单位,在Linux上表现形式和进程一样,占用一个端口,但管理方式不同 多核CPU使用下,线程的优势才能发挥,并行执行线程,更好的分配系统资源,

多路IO复用模型--select, poll, epoll

select 1.select能监听的文件描述符个数受限于FD_SETSIZE,一般为1024,单纯改变进程打开的文件描述符个数并不能改变select监听文件个数 2.解决1024以下客户端时使用select是很合适的,但如果链接客户端过多,select采用的是轮询模型,会大大降低服务器响应效率,不应在select上投入更多精力 int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct tim

7.3 5种IO模型与IO复用

5种IO模型分别如下: 1.阻塞IO模型 当上层应用app1调用recv系统调用时,如果对等方没有发送数据(缓冲区没有数据),上层app1将阻塞(默认行为,被linux内核阻塞). 当对等方发送了数据,linux内核recv端缓冲区有数据后,内核会把数据copy给用户空间.然后上层应用app1解除阻塞,执行下一步操作. 2.非阻塞IO模型 上层应用程序app2将套接字设置成非阻塞模式. 上层应用程序app2轮询调用recv函数,接收数据,若缓冲区没有数据,上层程序app2不会阻塞,recv返回值

IO复用之——poll

一. 关于poll 对于IO复用模型,其优点无疑是免去了对一个个IO事件就绪的等待,转而代之的是同时对多个IO数据的检测,当检测等待的事件中至少有一个就绪的时候,就会返回告诉用户进程"已经有数据准备好了,快看看是哪个赶紧处理",而对于IO复用的实现,除了可以用select函数,另外一个函数仍然支持这种复用IO模型,就是poll函数: 二. poll函数的用法 虽然同样是对多个IO事件进行检测等待,但poll和select多少还是有些不同的: 函数参数中, 先来说nfds,这个是和sel

《深入理解计算机系统》Tiny服务器4——epoll类型IO复用版Tiny

前几篇博客分别讲了基于多进程.select类型的IO复用.poll类型的IO复用以及多线程版本的Tiny服务器模型,并给出了主要的代码.至于剩下的epoll类型的IO复用版,本来打算草草带过,毕竟和其他两种IO复用模型差不太多.但今天在看Michael Kerrisk的<Linux/UNIX系统编程手册>时,看到了一章专门用来讲解epoll函数,及其IO复用模型.于是,自己也就动手把Tiny改版了一下.感兴趣的同学可以参考上述手册的下册1113页,有对于epoll比较详细的讲解. 前边针对IO

IO复用之——select

一. select 前面提到Linux下的五种IO模型中有一个是IO复用模型,这种IO模型是可以调用一个特殊的函数同时监听多个IO事件,当多个IO事件中有至少一个就绪的时候,被调用的函数就会返回通知用户进程来处理已经ready事件的数据,这样通过同时等待IO事件来代替单一等待一个IO窗口数据的方式,可以大大提高系统的等待数据的效率:而接下来,就要讨论在Linux系统中提供的一个用来进行IO多路等待的函数--select: 二. select函数的用法 首先在使用select之前,要分清在IO事件

IO复用之——epoll

一. 关于epoll 对于IO复用模型,前面谈论过了关于select和poll函数的使用,select提供给用户一个关于存储事件的数据结构fd_set来统一监测等待事件的就绪,分为读.写和异常事件集:而poll则是用一个个的pollfd类型的结构体管理事件的文件描述符和事件所关心的events,并通过结构体里面的输出型参数revents来通知用户事件的就绪状态: 但是对于上述两种函数,都是需要用户遍历所有的事件集合来确定到底是哪一个或者是哪些事件已经就绪可以进行数据的处理了,因此当要处理等待的事

UNPv1第六章:IO复用select&amp;poll

有些进程需要一种预先告知内核的能力,使得内核一旦发现进程指定的一个或多个I/O条件就绪(也就是说输入已准备好被读取,或者描述符已能承受更多的输出),他就通知进程,这个能力称为I/O复用 1.IO模型 5种基本I/O模型 阻塞式I/O 非阻塞式I/O I/O复用(select和poll) 信号驱动式I/O(SIGIO) 异步I/O 一个输入操作通常包括两个不同的阶段 (1)等待数据准备 (2)从内核向进程复制数据 对于一个套接口上的输入操作,第一步一般是等待数据到达网络,当分组到达时,它被拷贝到内