windows socket网络编程四:异步选择模型

分析

参考windows消息机制的模型。
消息机制:所有的用户操作均依次按顺序(有序处理)被记录,装进一个队列,消息队列由操作系统维护,系统通过我们设置的回调函数处理不同类型的消息。

操作系统为每个窗口创建一个消息队列并且维护,所以我们想要使用消息队列,那就要创建一个窗口,该模型只能用于windows,不过我们可以学到这种处理思想。

服务器

直到开始监听都是一模一样的。
创建窗口也不是重点。

异步选择

int WSAAPI WSAAsyncSelect(
  SOCKET s,
  HWND   hWnd,
  u_int  wMsg,
  long   lEvent
);

功能:将socket与消息绑定在一起,并投递给操作系统
参数:

  • s — socket
  • hWnd — 窗口句柄
  • wMsg — 消息编号
  • lEvent — 消息类型(跟WSASelectEvent一模一样)

返回值:

  • 如果成功,返回0。
  • 如果失败,返回SOCKET_ERROR。

绑定服务器事件

代码:

// 自定义消息
#define UM_ASYNCSELECTMSG  WM_USER + 1

// 异步选择
if (SOCKET_ERROR == WSAAsyncSelect(socketServer, hWnd, UM_ASYNCSELECTMSG, FD_ACCEPT))
{
	printf("WSAAsyncSelect 失败 error:%d\n", WSAGetLastError());
	closesocket(socketServer);
	WSACleanup();
	return -1;
}

绑定客户端事件

// 绑定客户端事件
if (SOCKET_ERROR == WSAAsyncSelect(socketClient, hWnd, UM_ASYNCSELECTMSG, FD_READ | FD_WRITE | FD_CLOSE))
{
	printf("WSAAsyncSelect 失败 error:%d\n", WSAGetLastError());
	closesocket(socketClient);
	break;
}

事件分类处理

参数信息
客户端socket(SOCKET)wParam
产生的错误码HIWORD(lParam)
具体的消息种类LOWORD(lParam)

处理错误

因为有序,所以删除需要整体移位置。

SOCKET sock = (SOCKET)wparam;	// 获取socket
// 获取错误
if (0 != HIWORD(lparam))
{
	if (WSAECONNABORTED == HIWORD(lparam))
	{
		TextOut(hdc, 0, x, _T("客户端正常下线"), strlen("客户端正常下线"));
		x += 15;
		
		WSAAsyncSelect(sock, hWnd, 0, 0);	// 关闭该socket上的消息
		closesocket(sock);
		// 记录数组中有序删除该socket
		for (int i = 0; i < g_count; ++i)
		{
			if (sock == g_sockALL[i])
			{
				g_sockALL[i] = g_sockALL[g_count - 1];
				--g_count;
				break;
			}
		}
	}
	break;
}

处理其他消息

和事件选择模型类似

// 具体消息
switch (LOWORD(lparam))
{
case FD_ACCEPT:
	{
		TextOut(hdc, 0, x, _T("accept"), strlen("accept"));
		x += 15;
		SOCKET socketClient = accept(sock, NULL, NULL);
		if (INVALID_SOCKET == socketClient)
		{
			printf("accept 失败 error:%d\n", WSAGetLastError());
			break;
		}
		// 绑定客户端事件
		if (SOCKET_ERROR == WSAAsyncSelect(socketClient, hWnd, UM_ASYNCSELECTMSG, FD_READ | FD_WRITE | FD_CLOSE))
		{
			printf("WSAAsyncSelect 失败 error:%d\n", WSAGetLastError());
			closesocket(socketClient);
			break;
		}
		// 记录
		g_sockALL[g_count] = socketClient;
		++g_count;
	}
	break;
case FD_READ:
  		{
		TextOut(hdc, 0, x, _T("read"), strlen("read"));
		char str[1024] = { 0 };
		if (SOCKET_ERROR == recv(sock, str, 1023, 0))
		{
			printf("recv 失败 error:%d\n", WSAGetLastError());
			break;
		}
		else
		{
			// 给客户回信
			if (SOCKET_ERROR == send(sock, "ok", strlen("ok") + 1, 0))
			{
				printf("send 失败 error:%d\n", WSAGetLastError());
			}
		}
		TextOut(hdc, 30, x, str, strlen(str));
		x += 15;
	}
	break;
case FD_WRITE:
	TextOut(hdc, 0, x, _T("wrtie"), strlen("wrtie"));
	x += 15;
	break;
case FD_CLOSE:
	TextOut(hdc, 0, x, _T("客户端正常下线"), strlen("客户端正常下线"));
	x += 15;
	
	WSAAsyncSelect(sock, hWnd, 0, 0);	// 关闭该socket上的消息
	closesocket(sock);
	// 记录数组中有序删除该socket
	for (int i = 0; i < g_count; ++i)
	{
		if (sock == g_sockALL[i])
		{
			g_sockALL[i] = g_sockALL[g_count - 1];
			g_count--;
			break;
		}
	}
}

问题

在一次处理过程中,客户端产生多次send,服务器会产生多次接收消息,第一次接收消息会收完所有信息。

运行结果

在这里插入图片描述

模型流程图

在这里插入图片描述
和事件选择模型一样把select模型的同步阻塞变成了异步阻塞。

源码链接

百度云链接:https://pan.baidu.com/s/1xBOiSADlAG2gO1TC6BBO_A
提取码:sxbd

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值