C++ Windows Socket五种I/O模型之WSAAsyncSelect模型

WSAAsyncSelect模型是Select模型的异步版本,在调用Select()函数的时候会发生阻塞现象,而WSAAsyncSelect()则不会。

不同点

1.WSAAsyncSelect模型是异步的。在应用程序中调用WSAAsyncSelect()函数,通知系统感兴趣的网络事件,该函数立即返回,应用程序继续执行;
2.发生网络事件时,应用程序得到的通知方式不同。Select()函数返回时,说明某个或者某些套接字满足可读可写的条件,应用程序需要使用FD_ISSET宏,判断套接字是否存在可读可写集合中。而对于WSAAsyncSelect模型来说,当网络事件发生时,系统向应用程序发送消息。
3.WSAAsyncSelect模型应用在基于消息的Windos环境下,使用该模型时必须创建窗口。而Select模型广泛应用在Unix系统和Windows系统,使用该模型不需要创建窗口。
4.应用程序调用WSAAsyncSelect()函数后,自动将套接字设置为非阻塞模式。而应用程序中调用select()函数后,并不能改变套接字的工作方式。

函数原型

int WSAAsyncSelect(
__in SOCKET s,
__in HWND hWnd,
__in unsigned int wMsg,
__in long lEvent
);
● s 参数指定的是我们感兴趣的那个套接字。
● hWnd 参数指定一个窗口句柄,它对应于网络事件发生之后,想要收到通知消息的那个窗口。
● wMsg 参数指定在发生网络事件时,打算接收的消息。该消息会投递到由hWnd窗口句柄指定的那个窗口。(通常,应用程序需要将这个消息设为比Windows的WM_USER大的一个值,避免网络窗口消息与系统预定义的标准窗口消息发生混淆与冲突)
● lEvent 参数指定一个位掩码,对应于一系列网络事件的组合,大多数应用程序通常感兴趣的网络事件类型包括: FD_READ、FD_WRITE、FD_ACCEPT、FD_CONNECT、FD_CLOSE。当然,到底使用FD_ACCEPT,还是使用FD_CONNECT类型,要取决于应用程序的身份是客户端,还是服务器。如应用程序同时对多个网络事件有兴趣,只需对各种类型执行一次简单的按位OR(或)运算,然后将它们分配给lEvent就可以了,例如:
WSAAsyncSeltct(s, hwnd, WM_SOCKET, FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE);
解释说明:我们的应用程序以后便可在套接字s上,接收到有关连接、发送、接收以及套接字关闭这一系列网络事件的通知。

创建窗口
异步选择模型基于Windows的消息机制,必须创建窗口。在Windows编程里,有个WinMain()入口函数,和C++中的main()类,其原型为:

int WINAPI WinMain(
In HINSTANCE hInstance,
In HINSTANCE hPrevInstance,
In LPSTR lpCmdLine,
In int nCmdShow
)
参数:hInstance 是第一次运行的实例句柄,
hPrevInstance是前一个实例句柄,例如:把同一个界面程序(a.exe)运行两次,进程里面就会有2个a.exe,第一次假设为01,就会传给hInstance ,第二次为02,就会传给hPrevInstance
lpCmdLine 是指命令行
nCmdShow 窗口的显示方式

利用WNDClASSEX 创建窗口

主要流程为:

设置窗口属性
注册
创建窗口
显示窗口

示例:

	char szClassName[] = "MyWNDClass";
	char szTitleName[] = "MyWindow";
	WNDCLASSEX  ws;
	ws.cbSize = sizeof(WNDCLASSEX);
	ws.style = CS_HREDRAW | CS_VREDRAW; //Heigth and width 宽度和高度发生变化重绘
	ws.cbClsExtra = 0;
	ws.cbWndExtra = 0;
	ws.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	ws.hInstance = hinstance;
	ws.lpfnWndProc = WndProc;
	ws.hCursor = LoadCursor(NULL,IDC_ARROW);
	ws.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(IDI_APPLICATION));
	ws.hIconSm=LoadIcon(ws.hInstance,MAKEINTRESOURCE(IDI_WINLOGO));

	ws.lpszMenuName = NULL;
	ws.lpszClassName = szClassName;
	RegisterClassEx(&ws); 
	HWND hWnd;
	hWnd=CreateWindowEx(NULL, szClassName, szTitleName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinstance, NULL);
	ShowWindow(hWnd, nShowCmd);
	UpdateWindow(hWnd);
	//消息循环处理
	MSG  msg;
	while (GetMessage(&msg,NULL,0,0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

注意事项:注册窗口类之前必须填充WNDCLASSEX的结构体,也就是要为所有的成员赋值,就算你不需要,也要赋值为零或者Null,没有初始化就无法正确的分配内存,导致注册失败。解决方法:
WNDCLASS ws={};

回调函数

LRESULT CALLBACK WndPro(
In HWND hwnd,
In UINT uMsg,
In WPARAM wParam,
In LPARAM lParam
)
参数:hwnd 接收消息的窗口句柄
message 网络事件发生时要接收的消息
wParam 标识网络事件发生的套接字
lParam 低字节指明了发生的网络事件,高字节含有一个错误代码

示例:


#include <windows.h>
#include <iostream>
#include <tchar.h>
#pragma comment(lib,"ws2_32.lib")

#define  _WINSOCK_DEPRECATED_NO_WARNINGS

#define  WM_SOCKET WM_USER+1

HINSTANCE hInst;
LRESULT  CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	sockaddr_in  recvSa;
	static SOCKET sListen;

	int len = sizeof(sockaddr_in);

	char recvBuf[1024];
	int ret = 0;

	switch (message)
	{
	case WM_CREATE:
	{
		WSADATA wsData;
		sockaddr_in sa;
		WSAStartup(MAKEWORD(2, 2), &wsData);

	    sListen = socket(AF_INET, SOCK_STREAM, 0);

		sa.sin_family = AF_INET;
		sa.sin_port = htons(8888);
		sa.sin_addr.S_un.S_addr = INADDR_ANY;

		bind(sListen, (sockaddr*)&sa, len);

		listen(sListen, 5);
		WSAAsyncSelect(sListen, hwnd, WM_SOCKET, FD_ACCEPT);
	}
		break;
	case WM_PAINT:
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	case WM_SOCKET:
	{
		if (WSAGETSELECTERROR(lParam))
		{
			closesocket(wParam);
			break;
		}
		switch (WSAGETSELECTEVENT(lParam))
		{


		case FD_ACCEPT:
		{
			SOCKET sClient = accept(wParam, (sockaddr*)&recvSa, &len);
			WSAAsyncSelect(sClient, hwnd, WM_SOCKET, FD_READ | FD_CLOSE);
		}
			break;
		case FD_READ:
			ZeroMemory(recvBuf, 1024);
			ret = recv(wParam, recvBuf, 1024, 0);
			if (ret>0)
			{
				WCHAR sBuf[256] = { 0 };

				MultiByteToWideChar(CP_ACP, 0, recvBuf, strlen(recvBuf) + 1, sBuf, sizeof(sBuf) / sizeof(sBuf[0]));

				//OutputDebugString(sBuf);

				//MessageBox(nullptr, sBuf,nullptr,NULL);

				


			}



			else if ( ret == 0)
			{
				closesocket(wParam);
			}
			//MessageBox(hwnd, recvBuf, "提示", MB_OK);
			break;
		case FD_CLOSE:
			//MessageBox(hwnd, "break", "提示", MB_OK);
			closesocket(wParam);
			break;
		default:
			break;
		}
	}
	default:
		return DefWindowProc(hwnd, message, wParam, lParam);
		break;
	}
	return 0;
}
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{


	WCHAR szClassName[] = _T("MyWNDClass");
	WCHAR szTitleName[] = _T("MyWindow");
	WNDCLASSEX  ws;

	ws.cbSize = sizeof(WNDCLASSEX);

	ws.style = CS_HREDRAW | CS_VREDRAW; //Heigth and width 宽度和高度发生变化重绘

	ws.cbClsExtra = 0;
	ws.cbWndExtra = 0;
	ws.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	ws.hInstance = hinstance;
	ws.lpfnWndProc = WndProc;
	ws.hCursor = LoadCursor(NULL, IDC_ARROW);
	/*
	* IDI_APPLICATION 缺省引用程序图标
	*IDI_ASTERISK 星号
	* IDI_WINLOGO 语言图标
	* IDI_ERROR 手形图标
	*/
	ws.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(IDI_APPLICATION));

	ws.hIconSm = LoadIcon(ws.hInstance, MAKEINTRESOURCE(IDI_WINLOGO));

	ws.lpszMenuName = NULL;
	ws.lpszClassName = szClassName;


	//Register  失败返回NULL

	RegisterClassEx(&ws);

	//CreateWinow  and show
	//重叠窗口  
	HWND hWnd;
	hWnd = CreateWindowEx(NULL, szClassName, szTitleName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinstance, NULL);

	hInst = hinstance;

	ShowWindow(hWnd, nShowCmd);
	//ReDraw

	UpdateWindow(hWnd);

	MSG  msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return  msg.wParam;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值