轉自http://www.cnblogs.com/yunboy4/archive/2009/08/04/1538915.html
select模型
select(選擇)模型是winsock中常見的I/O模型。之所以稱其為“select模型”,是由于它的
“中心思想”是利用select函數,實現對I/O的管理!最初設計該模型時,主要面向的是某些使用
Unix操作系統的計算機,它們采用的是Berkeley套接字方案。select模型已經集成到Winsock1.1中。
1
.通過調用select函數可以確定一個或多個套接字的狀態,判斷套接字上是否有數據,或
者能否向一個套接字寫入數據。
int select (int nfds, //忽略
fd_set FAR *readfds, //等待可讀性檢查的套接字組的地址
fd_set FAR *writefds, //等待可寫性檢查的套接字組的地址
fd_set FAR *exceptfds, //等待錯誤檢查的套接字組的地址
const struct timeval FAR *timeout); //struct timeval結構體地址,select() 最多等待的時間
//返回值 0--超時,SOCKET_ERROR--失敗
//說明:此函數的作用是刪除fd_set結構體中沒有IO操作的套接字
/*注意:在3個套接字組中至少有一個不為NULL;在非空集合中必須包含一個套接字句柄。
如果timeout設為(0,0),select() 會立即返回,允許應用程序對select操作進行“輪詢”。
如:
fd_set fdread;
FD_ZERO(&fdread);
FD_SET(s, &fdread);
select(0, &fdread, NULL, NULL, NULL);
if(FD_ISSET(s, &fdread))
{
//套接字可讀
}
*/
2.管理套接字的結構體
定義:
typedef struct fd_set {
u_int fd_count; /* how many are SET? */ //元素的個數
SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */
} fd_set;
對struct fd_set結構體操作的宏
FD_SETSIZE 容量,指定fd_array數組大小,默認為64,也可自己修改宏
FD_ZERO(*set) 置空,使數組的元素值都為3435973836,元素個數為0.
FD_SET(s, *set) 添加,向 struct fd_set結構體添加套接字s
FD_ISSET(s, *set) 判斷,判斷s是否為 struct fd_set結構體中的一員
FD_CLR(s, *set) 刪除,從 struct fd_set結構體中刪除成員s
3.用Select模型獲取網絡事件
FD_SET AllSockFd; //裝有所有的套接字
FD_ZERO(&AllSockFd);
AllSockFd = ClientSockFd ;
FD_SET(ListenSock, &AllSockFd);
FD_SET ReadSockFd; //讀集合
FD_SET WriteSockFd; //寫集合
while(1)
{
FD_ZERO(&ReadSockFd);
FD_ZERO(&WriteSockFd);
ReadSockFd = AllSockFd;
WriteSockFd = AllSockFd;
int nRet = select(0, &ReadSockFd, &WriteSockFd, NULL, NULL);
if(SOCKET_ERROR == nRet)
{
continue;
}
//有請求事件發生
if (FD_ISSET(ListenSock, &ReadSockFd))
{
//接受請求
SOCKET ClientSock;
u_short Port;
bool nRe = (*(Pam.pListenSock)).Accept(&ClientSock, 0, &Port);
if(nRe)
{
FD_SET(ClientSock, Pam.pClientSockFd);
//設置套接字發送緩沖區80K
int nBuf = SOCKET_BUFF;
int nBufLen = sizeof(nBuf);
int nRe = setsockopt(ClientSock, SOL_SOCKET, SO_SNDBUF, (char*)&nBuf, nBufLen);
if(SOCKET_ERROR == nRe)
AfxMessageBox("setsockopt error!");
//檢查緩沖區是否設置成功
nRe = getsockopt(ClientSock, SOL_SOCKET, SO_SNDBUF, (char*)&nBuf, &nBufLen);
if(SOCKET_BUFF != nBuf)
AfxMessageBox("檢查緩沖區:setsockopt error!");
else
AfxMessageBox("已連接客戶端!");
}
}
//判斷是否可讀或可寫
for(u_int n = 0;n < ClientSockFd.fd_count;n++)
{
if(FD_ISSET(ClientSockFd.fd_array[n], &ReadSockFd)) //發現可讀
{
//接收數據
//如果失敗 刪除此元素
}
if(FD_ISSET(ClientSockFd.fd_array[n], &WriteSockFd)) //發現可寫
{
//發送緩沖區未滿可以發送
//如果失敗 刪除此元素
}
}
}