当前位置: 首页 > news >正文

重叠IO模型

重叠IO模型

重叠IO模型

同一个线程内部向多个目标传输(或接收)数据引起的IO重叠现象称为重叠IO。调用IO函数应该立即返回,IO函数以非阻塞的模式工作。

除了IO本身,如何确定IO完成时的状态也是十分重要的。

创建重叠IO套接字

使用WSASocket函数创建重叠IO套接字。第四个参数指定``

执行重叠IO的函数

WSASendWSARecv

通过WSAGetOverlappedResult函数获得实际传输数据的大小

重叠IO的完成确认

有两种方法确认重叠IO是否完成:

  • 利用WSASendWSARecv的第六个参数,基于事件对象
  • 利用WSASendWSARecv的第七个参数,基于completion routine

第一种

使用WSAWaitForMultipleEventsWSAGetOverlappedResult函数确认IO是否完成。详情参见如下代码:

/*
该程序使用重叠IO模型实现发送数据的客户端
*/#include <stdio.h>
#include <WinSock2.h>
#include <WS2tcpip.h>#define BUF_SIZE 50void ErrorHandling(const char* msg);int main(int argc, char* argv[])
{if (argc != 3)ErrorHandling("usage error");WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHandling("WSAStartup error");SOCKET hSock;//创建重叠IO套接字hSock = WSASocket(AF_INET, SOCK_STREAM, 0,NULL,//包含创建套接字时的信息的结构体的地址0, //为拓展函数使用的参数WSA_FLAG_OVERLAPPED //套接字属性信息,传递该参数可以赋予套接字重叠IO的特性);sockaddr_in servAdr;memset(&servAdr, 0, sizeof(servAdr));servAdr.sin_family = PF_INET;//servAdr.sin_addr.s_addr = inet_addr(argv[1]);inet_pton(PF_INET, argv[1], &servAdr.sin_addr.s_addr);servAdr.sin_port = htons(atoi(argv[2]));//连接服务器if (connect(hSock, (sockaddr*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR)ErrorHandling("connect error");int nAdrSize; //sockaddr结构体大小//为了进行重叠IO,WSASend函数的lpOverlapped参数中应该传递具有有效的结构体变量地址//而不是NULL,如果传递NULL,WSASend()第一个参数指定的套接字将以阻塞模式工作//如果使用WSASend函数对不同目标进行数据传输,需要传递不同的WSAOVERLAPPED结构体WSAOVERLAPPED overlapped;memset(&overlapped, 0, sizeof(overlapped));WSAEVENT evObj = WSACreateEvent();overlapped.hEvent = evObj; //初始化该字段WSABUF dataBuf; //包含两个字段:待传输数据大小,缓冲区地址char msg[] = "Network is Computer";dataBuf.len = strlen(msg);dataBuf.buf = msg;unsigned long LSendBytes = 0;//使用该函数进行重叠IO--发送数据,如果WSASend函数在返回前恰好完成了数据传输//则返回值为0,LSendBytes中将保存事件传输数据的大小,如果该函数返回时没有完成数据传输,//则返回SOCKET_ERROR,并将WSA_IO_PENDING注册为错误代码if (WSASend(hSock, //需要发送信息的套接字的句柄&dataBuf, //结构体数组首地址,该结构体中存有待传输的数据和大小1, //第二个参数中数组的长度&LSendBytes, //用于保存实际发送的字节数0, //数据传输属性&overlapped, //该结构体用于确认完成了数据传输NULL //completion routine函数的地址,可使用该函数确认数据传输是否完成) == SOCKET_ERROR){//该函数返回时数据没有全部移动到输出缓冲中时if (WSAGetLastError() == WSA_IO_PENDING){puts("Background data send");//该函数用于验证事件状态是否转换为signaledWSAWaitForMultipleEvents(1, //需要验证的事件数&evObj,	//需要验证的事件对象的句柄的数组TRUE, //所有事件对象转换为signaled状态时便返回WSA_INFINITE, //time-outFALSE //传递TRUE时进入可等待可警告状态);//该函数用于获取WSASend函数实际发送数据的大小WSAGetOverlappedResult(hSock, //进行重叠IO的套接字的句柄&overlapped, //进行重叠IO时使用的结构体&LSendBytes, //用于保存实际传输数据的大小FALSE, //如果该函数调用时仍在进行IO,该参数为TRUE时等待IO完成NULL //如果不需要获取附加信息,传递NULL);}elseErrorHandling("WSASend() error");}printf("send data size: %d\n", LSendBytes);WSACloseEvent(evObj);closesocket(hSock);WSACleanup();return 0;
}void ErrorHandling(const char* msg)
{fprintf_s(stderr, "%s\n", msg);exit(1);
}

第二种

Pending的IO完成时才会调用CR函数,只有请求IO的线程处于alertable wait状态时才可以调用CR函数。该状态是等待接收操作系统消息的线程状态。调用下列函数时进入该状态:

  • WaitForSingleObjectEx
  • WaitForMultipleObjectEx
  • WSAWaitForMultipleEvents
  • SleepEx

上述函数中的第一、二、四个与对应的非Ex函数功能相同,只是多了一个参数用于决定是否让线程进入alertable wait状态。启动重叠IO任务后,上述函数可验证IO是否完成,如果有已完成的IO任务,则会调用CR函数。调用后均返回WAIT_IO_COMPLETION

下面是具体代码:

#include <stdio.h>
#include <WinSock2.h>
#include <WS2tcpip.h>#define BUF_SIZE 50DWORD dwRecvBytes;
char buf[BUF_SIZE];void ErrorHandling(const char* msg)
{fprintf_s(stderr, "%s\n", msg);exit(1);
}void CALLBACK CmpRoutine(DWORD dwError, DWORD szRecvBytes, LPWSAOVERLAPPED lpOverlapped, DWORD flags)
{if (dwError != 0)ErrorHandling("CmpRoutine() error");else{dwRecvBytes = szRecvBytes;printf_s("recv data size: %d\n", dwRecvBytes);}
}int main(int argc, char* argv[])
{if (argc != 2)ErrorHandling("Usage error");WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHandling("WSAStartup() error");SOCKET hServSock, hClntSock;hServSock = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);if (hServSock == INVALID_SOCKET)ErrorHandling("WSASocket() error");sockaddr_in servAdr, clntAdr;memset(&servAdr, 0, sizeof(servAdr));servAdr.sin_family = PF_INET;servAdr.sin_addr.s_addr = htonl(INADDR_ANY);servAdr.sin_port = htons(atoi(argv[1]));if (bind(hServSock, (sockaddr*)&servAdr, sizeof(sockaddr)) == SOCKET_ERROR)ErrorHandling("bind() error");if (listen(hServSock, 5) == SOCKET_ERROR)ErrorHandling("listen() error");int szAdr, idx;unsigned long flags;while (1){WSAEVENT evObj = WSACreateEvent();WSAOVERLAPPED overlapped;memset(&overlapped, 0, sizeof(overlapped));overlapped.hEvent = evObj;WSABUF wsaBuf;wsaBuf.buf = buf;wsaBuf.len = BUF_SIZE;flags = 0;szAdr = sizeof(szAdr);hClntSock = accept(hServSock, (sockaddr*)&clntAdr, &szAdr);//最后一个参数指定CR函数,在IO完成时调用if (WSARecv(hClntSock,&wsaBuf,1,&dwRecvBytes,&flags,&overlapped,CmpRoutine)==SOCKET_ERROR){if (WSAGetLastError() == WSA_IO_PENDING){printf_s("bakcground data recv");}}//最后一个参数指定为TRUE,线程进入alertable wait状态idx = WSAWaitForMultipleEvents(1, &evObj, TRUE, WSA_INFINITE, TRUE);//调用CR函数后,上述函数返回WSA_IO_INCOMPLETEif (idx == WSA_IO_INCOMPLETE)printf("IO completed\n");elseprintf("IO error");WSACloseEvent(evObj);closesocket(hClntSock);}closesocket(hServSock);WSACleanup();return 0;
}

调用CR函数后,上述函数返回WSA_IO_INCOMPLETE
if (idx == WSA_IO_INCOMPLETE)
printf(“IO completed\n”);
else
printf(“IO error”);

	WSACloseEvent(evObj);closesocket(hClntSock);
}closesocket(hServSock);
WSACleanup();return 0;

}

http://www.dtcms.com/a/359063.html

相关文章:

  • MySQL基础理解入门
  • 微服务架构中的 “双保险“:服务保护与分布式事务解决方案实战
  • 执行一条Select语句流程
  • Linux从入门到进阶--第四章--Linux使用操作
  • 深度学习核心损失函数详解:交叉熵、MSE、对比学习(InfoNCE)
  • Linux中的Shell编程 第一章
  • TechPowerUp GPU-Z中文版:专业显卡检测工具
  • 分分合合,门模块方案又兴起了
  • 架构进阶——解读 69页 方法轮IT规划培训 架构-重点-细节【附全文阅读】
  • FFmpeg音视频处理解决方案
  • 互联网大厂面试:大模型应用开发岗位核心技术点解析
  • CSS基础学习第二天
  • 算法之x数之和
  • nginx配置websock请求,wss
  • GooglePlay提审问题记录
  • 生成式BI工具(WrenAI)
  • 防抖与节流的区别及实现【JS核心】
  • 恶补DSP:3.F28335的ePWM模块
  • 语义分割目前还是研究热点吗?
  • 【CF】Day136——Codeforces Round 1046 (Div. 2) CD (动态规划 | 数学)
  • 血氧检测原理与算法
  • Linux系统直接查询文件或目录绝对路径的方式
  • TensorFlow 深度学习 | 使用底层 API 实现模型训练(附可视化与 MLP)
  • HyperPlonk 的硬件友好性
  • Linux kernel 多核启动
  • LINUX-网络编程-TCP-UDP
  • Python 入门 Swin Transformer-T:原理、作用与代码实践
  • AI + 行业渗透率报告:医疗诊断、工业质检领域已进入规模化落地阶段
  • 通过数据蒸馏打破语音情感识别的资源壁垒
  • 基于单片机音乐喷泉/音乐流水灯/音乐播放器设计