栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++ Windows Socket五种I/O模型之重叠IO模型(二)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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 
#include 
#define  MSGSIZE 1024
#include 
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;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/629027.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号