1. 引言
多路複用I/O模型(select)是UNIX/LINUX用得的最多的一種I/O模型,在Windows下也
可做爲一種同步I/O使用。本文給出該I/O模型處理多Client的簡單(在主線程中)實現。
2. 關於select
select I/O模型是一種異步I/O模型,在單線程中Linux/WinNT默認支持64個客戶端套
接字。這種I/O模型主要涉及以下幾個函數及宏:
int select(…)、FD_ZERO、FD_SET、FD_ISSET以及FD_SETSIZE。
3. 用select開發一個Server
3.1 只支持單個Client
// 相關初始化處理, 創建監聽套接字
listen(listensock, 5);
clientsock = accept(listensock, NULL, NULL);
for (; ;)
{
FD_ZERO(&readfds);
FD_SET(clientsock,&readfds);
nResult = select(
0, // Windows中這個參數忽略,Linux中在此處爲1
readfds, // 可讀套接字集合
……
)
if (nResult = = SOCKET_ERROR)
return –1;
// 判斷cliensock是否處於待讀狀態
if (FD_ISSET(clientsock, &readfds))
{
// 相關處理
}
}
其實Winsock中的WSAEventSelect模型是與之類似的。
3.2 在單線程中支持63個Client
SOCKET clientsockarray[FD_SETSIZE – 1]; // FD_SETSIZE is 64
// 相關初始化處理, 創建監聽套接字
listen(listensock, 5);
// 初始化套接字數組
InitSock(clientsockarray);
FD_ZERO(&readfds);
FD_SET(listensock, &readfds);
for (; ;)
{
nRet = select(0, &readfds, NULL, NULL, NULL);
// 判斷監聽套接字是否可讀
if (FD_ISSET(listensock, &readfds))
{
clientsock = accept(listensock, NULL, NULL);
// 將客戶套接字放到套接字數組中
if (!InsertSock(clientsockarray, clientsock))
{
printf("客戶端超過了63個,此次連接被拒絕.\n");
closesocket(clientsock);
continue;
}
}
// 逐個處理處於待決狀態的套接字
for (nIndex = 0; nIndex < FD_SETSIZE - 1; nIndex++)
{
if (FD_ISSET(clientsockarray[nIndex], &readfds))
{
nRet = recv(clientsockarray[nIndex], buff, sizeof(buff), 0);
if (nRet = = 0 || nRet = = SOCKET_ERROR)
{
closesocket(clientsockarray[nIndex]);
clientsockarray[nIndex] = INVALID_SOCKET;
continue; // 繼續處理套接字句柄數組中的其它套接字
}
// 將接受到的數據進行處理,此處只將其輸出
printf("%s\n", buff);
}
}
// 初始化套接字集合
FD_ZERO(&readfds);
FD_SET(listensock,&readfds);
// 將所有有效的套接字句柄加入到套接字句柄數組中
for (nIndex = 0; nIndex< FD_SETSIZE - 1; nIndex++)
{
if (clientsockarray[nIndex] != INVALID_SOCKET)
FD_SET(clientsockarray[nIndex],&readfds);
}
}
BOOL InsertSock(SOCKET* pSock, SOCKET sock)
{
for (int nIndex = 0; nIndex < FD_SETSIZE – 1; nIndex++)
{
if (pSock[nIndex] = = INVALID_SOCKET)
{
pSock[nIndex] = sock;
break;
}
}
if (nIndex = = FD_SETSIZE – 1)
return FALSE;
return TRUE;
}
上面只是給簡要的代碼,有的輔助函數也沒有給出。用select支持多Client是比較方便的,在一個線程中可支持63個;可以採用多線程支持更大數量的Client。
4. 效率的討論
4.1 對套接字數組掃描的效率問題
在上面的程序中,存在多處對套接字句柄的掃描處理,這個肯定會影響效率。不知道各位朋友是怎麼處理這個問題的。
4.2 對客戶端實時響應問題
上面的程序處理待決的套接字的時候,是逐個處理的,如果響應某個Client的時間長到一定程度的話,肯定會影響對其它客戶端的響應。我的解決方法是當這個套接字處於可讀的待決狀態的話,產生一個子線程去處理------接收數據和處理數據。這樣主線程繼續自己的工作,其它Client可以得及時的響應;當然當有大量的Client請求時,對線程的控制會成爲一個新問題。
在UNIX/LINUX下做一個支持大量Client的Server的話,本人還是最先選擇select這種I/O模型,這是因爲我還不知道LINUX還有哪些更好的I/O模型。WinNT的話,還有CompletionPort和Overlapped,特別對於有大數據量傳送,同時只有少量的Client時,Overlapped可以發揮相當大的作用。各位朋友請給出使用select的好方法。
來源:http://waiter94.blog.163.com/blog/static/5277376920103413919364/