/*
*Author: wainiwann
*Source: 博客園 http://www.cnblogs.com/wainiwann
*Remarks: 轉載請說明出處!!!
*/
server端開啟之后始終有兩個線程在處理連接請求,一個是只負責客戶端的請求連接的(這里是只針對TCP協議),當客戶端connect的時候記錄當前客戶端連接存放到數據組中當中,而這個數組聲明為全局成員,其實在線程內處理外部成員的話,也沒必要非要用靜態或者全局成員,今天聽經理說也可以在創建該線程時,把某類的this指針傳遞過去,同樣好像也可以訪問public成員的,具體行不行,還沒試不過真的是不錯的方法。要知道很多在項目很避諱使用全局的東西,甚至有的公司直接不讓使用全局的東西。這里扯的有點遠了。
另外一個同步允許的線程就是對accept記錄的數組進行操作,依次處理各個客戶端請求通信和狀態監控等,當數組內socket成員不為空時記錄當前索引然后在create發送或者獲取線程處理函數並發響應多個客戶端的連接請求通信。
加載套接字庫:
1 BOOL TcpServer::InitSocket() 2 { 3 WORD wVersionRequested; 4 WSADATA wsaData; 5 int err; 6 wVersionRequested = MAKEWORD( 1, 1 ); 7 err = WSAStartup( wVersionRequested, &wsaData ); 8 if ( err != 0 ) 9 { 10 return FALSE; 11 } 12 13 if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) 14 { 15 WSACleanup( ); 16 return FALSE; 17 } 18 19 //創建套接字 20 //SOCKET m_socket=socket(AF_INET,SOCK_STREAM,0); 21 22 23 return TRUE; 24 }
開啟執行Accept線程處理函數:
1 BOOL TcpServer::SatartServer() 2 { 3 //創建線程 4 HANDLE hThread = CreateThread(NULL,0,ThreadProc_Accept,NULL,0,NULL); 5 //關閉該接收線程句柄,釋放引用計數 6 CloseHandle(hThread); 7 8 return TRUE; 9 }
線程處理函數:
1 DWORD WINAPI TcpServer::ThreadProc_Accept(LPVOID lpParameter) 2 { 3 int len = sizeof(SOCKADDR); 4 int err; 5 m_socket=socket(AF_INET,SOCK_STREAM,0); 6 if (m_socket == INVALID_SOCKET) 7 { 8 AfxMessageBox(_T("套接字創建失敗!")); 9 return FALSE; 10 } 11 12 SOCKADDR_IN addrSrv; 13 addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY); 14 addrSrv.sin_family=AF_INET; 15 addrSrv.sin_port=htons(8099); 16 17 err = bind(m_socket,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); //綁定本地端口 18 if (err==SOCKET_ERROR) 19 { 20 closesocket(m_socket); 21 AfxMessageBox(_T("綁定失敗!")); 22 return FALSE; 23 } 24 listen(m_socket,10);//開啟監聽 25 26 //創建線程 27 HANDLE hThread = CreateThread(NULL,0,ThreadProc_Select,NULL,0,NULL); 28 //關閉該接收線程句柄,釋放引用計數 29 CloseHandle(hThread); 30 31 while (TRUE) 32 { 33 m_CliSocketArr[m_ToolConn++] = accept(m_socket,(SOCKADDR*)&addrSrv,&len); 34 } 35 return 0; 36 }
同時在該線程函數內創建處理客戶端數組的線程處理函數:
1 DWORD WINAPI TcpServer::ThreadProc_Select(LPVOID lpParameter) 2 { 3 int recvflag=0; 4 5 fd_set fdread; //讀集fdread 6 int ret; //查看某個套接字的狀態 7 struct timeval tv = {1,0}; //實例化timeval變量 8 9 while (TRUE) 10 { 11 //判斷當前連接數是否為 0 12 if (m_ToolConn == 0) 13 { 14 Sleep(50); 15 continue; 16 } 17 18 FD_ZERO(&fdread); 19 for (int i = 0;i < m_ToolConn;i++) 20 { 21 FD_SET(m_CliSocketArr[i],&fdread); 22 } 23 ret = select(0,&fdread,NULL,NULL,&tv); 24 if (ret == 0) 25 { 26 continue; 27 } 28 for (int i =0;i<m_ToolConn;i++) 29 { 30 if (FD_ISSET(m_CliSocketArr[i],&fdread)) 31 { 32 ret = recv(m_CliSocketArr[i],(char*)&recvflag,sizeof(int)+1,0); 33 if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)) 34 { 35 closesocket(m_CliSocketArr[i]); 36 if (i < m_ToolConn-1) 37 { 38 m_CliSocketArr[i] = m_CliSocketArr[--m_ToolConn]; 39 }else 40 { 41 --m_ToolConn; 42 } 43 44 }else 45 { 46 INDEX * inx = new INDEX; 47 inx->flag = recvflag; 48 inx->index = i; 49 50 //創建線程 51 HANDLE hThread = CreateThread(NULL,0,ThreadProc_Response,(LPVOID)inx,0,NULL); 52 //關閉該接收線程句柄,釋放引用計數 53 CloseHandle(hThread); 54 55 } 56 }//if 57 }//for 58 }//while 59 60 return 0; 61 }
下面就是一次創建線程並發處理客戶端請求線程處理函數:
1 DWORD WINAPI TcpServer::ThreadProc_Response(LPVOID lpParameter) 2 { 3 int ix = ((INDEX*)lpParameter)->index; 4 int flag = ((INDEX*)lpParameter)->flag; 5 6 delete lpParameter; 7 8 if (flag == 1) 9 { 10 //............................. 11 unsigned char sendBuffer[1024] = {'a'}; 12 send(m_CliSocketArr[ix],(char*)sendBuffer,sizeof(sendBuffer)+1,0); 13 } 14 15 return 0; 16 }
這里線程的創建以及處理也可以使用_beginthread()去實現。
另外里面沒有做太多的資源釋放等等,如果真正去用的話,是應該注意的。