Windows Sockets网络编程(4)套接字重叠IO模型

本文介绍了Windows Sockets的套接字重叠IO模型,包括事件通知和完成例程两种实现方式。通过WSARecv、WSAGetOverlappedResult等函数,详细阐述了如何在非阻塞模式下处理网络数据,以及如何通过事件或完成例程获取数据。同时,文中提供了实践案例,帮助读者理解这两种模型的应用。
摘要由CSDN通过智能技术生成

摘要:上一篇文章《Windows Sockets网络编程(3)WSAEventSelect模型开发》事件通知的Select模型,较之该文《Windows Sockets网络编程(1)TCP select & thread》中单纯的select模型有了很大的改进,其中一个最大的优点就是解决了Select不能被用户主动触发的问题。但是,还是存在不少缺陷。试想这样的情景:一般网络通信的这样的,①首先网卡收到数据,②然后Socket阻塞事件被触发,③接着开始读取网卡数据,④读取完毕开始使用相关数据。其中,将数据从网卡读取到内存中的操作,又称作IO操作。这种操作一般是耗时的。在学习《操作系统》课程中,学习过一种叫做“DMA的直接内存访问机制”,这种机制主要是将IO数据直接送往内存中某处,而基本不需要CPU干预,当IO操作完毕时再通知CPU。这是一个操作系统领域的突破性进展,极大的解放了CPU的工作强度。本文即将介绍的套接字重叠IO模型,是一种几乎不需要CPU参与,就能将数据从网卡输送到内存相应位置的技术,它主要有两种实现方式:事件通知完成例程

 

目录:

-----------------------------------------------

- 事件通知

    - WSARecv与LPWSAOVERLAPPED

    - 立即数据与异步问题

    - WSAGetOverlappedResult函数

    - 绑定SOCKET与EVENT

- 完成例程

    - 何为APC函数?

    - 完成例程的原型

    - 线程可警告状态SleepEx

- 实践1:事件通知模型

- 实践2:完成例程模型

- 实践3:TCPClient

 

1.事件通知

〇 WSARecv与LPWSAOVERLAPPED

事件通知技术的故事要从WSARecv函数说起,这里只关注事件通知是如何完成的,而不去关注其他细节。观察一下函数原型,

WSARecv(

_In_ SOCKETs,

_In_reads_(dwBufferCount)__out_data_source(NETWORK)LPWSABUFlpBuffers,

_In_ DWORDdwBufferCount,

_Out_opt_ LPDWORDlpNumberOfBytesRecvd,

_Inout_ LPDWORDlpFlags,

_Inout_opt_ LPWSAOVERLAPPED lpOverlapped,

_In_opt_ LPWSAOVERLAPPED_COMPLETION_ROUTINElpCompletionRoutine

);

从上述原型中可以看到倒数第二个参数LPWSAOVERLAPPED,这并不是一个系统变量类型,而是一个结构体,原型如下,

typedef struct _OVERLAPPED {
    ULONG_PTR Internal;
    ULONG_PTR InternalHigh;
    union {
        struct {
            DWORD Offset;
            DWORD OffsetHigh;
        } DUMMYSTRUCTNAME;
    PVOID Pointer;
    } DUMMYUNIONNAME;
    HANDLE hEvent;
} OVERLAPPED, *LPOVERLAPPED;

同样,这里只关注与事件相关的最重要的参数HANDLE hEvent。文章讲到这里,基本上已经梳理清楚了事件通知模型的条理了——就是WSACreateEvent创建一个事件,将其绑定在结构体中的hEvent当中,然后将结构体整体作为WSARecv的一个参数。这样,当“网卡数据传输到内存指定位置时”,hEvent事件就会被激发(又叫做已触发状态),这时候直接去读取内存数据就可以了。

 

〇 立即数据与异步问题

那么,问题来了。WSARecv是非阻塞函数,数据要如何读取呢?

那么要继续分析一下这个函数,①非阻塞函数也是可以返回读取到的网卡数据的。②如果一时间获取不到网卡数据该怎么办?

针对问题1:WSARecv函数直接获取网卡数据。

这个毫无疑问,需要获取的数据量极其小,能在WSARecv被调用的一瞬间完成。那么,此时WSARecv函数返回值将为0,同时参数lpNumberOfBytesRecvd将被置为获取到的字节数。

针对问题2:一时间获取不到网卡数据。

WSARecv函数为非阻塞函数,它不会傻傻的等待网卡数据运输到内存。这时候,WSARecv函数会返回SOCKET_ERROR,一旦检测到该返回值,应该马上调用WSAGetLastError()获取此时的错误码——ERROR_IO_PENDING(一般是这个错误码,它表示recv操作正在异步执行中)。至于何时获取完毕?这就是上文说道的Event机制了。

 

BOOL EventNotification::recvAsynData()
{
	DWORD recv_length = 0L, flag = 0L;
	ZeroMemory(&m_io, sizeof(m_io));
	m_op_type = RECV_FLAG;
	m_io.hEvent = m_
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值