阻塞式的Socket很容易了解,但是使用起來非常别扭。Windows提供了選擇(Select)I/O模型,進行異步Socket通信.
Select模型
int select(
_In_ int nfds,//忽略. 此參數為了與Berkeley sockets相容.
_Inout_ fd_set *readfds,//檢查可讀性fd_set指針.
_Inout_ fd_set *writefds,//檢查可寫性fd_set指針.
_Inout_ fd_set *exceptfds,//檢查錯誤fd_set指針
_In_ const struct timeval *timeout//逾時
);
本函數用于确定一個或多個套接口的狀态。對每一個套接口,調用者可查詢它的可讀性、可寫性及錯誤狀态資訊。用fd_set結構來表示一組等待檢查的套接口。在調用傳回時,這個結構存有滿足一定條件的套接口組的子集,并且select()傳回滿足條件的套接口的數目。
下面是代碼:
#include <Winsock2.h>
#include <stdio.h>
#include <process.h>
static const int PORT = ;
static const int BUFFER_LENGTH = ;
static int g_TotalConn = ;
static SOCKET g_ClientSocket[FD_SETSIZE] ;
unsigned int __stdcall WorerkThread(void *);
bool InitWSA() ;
int main()
{
if(!InitWSA())
{
return - ;
}
SOCKET sockListen = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP) ;
SOCKADDR_IN addrSrv ;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_LOOPBACK) ;
addrSrv.sin_family = AF_INET ;
addrSrv.sin_port = htons(PORT) ;
bind (sockListen, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR)) ;
listen (sockListen, ) ;
_beginthreadex(NULL, , WorerkThread, NULL, , NULL) ;
SOCKADDR_IN addrClient ;
int len = sizeof (addrClient) ;
while (true)
{
SOCKET client = accept(sockListen, (SOCKADDR *)&addrClient, &len) ;
g_ClientSocket[g_TotalConn++] = client ;
}
closesocket(sockListen) ;
WSACleanup () ;
return ;
}
unsigned int __stdcall WorerkThread(void *)
{
printf("線程begin:\n") ;
int i;
fd_set fdread;
fd_set fdwrite;
int ret;
struct timeval tv = {, };
char szMessage[BUFFER_LENGTH];
while (true)
{
FD_ZERO(&fdread);
FD_ZERO(&fdwrite);
for (i = ; i < g_TotalConn; i++)
{
FD_SET(g_ClientSocket[i], &fdread) ;
FD_SET(g_ClientSocket[i], &fdwrite) ;
}
ret = select(, &fdread, &fdwrite, NULL, &tv);
/*
if(g_TotalConn > 0 && ret == SOCKET_ERROR)
{
printf("Socket:an error occurred!\n") ;
}
*/
if (ret == )
{
printf("逾時\n") ;
continue ;
}
for (i = ; i < g_TotalConn; i++)
{
if (FD_ISSET(g_ClientSocket[i], &fdread))
{
ret = recv(g_ClientSocket[i], szMessage, BUFFER_LENGTH, );
if (ret == || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))
{
printf("Client socket %d closed.\n", i);
closesocket(g_ClientSocket[i]);
g_ClientSocket[i] = ;
if(i != g_TotalConn -)
{
g_ClientSocket[i--] = g_ClientSocket[g_TotalConn -] ;
}
g_TotalConn-- ;
}
else
{
szMessage[ret] = '\0';
send(g_ClientSocket[i], szMessage, strlen(szMessage), );
printf("發送資料給Client:%s\n", szMessage) ;
}
}
if (FD_ISSET(g_ClientSocket[i], &fdwrite))
{
//可以發送資料
}
}
}
printf("線程end:\n") ;
return ;
}
bool InitWSA()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( , );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != )
{
return false ;
}
if ( LOBYTE( wsaData.wVersion ) != ||
HIBYTE( wsaData.wVersion ) != )
{
WSACleanup();
return false ;
}
return true ;
}
特點:
1、程式可能阻塞在select處一段指定的時間。
2、使用輪循的方式,檢測Socket是否可讀或可寫、效率低下。
點評:雖然實作了異步IO,但是實作方式“扭曲”,效率低下,不推薦使用,尤其是服務端。