网络编程——重叠I/O模型

本文深入探讨了Windows中的重叠I/O模型,它允许在同一线程内向多个目标发送或接收数据。文章详细介绍了如何创建具有重叠I/O属性的套接字,以及使用WSASend和WSARecv函数执行异步I/O操作。通过事件对象和CompletionRoutine函数,读者可以了解如何确认和处理I/O操作的完成。示例代码展示了发送和接收数据的过程,有助于理解重叠I/O的工作机制。

参考

  1. 《TCP/IP网络编程》 尹圣雨

重叠I/O模型

理解重叠I/O模型

同一线程内部向多个目标传输(或从多个目标接收)数据引起的I/O重叠现象称为“重叠I/O”。为了完成这项任务,调用的I/O函数应立即返回,因此前提条件是异步I/O。而且,为了完成异步I/O,调用的I/O函数应以非阻塞模式工作

Windows中重叠I/O的重点并非I/O本身,而是如何确认I/O完成时的状态

创建重叠I/O套接字

#include <winsock2.h>

SOCKET WSASocket(int af, int type, int protocol, LPWSAPROTOCOL_INFO lpProtocolInfo, GROUP g, DWORD dwFlags);

成功时返回套接字句柄,失败时返回INVALID_SOCKET。其中:

  1. af:协议族信息
  2. type:套接字数据传输方式
  3. protocol:2个套接字之间使用的协议信息
  4. lpProtocolInfo:包含创建的套接字信息的WSAPROTOCOL_INFO结构体变量地址值,不需要时传递NULL
  5. g:为扩展函数而预约的参数,可以使用0
  6. dwFlags:套接字属性信息

可以向最后一个参数传递WSA_FLAG_OVERLAPPED,赋予创建出的套接字重叠I/O特性。例如:

WSASocket(PF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);

执行重叠I/O的函数

重叠I/O模型中,服务器端和客户端间的连接过程与一般的套接字连接过程相同,但I/O数据时使用的函数不同

WSASend
#include <winsock2.h>

int WSASend(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);

成功时返回0,失败时返回SOCKET_ERROR。其中:

  1. s:套接字句柄,传递具有重叠I/O属性的套接字句柄时,以重叠I/O模型输出
  2. lpBuffers:WSABUF结构体变量数组的地址值,WSABUF中存有待传输数据
  3. dwBufferCount:第二个参数中数组的长度
  4. lpNumberOfBytesSent:用于保存实际发送字节数的变量地址值
  5. dwFlags:用于更改数据传输特性,如传递MSG_OOB时发送OOB模式的数据
  6. lpOverlapped:WSAOVERLAPPED结构体变量的地址值,使用事件对象,用于确认完成数据传输
  7. lpCompletionRoutine:传入Completion Routine函数的入口地址值,可以通过该函数确认是否完成数据传输
WSABUF结构体

WSASend第二个参数涉及的WSABUF结构体中存有待传输数据的地址和大小等信息:

typedef struct __WSABUF
{
   
   
	u_long len;        // 待传输数据的大小
	char FAR* buf;     // 缓冲地址值
}WSABUF, * LPWSABUF;
WSAOVERLAPPED结构体

WSASend第六个参数涉及的WSAOVERLAPPED结构体定义如下:

typedef struct __WSAOVERLAPPED
{
   
   
	DWORD Internal;
	DWORD InternalHigh;
	DWORD Offset;
	DWORD OffsetHigh;
	WSAEVENT hEvent;
}WSAOVERLAPPED, * LPWSAOVERLAPPED;

其中,Internal、InternalHigh成员是进行重叠I/O时操作系统内部使用的成员,而Offset、OffsetHigh属于具有特殊用途的成员。此处只需关注hEvent成员

WSASend函数的lpOverlapped参数中应传递有效的结构体变量地址值,如果传递NULL,WSASend函数的第一个参数中的句柄所指的套接字将以阻塞模式工作。另外,利用WSASend函数同时向多个目标传递数据时,需要分别构建传入第六个参数的WSAOVERLAPPED结构体变量

WSASend示例
WSAEVENT event;
WSAOVERLAPPED overlapped;
WSABUF dataBuf;
char buf[BUF_SIZE] = {
   
   "待传输的数据"};
int recvBytes = 0;
......
event = WSACreateEvent();
memset(&overlapped, 0, sizeof(overlapped));
overlapped.hEvent = event;
dataBuf.len = sizeof(buf);
dataBuf.buf = buf;
WSASend(hSocket, &dataBuf, 1, &recvBytes, 0, &overlapped, NULL);
......
获取实际传输数据大小

WSASend函数调用过程中,如果输出缓冲是空的,且传输的数据并不大,那么函数调用后可以立即完成数据传输。此时WSASend函数将返回0,而lpNumberOfBytesSent中将保存实际传输的数据大小的信息。反之,WSASend函数返回后仍需要传输数据时,将返回SOCKET_ERROR,并将WSA_IO_PENDING注册为错误代码,该代码可以通过WSAGetLastError函数得到。这时使用WSAGetOverlappedResult函数获取实际传输的数据大小

#include <winsock2.h>

BOOL WSAGetOverlappedResult(SOCKET s, LPWSAOVERLAPPED lpOverlapped, LPDWORD lpcbTransfer, BOOL fWait, LPDWORD lpdwFlags);

成功时返回TRUE,失败时返回FALSE。其中:

  1. s:进行重叠I/O的套接字句柄
  2. lpOverlapped:进行重叠I/O时传递的WSAOVERLAPPED结构体变量的地址值
  3. lpcbTransfer:用于保存实际传输的字节数的变量地址值
  4. fWait:如果调用该函数时仍在进行I/O,fWait为TRUE时等待I/O完成,fWait为FALSE时将返回FALSE并跳出函数
  5. lpdwFlags:调用WSARecv函数时,用于获取附加信息(例如OOB消息)。如果不需要,可以传递NULL
WSARecv
#include <winsock2.h>

int WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);

成功时返回0,失败时返回SOCKET_ERROR。其中:

  1. s:赋予重叠I/O属性的套接字句柄
  2. lpBuffers:用于保存接收数据的WSABUF结构体数组地址值
  3. dwBufferCount:向第二个参数传递的数组的长度
  4. lpNumberOfBytesRecvd:保存接收的数据大小信息的变量地址值
  5. lpFlags:用于设置或读取传输特性信息
  6. lpOverlapped:WSAOVERLAPPED结构体变量地址值
  7. lpCompletionRoutine:Completion Routine函数地址值

确认重叠I/O的I/O完成

重叠I/O中有2种方法确认I/O的完成并获取结果:

  1. 利用WSASend、WSARecv函数的第六个参数,基于事件对象
  2. 利用WSASend、WSARecv函数的第七个参数,基于Completion Routine
使用事件对象
Sender
#include <stdio.h>
#include <stdlib.h>
#
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值