在文章网络编程技术学习笔记之基础模型中,当服务端收到新的客户端的连接的时候都通过开起一个新线程的方式来完成与客户端的连接。但是,开起一个新的线程所耗费的资源是比较大的。因此,需要考虑其他的方法来让服务端可以只用一个线程就完成与所有客户端的通信。
实现I/O复用的目的就是想要改变如图用多进程或者多线程来处理客户端连接的服务端。
所以引入了I/O复用模型,它将改变服务端与客户端的连接方式变为如下的单线程的连接方式。
而实现I/O复用的核心是使用select函数,该函数可以监视一组套接字的状态变化,定义如下:
返回值:发生错误时返回SOCKET_ERROR,超时返回0。因关注的事件返回时,返回值大于0,该值是发生事件的套接字
由此可知,select函数的作用是可以用来监视多个套接字(文件描述符),监视的事件如下:
是否存在套接字接收数据
无需阻塞传输数据的套接字有哪些
哪些套接字发生了异常
另外,select函数所监视的变量是fd_set结构体变量,该结构体的定义如下:
由此可知,要监视的套接字需要加入到该结构体中才可以调用select函数进行监视。对于fd_set结构体的操作,Winsock库中提供了一组宏来完成,
根据这组宏中的FD_ZERO和FD_SET就可以完成对结构体fd_set变量的初始化,而这个初始化的目的就是将服务器套接字加入到fd_set变量中,这样才可以通过调用select函数来监视套接字的变化。
另外为了防止长时间进入阻塞状态,也需要对select函数的timeout进行初始化,也就是指定超时时间,这个参数是一个timeval结构体变量,该结构体的定义如下:
所以对于select函数的调用就如下图所示:
这里还需要知道的几点是:
连接服务端和与服务端断开连接的变化都在select函数的第二个参数,也就是readfds中
监视到readfds中变化的套接字是服务器套接字的时候,说明客户端发起了对服务端的连接,此时应该要把客户端的套接字加入监视范围
当监视到readfds中变化的是客户端套接字,且接收数据的长度为0的时候,说明发生了与客户端的断开连接,需要将相应的客户端套接字从监视范围内删除,否则就是接收到了数据
完整的代码如下:
服务端:
客户端:
运行结果如下,可以看到此时只用了一个线程就完成了与多个客户端的连接和通信。
重叠I/O的模型如下所示:
由图可知,重叠I/O是同一线程内部向多个目标传输(或从多个目标接收)数据引起I/O重叠现象。为了完成这项任务,调用的I/O函数应立即返回,只有这样才能发送后续的数据,为了完成这项功能,调用的I/O函数应以非阻塞模型工作。
首先要使用WSASocket函数来创建适用于重叠I/O套接字
在重叠I/O中,使用WSASend函数来发送数据,该函数定义如下:
其中WSABUF结构体定义如下:
该参数保存的就是需要传输的数据信息,而WSAOVERLAPPED结构体的定义如下:
这里最关键的是hEvent句柄,该句柄用来保证数据的完整发送。
为了进行重叠I/O,WSASend函数的lpOverlapped参数中应该传递有效的结构体变量地址值,而不是NULL。如果传递的是NULL,那么WSASend函数的第一个参数中的句柄所指的套接字将以阻塞模式工作
WSASend函数调用以后,如果返回值为SOCKET_ERROR并且WSAGetLastError的值为WSA_IO_PENDING就说明此时数据并没有发送完。而要等待数据发送完几句要用到OVERLAPPED结构体的hEvent,通过调用WSAWaitFirMultipleEvent函数并将其事件传入就可以等待发送数据完成,该函数定义如下
当该函数成功返回,此时代表数据发送完毕,可以使用WSAGetOverlappedResult函数来获取状态,该函数定义如下:
在重叠I/O模型中,接收数据使用WSARecv函数,该函数定义如下:
参数含义和WSASend是一样的所以不重复,到这里就可以实现基于事件的重叠I/O模型,具体代码如下:
服务端:
客户端:
可以看到成功完成通信
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2021-11-30 09:50
被1900编辑
,原因: