IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++ Windows Socket五种I/O模型之重叠IO模型(二) -> 正文阅读

[C++知识库]C++ Windows Socket五种I/O模型之重叠IO模型(二)

前面一节我们简单的介绍了事件通知的重叠IO模型,有兴趣的可以点击
C++ Windows Socket五种I/O模型之重叠IO模型(一)查看一下,这一章主要是介绍一下完成例程模型。

基本原理
完成例程在投递一个请求之后如(WSARecv)之后,系统完成之后不再以事件通知你,而是在完成之后自动调用你提供的LPWSAOVERLAPPED_COMPLETION_ROUTINE 类型的的回调函数来处理消息。

WSARecv
在重叠模型中,接收数据就要靠它了,它的参数也比recv要多,和事件通知重叠唯一不同的是最后一个参数: lpCompletionRoutine,名称你可以随便起,但是他参数是固定的,下面详细接收

int WSARecv(
    SOCKET s,// 当然是投递这个操作的套接字
    LPWSABUF lpBuffers,       //接收缓冲区,与Recv函数不同,需要一个由WSABUF结构构成的数组
    DWORD dwBufferCount,      // 数组中WSABUF结构的数量
    LPDWORD lpNumberOfBytesRecvd,// 如果接收操作立即完成,这里会返回函数调用接收到的字节数
    LPDWORD lpFlags,             // 具体内容比较多,具体可以看MSDN,可以对接收数据进行筛选,我们这里设置为0即可
    LPWSAOVERLAPPED lpOverlapped,// “绑定”的重叠结构
    LPWSAOVERLAPPED_COMPLETION_ROUTINE  lpCompletionRoutine //在这你要设定成你回调函数的名称,比如:CompletionROUTINE
);

LPWSAOVERLAPPED_COMPLETION_ROUTINE 类型回调函数
这是完成例程重叠模式关键函数,名称你可以随便命名,但是查询Windows文档中可以看到他的参数是固定如下:

LPWSAOVERLAPPED_COMPLETION_ROUTINE LpwsaoverlappedCompletionRoutine;
void LpwsaoverlappedCompletionRoutine(
  DWORD dwError,
  DWORD cbTransferred,
  LPWSAOVERLAPPED lpOverlapped,
  DWORD dwFlags
)
{...}

参数:
dwError 表示完成状态,0 表示成功,其他代表不同错误码
cbTransferred 表示都到数据长度,如果出现错误,值为 0
lpOverlapped 返回的重叠结构
dwFlags 与呼叫关联的标志

SleepEX
这一个函数是完成回调的一个重要函数,如果不调用这个函数,回调函数不会执行,函数原型:

DWORD WINAPI SleepEx(
    DWORD dwMillesconds,
    BOOL bAlertable);

参数:
dwMillesconds 线程挂起的最短时间,以毫秒为单位。
bAlertable 如果设置为FALSE,这个函数直到设定的最短时间才会返回,即使中间有回调发生,函数也不回返回,并且不会执行回调函数。
如果设置为TRUE,且调用此函数的线程与调用扩展I/O函数(ReadFileEx或WriteFileEx)的线程相同,则当超时时间已过或出现I/O完成回调函数时,该函数将返回。如果发生I/O完成回调,则调用I/O完成函数。如果一个APC被排队到线程(QueueUserAPC),则该函数在计时器超时或调用APC函数时返回。(简而言之,就是如果要调用回调,此处设置为TRUE)

基本函数就介绍到这个地方,是具体的代码(服务端):
编译Vs2019

#include <iostream>
#include <WinSock2.h>
#define  MSGSIZE 1024
#include <thread>
using namespace std;
#pragma  comment(lib,"ws2_32.lib")
SOCKET g_sNewSocket;
bool   g_bNewConnectionArrived = FALSE;
typedef struct
{
	WSAOVERLAPPED overlap;
	SOCKET socket;
	WSABUF buffer;
	char          szMessage[MSGSIZE];
	DWORD         NumberOfBytesRecvd;
	DWORD         Flags;
} PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;

//回调函数
void CALLBACK CompletionROUTINE(DWORD dwError,DWORD cbTransferred,
	LPWSAOVERLAPPED lpOverlapped,DWORD dwFlags)
{
	LPPER_IO_OPERATION_DATA lap = (LPPER_IO_OPERATION_DATA)lpOverlapped;
	if (dwError!=0 || cbTransferred==0)
	{
		closesocket(lap->socket);
		HeapFree(GetProcessHeap(), 0, lap);
	}
	else
	{
		cout << lap->szMessage << endl;
		//清空重叠结构和缓冲区
		ZeroMemory(&lap->overlap, sizeof(WSAOVERLAPPED));
		ZeroMemory(lap->szMessage,MSGSIZE);
		lap->buffer.buf = lap->szMessage;
		//继续投递重叠结构
		WSARecv(lap->socket,
			&lap->buffer,
			1,
			&lap->NumberOfBytesRecvd,
			&lap->Flags,
			&lap->overlap,
			CompletionROUTINE);

	}

}

void workThread()
{
	while (true)
	{
		if (g_bNewConnectionArrived)
		{
			LPPER_IO_OPERATION_DATA lap = (LPPER_IO_OPERATION_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_IO_OPERATION_DATA));
			lap->buffer.len = MSGSIZE;
			lap->buffer.buf = lap->szMessage;
			lap->socket = g_sNewSocket;
			
			//重叠投递

			WSARecv(g_sNewSocket, &lap->buffer, 1, &lap->NumberOfBytesRecvd, &lap->Flags, &lap->overlap, CompletionROUTINE); 
			g_bNewConnectionArrived = false;

		}
		SleepEx(1000, TRUE);

	}

}

bool initSocket()
{
	WSADATA wsdata;

	WORD sVer = MAKEWORD(2, 2);

	int  ret = WSAStartup(sVer, &wsdata);

	if (ret != 0)
	{
		return false;
	}
	return true;
}

int main()
{
	//初始化com
	if (!initSocket())
	{
		return -1;
	}
	sockaddr_in Sa, recvSa;
	int len = sizeof(Sa);
	Sa.sin_port = htons(8888);
	Sa.sin_addr.S_un.S_addr = INADDR_ANY;
	Sa.sin_family = AF_INET;
	SOCKET  sock = socket(AF_INET, SOCK_STREAM, 0);

	if (sock == INVALID_SOCKET)
	{
		cout << "Create socket fail" << endl;
		return -1;
	}
	if (bind(sock, (sockaddr*)&Sa, len) == SOCKET_ERROR)
	{
		cout << "bind fail" << endl;
		return -1;
	}
	if (listen(sock, 5) == SOCKET_ERROR)
	{
		cout << "listen fail" << endl;
		return -1;
	}

	//创建工作线程
	thread t(workThread);
	t.detach();

	while (1)
	{
		g_sNewSocket= accept(sock, (sockaddr*)&recvSa, &len);
		if (g_sNewSocket!=INVALID_SOCKET)
		{
			g_bNewConnectionArrived =true;
		}
	}

	return 0;
}
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-11-27 09:43:57  更:2021-11-27 09:44:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:49:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码