• 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("绑定失败 
    ");
    			return 0;
    		}
    
    		// 	3) 监听
    		nRet = listen(sockServer, SOMAXCONN);
    		if (nRet == SOCKET_ERROR)
    		{
    			printf("监听失败 
    ");
    			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("接受数据失败 
    ");
    		return false;
    	}
    	printf("收到数据: %s 
    ", aryBuff);
    
    	char szBuff[] = { "recv OK " };
    	nRet = send(sockClient, szBuff, sizeof(szBuff), 0);
    	if (nRet == SOCKET_ERROR)
    	{
    		printf("数据发送失败 
    ");
    		return false;
    	}
    
    	return true;
    }
    
  • 相关阅读:
    集群项目总结和思考
    centos7安装zabbix
    string方法中字符与Unicode编码的相互转化:fromCharCode(),charCodeAt()
    element-ui @change添加自定义参数
    js ES6字符串的新功能
    js 去除字符串中的空格
    五月份,是金色的
    夏天快要到来了,2019年还有一半多的时间
    自定义element-ui中的图标
    js下拉框:从数组中筛选出匹配的数据
  • 原文地址:https://www.cnblogs.com/zhaoyixiang/p/12964762.html
Copyright © 2020-2023  润新知