日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

socket编程的select模型

發(fā)布時(shí)間:2025/3/21 编程问答 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 socket编程的select模型 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
在掌握了socket相關(guān)的一些函數(shù)后,套接字編程還是比較簡(jiǎn)單的,日常工作中碰到很多的問題就是客戶端/服務(wù)器模型中,如何讓服務(wù)端在同一時(shí)間高效的處理多個(gè)客戶端的連接,我們的處理辦法可能會(huì)是在服務(wù)端不停的監(jiān)聽客戶端的請(qǐng)求,有新的請(qǐng)求到達(dá)時(shí),開辟一個(gè)新的線程去和該客戶端進(jìn)行后續(xù)處理,但是這樣針對(duì)每一個(gè)客戶端都需要去開辟一個(gè)新的線程,效率必定底下。

???? 其實(shí),socket編程提供了很多的模型來(lái)處理這種情形,我們只要按照模型去實(shí)現(xiàn)我們的代碼就可以解決這個(gè)問題。主要有select模型和重疊I/o模型,以及完成端口模型。這次,我們主要介紹下select模型,該模型又分為普通select模型,wsaasyncselect模型,wsaeventselect模型。我們將通過(guò)樣例代碼的方式逐一介紹。

一、select模型

使用該模型時(shí),在服務(wù)端我們可以開辟兩個(gè)線程,一個(gè)線程用來(lái)監(jiān)聽客戶端的連接

請(qǐng)求,另一個(gè)用來(lái)處理客戶端的請(qǐng)求。主要用到的函數(shù)為select函數(shù)。如:

全局變量:fd_set g_fdClientSock;

線程1處理函數(shù):

SOCKET listenSock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(7788);sin.sin_addr.S_un.S_addr = INADDR_ANY;int nRet = bind( listenSock, (sockaddr*)&sin, (int)(sizeof(sin)));if ( nRet == SOCKET_ERROR ){DWORD errCode = GetLastError();return;}listen( listenSock, 5);int clientNum = 0;sockaddr_in clientAddr;int nameLen = sizeof( clientAddr );while( clientNum < FD_SETSIZE ){SOCKET clientSock = accept( listenSock, (sockaddr*)&clientAddr, &nameLen );FD_SET( clientSock, &g_fdClientSock);clientNum++;}

線程2處理函數(shù):

fd_set fdRead;FD_ZERO( &fdRead );int nRet = 0;char* recvBuffer =(char*)malloc( sizeof(char) * 1024 );if ( recvBuffer == NULL ){return;}memset( recvBuffer, 0, sizeof(char) * 1024 ); while ( true ){fdRead = g_fdClientSock;nRet = select( 0, &fdRead, NULL, NULL, NULL );if ( nRet != SOCKET_ERROR ){for ( int i = 0; i < g_fdClientSock.fd_count; i++ ){if ( FD_ISSET(g_fdClientSock.fd_array[i],&fdRead) ){memset( recvBuffer, 0, sizeof(char) * 1024 );nRet = recv( g_fdClientSock.fd_array[i], recvBuffer, 1024, 0);if ( nRet == SOCKET_ERROR ){closesocket( g_fdClientSock.fd_array[i] );FD_CLR( g_fdClientSock.fd_array[i], &g_fdClientSock );}else{//todo:后續(xù)處理 }}}}} if ( recvBuffer != NULL ){free( recvBuffer );}

該模型有個(gè)最大的缺點(diǎn)就是,它需要一個(gè)死循環(huán)不停的去遍歷所有的客戶端套接字集合,詢問是否有數(shù)據(jù)到來(lái),這樣,如果連接的客戶端很多,勢(shì)必會(huì)影響處理客戶端請(qǐng)求的效率,但它的優(yōu)點(diǎn)就是解決了每一個(gè)客戶端都去開辟新的線程與其通信的問題。如果有一個(gè)模型,可以不用去輪詢客戶端套接字集合,而是等待系統(tǒng)通知,當(dāng)有客戶端數(shù)據(jù)到來(lái)時(shí),系統(tǒng)自動(dòng)的通知我們的程序,這就解決了select模型帶來(lái)的問題了。

二、WsaAsyncSelect模型

WsaAsyncSelect模型就是這樣一個(gè)解決了普通select模型問題的socket編程模型。它是在有客戶端數(shù)據(jù)到來(lái)時(shí),系統(tǒng)發(fā)送消息給我們的程序,我們的程序只要定義好消息的處理方法就可以了,用到的函數(shù)只要是WSAAsyncSelect,如:

首先,我們定義一個(gè)Windows消息,告訴系統(tǒng),當(dāng)有客戶端數(shù)據(jù)到來(lái)時(shí),發(fā)送該消息給我們。

#define UM_SOCK_ASYNCRECVMSG WM_USER + 1

在我們的處理函數(shù)中可以如下監(jiān)聽客戶端的連接:

SOCKET listenSock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(7788);sin.sin_addr.S_un.S_addr = INADDR_ANY;int nRet = bind( listenSock, (sockaddr*)&sin, (int)(sizeof(sin)));if ( nRet == SOCKET_ERROR ){DWORD errCode = GetLastError(); return;}listen( listenSock, 5); int clientNum = 0;sockaddr_in clientAddr;int nameLen = sizeof( clientAddr );while( clientNum < FD_SETSIZE ){SOCKET clientSock = accept( listenSock, (sockaddr*)&clientAddr, &nameLen );//hWnd為接收系統(tǒng)發(fā)送的消息的窗口句柄 WSAAsyncSelect( clientSock, hWnd, UM_SOCK_ASYNCRECVMSG, FD_READ | FD_CLOSE );clientNum++;}

?

接下來(lái),我們需要在我們的窗口添加對(duì)UM_SOCK_ASYNCRECVMSG消息的處理函數(shù),在該函數(shù)中真正接收客戶端發(fā)送過(guò)來(lái)的數(shù)據(jù),在這個(gè)消息處理函數(shù)中的wparam參數(shù)表示的是客戶端套接字,lparam參數(shù)表示的是發(fā)生的網(wǎng)絡(luò)事件如:

SOCKET clientSock = (SOCKET)wParam; if ( WSAGETSELECTERROR( lParam ) ){closesocket( clientSock );return;}switch ( WSAGETSELECTEVENT( lParam ) ){case FD_READ:{char recvBuffer[1024] = {'\0'};int nRet = recv( clientSock, recvBuffer, 1024, 0 );if ( nRet > 0 ){szRecvMsg.AppendFormat(_T("Client %d Say:%s\r\n"), clientSock, recvBuffer );}else{//client disconnectszRecvMsg.AppendFormat(_T("Client %d Disconnect!\r\n"), clientSock );}} break;case FD_CLOSE:{closesocket( clientSock );szRecvMsg.AppendFormat(_T("Client %d Disconnect!\r\n"), clientSock );}break;}

??? 可以看到WsaAsyncSelect模型是非常簡(jiǎn)單的模型,它解決了普通select模型的問題,但是它最大的缺點(diǎn)就是它只能用在windows程序上,因?yàn)樗枰粋€(gè)接收系統(tǒng)消息的窗口句柄,那么有沒有一個(gè)模型既可以解決select模型的問題,又不限定只能是windows程序才能用呢?下面我們來(lái)看看WsaEventSelect模型。

三、WsaEventSelect模型

WsaEventSelect模型是一個(gè)不用主動(dòng)去輪詢所有客戶端套接字是否有數(shù)據(jù)到來(lái)的模型,它也是在客戶端有數(shù)據(jù)到來(lái)時(shí),系統(tǒng)發(fā)送通知給我們的程序,但是,它不是發(fā)送消息,而是通過(guò)事件的方式來(lái)通知我們的程序,這就解決了WsaAsyncSelect模型只能用在windows程序的問題。

該模型的實(shí)現(xiàn),我們也可以開辟兩個(gè)線程來(lái)進(jìn)行處理,一個(gè)用來(lái)接收客戶端的連接請(qǐng)求,一個(gè)用來(lái)與客戶端進(jìn)行通信,用到的主要函數(shù)有WSAEventSelect,WSAWaitForMultipleEvents,WSAEnumNetworkEvents實(shí)現(xiàn)方式如下:

首先定義三個(gè)全局?jǐn)?shù)組

SOCKET g_SockArray[MAX_NUM_SOCKET];//存放客戶端套接字 WSAEVENT g_EventArray[MAX_NUM_SOCKET];//存放該客戶端有數(shù)據(jù)到來(lái)時(shí),觸發(fā)的事件 UINT32 g_totalEvent = 0;//記錄客戶端的連接數(shù)

線程1處理函數(shù)如下:

SOCKET listenSock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(7788);sin.sin_addr.S_un.S_addr = INADDR_ANY;int nRet = bind( listenSock, (sockaddr*)&sin, (int)(sizeof(sin)));if ( nRet == SOCKET_ERROR ){DWORD errCode = GetLastError();return;}listen( listenSock, 5);sockaddr_in clientAddr;int nameLen = sizeof( clientAddr );while( g_totalEvent < MAX_NUM_SOCKET ){SOCKET clientSock = accept( listenSock, (sockaddr*)&clientAddr, &nameLen );if ( clientSock == INVALID_SOCKET ){continue;}g_SockArray[g_totalEvent] = clientSock;if( (g_EventArray[g_totalEvent] = WSACreateEvent()) == WSA_INVALID_EVENT ){continue;}WSAEventSelect( clientSock, g_EventArray[g_totalEvent],FD_READ | FD_CLOSE );g_totalEvent++;}

??? 線程2的處理函數(shù)如下:

int nIndex = 0; char* recvBuffer =(char*)malloc( sizeof(char) * 1024 );if ( recvBuffer == NULL ){return;}memset( recvBuffer, 0, sizeof(char) * 1024 ); while( true ){nIndex = WSAWaitForMultipleEvents( g_totalEvent, g_EventArray, FALSE, WSA_INFINITE,FALSE );if ( nIndex == WSA_WAIT_FAILED ){continue;}else{ WSAResetEvent( g_EventArray[ nIndex - WSA_WAIT_EVENT_0]);SOCKET clientSock = g_SockArray[ nIndex - WSA_WAIT_EVENT_0 ];WSANETWORKEVENTS wsaNetWorkEvent;int nRet = WSAEnumNetworkEvents( clientSock, g_EventArray[nIndex - WSA_WAIT_EVENT_0], &wsaNetWorkEvent );if ( SOCKET_ERROR == nRet ){continue;}else if ( wsaNetWorkEvent.lNetworkEvents & FD_READ ){if ( wsaNetWorkEvent.iErrorCode[FD_READ_BIT] != 0 ){//occur errorclosesocket( clientSock );}else {memset( recvBuffer, 0, sizeof(char) * 1024 );nRet = recv( clientSock, recvBuffer, 1024, 0);if ( nRet == SOCKET_ERROR ){closesocket( clientSock );}else{//todo:對(duì)接收到的客戶端數(shù)據(jù)進(jìn)行處理}}}else if( wsaNetWorkEvent.lNetworkEvents & FD_CLOSE ){if ( wsaNetWorkEvent.iErrorCode[FD_CLOSE_BIT] != 0 ){//occur errorclosesocket( clientSock );}else{closesocket( clientSock );} }}}if ( recvBuffer != NULL ){free( recvBuffer );}

?

???? 該模型通過(guò)一個(gè)死循環(huán)里面調(diào)用WSAWaitForMultipleEvents函數(shù)來(lái)等待客戶端套接字對(duì)應(yīng)的Event的到來(lái),一旦事件通知到達(dá),就通過(guò)該套接字去接收數(shù)據(jù)。雖然WsaEventSelect模型的實(shí)現(xiàn)較前兩種方法復(fù)雜,但它在效率和兼容性方面是最好的。

??? 以上三種模型雖然在效率方面有了不少的提升,但它們都存在一個(gè)問題,就是都預(yù)設(shè)了只能接收64個(gè)客戶端連接,雖然我們?cè)趯?shí)現(xiàn)時(shí)可以不受這個(gè)限制,但是那樣,它們所帶來(lái)的效率提升又將打折扣,那又有沒有什么模型可以解決這個(gè)問題呢?我們的下一篇重疊I/0模型將解決這個(gè)問題

總結(jié)

以上是生活随笔為你收集整理的socket编程的select模型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。