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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM