因爲這個模型是後來才使用的,差一點就忘記了....這個模型是從UNIX裏來的,是比較早的一種模型。上次總結的WSAEventSelect模型是微軟將其進行改進後的版本。來看一下這個函數聲明:
int select(
intnfds, //忽略
fd_set FAR*readfds, //讀操作
fd_set FAR*writefds, //寫操作
fd_set FAR*exceptfds, //錯誤
const struct timeval FAR*timeout //等待的最大時間
);
另外需要看一看幾個操作函數:fd_set ,FD_SET,FD_ZERO,FD_ISSET
1.fd_set:是一個結構體:
typedef struct fd_set {
u_int fd_count; //記錄個數
SOCKET fd_array[FD_SETSIZE]; //套接字數組
} fd_set;
2.FD_SET:是宏操作看一下整的代碼:
#define FD_SET(fd, set) do { /
u_int __i; /
for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count; __i++) { /
if (((fd_set FAR *)(set))->fd_array[__i] == (fd)) { /
break; /
} /
} /
if (__i == ((fd_set FAR *)(set))->fd_count) { /
if (((fd_set FAR *)(set))->fd_count < FD_SETSIZE) { /
((fd_set FAR *)(set))->fd_array[__i] = (fd); /
((fd_set FAR *)(set))->fd_count++; /
} /
} /
} while(0)
這是Winsock2頭文件裏的定義,如果沒有看過MFC仿真,看到這樣的代碼一定很頭痛,函數操作還可以寫成這樣...
其實它完成的功能就是將fd加載到記錄set裏。
3.#define FD_ZERO(set) (((fd_set FAR *)(set))->fd_count=0)
將記錄set清零
4.#define FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)(fd), (fd_set FAR *)(set))
檢測fd是否在記錄set裏
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
好了現在開始例子:
//前三步省略...
//1.Winsock2.2初始化
//2.建立套接字 ServerSock
//3.bind並listen
//4.將建立好的套接字加入記錄數組裏
fd_set fdSocket;
//清零
FD_ZERO(&fdSocket);
//將建立好的套接字加入到記錄
FD_SET(ServerSock,&fdSocket);
//開啓線程處理通信事件
HANDLE hThread = (HANDLE)_beginthreadex(0,0,ThreadProc,this,0,0);
//5.在線程裏建立select模型
CSelectModelDlg* pDlg = (CSelectModelDlg*)pParam;//得到當前對話框的指針
SOCKADDR_IN addrClient;
int nLen = sizeof(SOCKADDR);
while(1)
{
//保存fdSocket
fd_set fdReadSocket = pDlg->fdSocket;
//建立select模型,由於只關心FD_READ則只需處理第二個參數即可
int nEvent = select(0,&fdReadSocket,NULL,NULL,NULL);
if (nEvent > 0)
{
//當有事件到來時,遍歷記錄集
for (int i = 0; i < pDlg->fdSocket.fd_count; i++)
{
//判斷是否在記錄集裏
if (FD_ISSET(pDlg->fdSocket.fd_array[i],(SOCKADDR*)&fdReadSocket))
{
//如果是Server端的套接字,則處理accept
if (pDlg->ServerSocket == pDlg->fdSocket.fd_array[i])
{
if (pDlg->fdSocket.fd_count < FD_SETSIZE)
{
pDlg->ClientSocket = accept(pDlg->ServerSocket,(SOCKADDR*)&addrClient,&nLen);
char* ip = inet_ntoa(addrClient.sin_addr);
int nPort = ntohs(addrClient.sin_port);
//將返回的套接字加入到記錄集裏
FD_SET(pDlg->ClientSocket,&(pDlg->fdSocket)); //把ClientSocket加入到fdSocket
}
}
else
{
//如果不是Server端的套接字,則處理接收信息
pDlg->ClientSocket = pDlg->fdSocket.fd_array[i];
TCHAR szBuf[MAX_PATH] = {0};
int nRecv = recv(pDlg->fdSocket.fd_array[i],(char*)szBuf,MAX_PATH*sizeof(TCHAR),0);
if (nRecv >= 0)
{
szBuf[nRecv] = 0;
}
pDlg->SetDlgItemText(IDC_ED_RECV,szBuf);
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
無論代碼實現還是邏輯方面我認爲select比WSAEvenSelect實現起來要繁瑣一些,建立模型後select需要遍歷判斷,檢驗等操作,所以看個從習慣選擇了。