C++Socket编程—socket网络模型之异步选择模型

时间:2020-05-26
本文章向大家介绍C++Socket编程—socket网络模型之异步选择模型 ,主要包括C++Socket编程—socket网络模型之异步选择模型 使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一、什么是异步选择模型

异步选择(WSAAsyncSelect)模型是一个异步 I/O 模型。利用这个模型,应用程序可在一个套接字上,接收以 Windows 消息机制为基础的网络事件通知,开发者将socket注册到消息机制,当socket有事件(新的连接,新的数据,连接断开,可以写入)来时候。具体的做法是在建好一个套接字后,调用WSAAsyncSelect函数。
该模型的核心即是WSAAsyncSelect函数,该函数是非阻塞的。

二、与select模型比较

相同点:
他们都可以对Windows套接字应用程序所使用的多个套接字进行有效的管理。

不同点:     
1.WSAAsyncSelect模型是异步的。在应用程序中调用WSAAsyncSelect()函数,通知系统感兴趣的网络事件,该函数立即返回,应用程序继续执行;   

2.发生网络事件时,应用程序得到的通知方式不同。Select()函数返回时,说明某个或者某些套接字满足可读可写的条件,应用程序需要使用FD_ISSET宏,判断套接字是否存在可读可写集合中。而对于WSAAsyncSelect模型来说,当网络事件发生时,系统向应用程序发送消息。    

3.WSAAsyncSelect模型应用在基于消息的Windos环境下,使用该模型时必须创建窗口。而Select模型广泛应用在Unix系统和Windows系统,使用该模型不需要创建窗口。    

4.应用程序调用WSAAsyncSelect()函数后,自动将套接字设置为非阻塞模式。而应用程序中调用select()函数后,并不能改变套接字的工作方式

三、异步选择模型API函数

WSAAsyncSelect函数定义如下:

 
    int WSAAsyncSelect(
        __in SOCKET s,              //指定的是我们感兴趣的那个套接字。
        __in HWND hWnd,          //指定一个窗口句柄,它对应于网络事件发生之后,想要收到通知消息的那个窗口。
        __in unsigned int wMsg,  //指定在发生网络事件时,打算接收的消息。该消息会投递到由hWnd窗口句柄指定的那个窗口。
        __in long lEvent              //指定一个位掩码,对应于一系列网络事件的组合
   );


WSAAsyncSelect模型是Select模型的异步版本,在调用select()函数时,会发生阻塞现象。可以通过select()函数timeout参数,设置函数调用的阻塞时间。在设定的时间内,线程保持等待,直到其中一个或多个套接字满足可读可写的条件时,该函数返回。

四、异步选择模型缺陷

不适合高并发网络结构,因为是基于窗口消息机制,消息太多就处理速度较慢。

五、代码示例

创建windwos窗口项目:

//1.添加头文件
#include<WinSock2.h>
#pragma comment(lib,"Ws2_32.lib")

 #define WM_MYSOCKMSG WM_USER+1
bool HandleData(SOCKET sockClient);//处理收发数据
//2.初始化WSAstartup
WORD wVersionRequested;
WSADATA wsaData;
int err;
 
wVersionRequested = MAKEWORD( 1, 1 );
 
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
 /* Tell the user that we could not find a usable */
 /* WinSock DLL.   */
 return 0;
}
 
/* Confirm that the WinSock DLL supports 1.1.*/
/* Note that if the DLL supports versions greater */
/* than 1.1 in addition to 1.1, it will still return */
/* 1.1 in wVersion since that is the version we */
/* requested.   */
 
if ( LOBYTE( wsaData.wVersion ) != 1 ||
 HIBYTE( wsaData.wVersion ) != 1 ) {
 /* Tell the user that we could not find a usable */
 /* WinSock DLL.   */
 WSACleanup( );
 return 0; 
}
//3.再处理消息处添加代码
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
	case WM_CREATE:
	{
		//1) 创建socket
		SOCKET sockServer = socket(
			AF_INET,
			SOCK_STREAM, //流式 
			IPPROTO_TCP);//tcp协议


		// 	2) 绑定端口
		sockaddr_in siServer;
		siServer.sin_family = AF_INET;
		siServer.sin_port = htons(9527);
		siServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
		int nRet = bind(sockServer, (sockaddr*)&siServer, sizeof(siServer));
		if (nRet == SOCKET_ERROR)
		{
			printf("绑定失败 \r\n");
			return 0;
		}

		// 	3) 监听
		nRet = listen(sockServer, SOMAXCONN);
		if (nRet == SOCKET_ERROR)
		{
			printf("监听失败 \r\n");
			return 0;
		}
		//4)接受连接

		//这个socket只关系新连接和关闭时间
		WSAAsyncSelect(sockServer, hWnd, WM_MYSOCKMSG, FD_ACCEPT | FD_CLOSE);

		break;
	}
	case WM_MYSOCKMSG:
	{
		SOCKET sock = (SOCKET)wParam;
		WORD wErrCode = WSAGETSELECTERROR(lParam);
		WORD wSelectEvent = WSAGETSELECTEVENT(lParam);

		switch (wSelectEvent)
		{
		case FD_ACCEPT:
		{
			sockaddr_in siClient;
			int nSize = sizeof(siClient);
			SOCKET sockClient = accept(sock, (sockaddr*)&siClient, &nSize);

			//为新的连接注册对应的网络事件
			WSAAsyncSelect(sockClient, hWnd, WM_MYSOCKMSG, FD_READ | FD_CLOSE);
			break;   
		}
		case FD_READ://数据来了
		{
			HandleData(sock); //处理数据
			break;
		}
		case FD_CLOSE://连接断开
		{
			//将sock从网络事件中移除
			closesocket(sock);
			break;
		}
		}
		break;
	}

    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

bool HandleData(SOCKET sockClient)
{
	// 	5) 收发数据
	char aryBuff[MAXWORD] = { 0 };
	int nRet = recv(sockClient, aryBuff, sizeof(aryBuff), 0);
	if (nRet == 0 || nRet == SOCKET_ERROR)
	{
		printf("接受数据失败 \r\n");
		return false;
	}
	printf("收到数据: %s \r\n", aryBuff);

	char szBuff[] = { "recv OK " };
	nRet = send(sockClient, szBuff, sizeof(szBuff), 0);
	if (nRet == SOCKET_ERROR)
	{
		printf("数据发送失败 \r\n");
		return false;
	}

	return true;
}

原文地址:https://www.cnblogs.com/zhaoyixiang/p/12964762.html