服務端代碼僅四百餘行,主要是AcceptThread與HelperThread兩個線程函數難以理解,全部代碼如下:
// Server.cpp : Defines the entry point for the console application.<br />//</p><p>#include "stdafx.h"<br />#include <iostream.h><br />#include <afx.h><br />#include <winbase.h><br />#include <winsock2.h><br />#include <process.h><br />#include <list><br />#include "Client.h"</p><p>/*<br /> * 變數的第一個字母為小寫且該字母表明該變數的類型,如 nErrCode.為整形的錯誤碼<br /> * 函數的第一個字母為大寫且表明函數的功能。如void AcceptThread(void).為接受串連的線程<br /> */<br />#pragma comment(lib, "ws2_32.lib")</p><p>using namespace std;<br />typedef list<CClient*> CLIENTLIST;//鏈表</p><p>#define SERVERPORT5556//伺服器TCP連接埠<br />#define SERVER_SETUP_FAIL1//啟動伺服器失敗</p><p>#define TIMEFOR_THREAD_EXIT5000//主線程睡眠時間<br />#define TIMEFOR_THREAD_HELP1500//清理資源線程退出時間<br />#define TIMEFOR_THREAD_SLEEP500//等待用戶端請求線程睡眠時間</p><p>HANDLEhThreadAccept;//接受用戶端連接線程控制代碼<br />HANDLEhThreadHelp;//釋放資源線程控制代碼<br />SOCKETsServer;//監聽通訊端<br />BOOLbServerRunning;//伺服器的工作狀態<br />HANDLEhServerEvent;//關閉伺服器事件對象<br />CLIENTLISTclientlist;//管理串連的鏈表<br />CRITICAL_SECTIONcsClientList;//保護鏈表的臨界區對象</p><p>BOOLInitSever(void);//初始化<br />BOOLStartService(void);//啟動服務<br />voidStopService(void);//停止服務<br />BOOLCreateHelperAndAcceptThread(void);//建立接收用戶端連接線程<br />voidExitServer(void);//伺服器退出</p><p>voidInitMember(void);//初始化全域變數<br />BOOLInitSocket(void);//初始化SOCKET</p><p>voidShowTipMsg(BOOL bFirstInput);//顯示提示資訊<br />voidShowServerStartMsg(BOOL bSuc);//顯示伺服器已經啟動<br />voidShowServerExitMsg(void);//顯示伺服器正在退出</p><p>DWORD__stdcallHelperThread(void *pParam);//釋放資源<br />DWORD__stdcall AcceptThread(void *pParam);//接受用戶端連接線程<br />voidShowConnectNum();//顯示用戶端的串連數目</p><p>int main(int argc, char* argv[])<br />{<br />//初始化伺服器<br />if (!InitSever())<br />{<br />ExitServer();<br />return SERVER_SETUP_FAIL;<br />}</p><p>//啟動服務<br />if (!StartService())<br />{<br />ShowServerStartMsg(FALSE);<br />ExitServer();<br />return SERVER_SETUP_FAIL;<br />}</p><p>//停止服務<br />StopService();</p><p>//伺服器退出<br />ExitServer();</p><p>return 0;<br />}</p><p>/**<br /> * 初始化<br /> */<br />BOOLInitSever(void)<br />{</p><p>InitMember();//初始化全域變數</p><p>//初始化SOCKET<br />if (!InitSocket())<br />return FALSE;</p><p>return TRUE;<br />}</p><p>/**<br /> * 初始化全域變數<br /> */<br />voidInitMember(void)<br />{<br />InitializeCriticalSection(&csClientList);//初始化臨界區<br />hServerEvent = CreateEvent(NULL, TRUE, FALSE, NULL);//手動設定事件,初始化為無資訊號狀態<br />hThreadAccept = NULL;//設定為NULL<br />hThreadHelp = NULL;//設定為NULL<br />sServer = INVALID_SOCKET;//設定為無效的通訊端<br />bServerRunning = FALSE;//伺服器為沒有運行狀態<br />clientlist.clear();//清空鏈表<br />}</p><p>/**<br /> * 初始化SOCKET<br /> */<br />BOOL InitSocket(void)<br />{<br />//傳回值<br />int reVal;</p><p>//初始化Windows Sockets DLL<br />WSADATA wsData;<br />reVal = WSAStartup(MAKEWORD(2,2),&wsData);</p><p>//建立通訊端<br />sServer = socket(AF_INET, SOCK_STREAM, 0);<br />if(INVALID_SOCKET== sServer)<br />return FALSE;</p><p>//設定通訊端非阻塞模式<br />unsigned long ul = 1;<br />reVal = ioctlsocket(sServer, FIONBIO, (unsigned long*)&ul);<br />if (SOCKET_ERROR == reVal)<br />return FALSE;</p><p>//綁定通訊端<br />sockaddr_in serAddr;<br />serAddr.sin_family = AF_INET;<br />serAddr.sin_port = htons(SERVERPORT);<br />serAddr.sin_addr.S_un.S_addr = INADDR_ANY;<br />reVal = bind(sServer, (struct sockaddr*)&serAddr, sizeof(serAddr));<br />if(SOCKET_ERROR == reVal )<br />return FALSE;</p><p>//監聽<br />reVal = listen(sServer, SOMAXCONN);<br />if(SOCKET_ERROR == reVal)<br />return FALSE;</p><p>return TRUE;<br />}</p><p>/**<br /> * 啟動服務<br /> */<br />BOOLStartService(void)<br />{<br />BOOL reVal = TRUE;//傳回值</p><p>ShowTipMsg(TRUE);//提示使用者輸入</p><p>char cInput;//輸入字元<br />do<br />{<br />cin >> cInput;<br />if ('s' == cInput || 'S' == cInput)<br />{<br />if (CreateHelperAndAcceptThread())//建立清理資源和接受用戶端請求的線程<br />{<br />ShowServerStartMsg(TRUE);//建立線程成功資訊<br />}else{<br />reVal = FALSE;<br />}<br />break;//跳出迴圈體</p><p>}else{<br />ShowTipMsg(TRUE);<br />}</p><p>} while(cInput != 's' &&//必須輸入's'或者'S'字元<br />cInput != 'S');</p><p>return reVal;<br />}</p><p>/**<br /> * 停止服務<br /> */<br />voidStopService(void)<br />{<br />BOOL reVal = TRUE;//傳回值</p><p>ShowTipMsg(FALSE);//提示使用者輸入</p><p>char cInput;//輸入的操作字元<br />for (;bServerRunning;)<br />{<br />cin >> cInput;<br />if (cInput == 'E' || cInput == 'e')<br />{<br />if (IDOK == MessageBox(NULL, "Are you sure?", //等待使用者確認退出的訊息框<br />"Server", MB_OKCANCEL))<br />{<br />break;//跳出迴圈體<br />}else{<br />Sleep(TIMEFOR_THREAD_EXIT);//線程睡眠<br />}<br />}else{<br />Sleep(TIMEFOR_THREAD_EXIT);//線程睡眠<br />}<br />}</p><p>bServerRunning = FALSE;//伺服器退出</p><p>ShowServerExitMsg();//顯示伺服器退出資訊</p><p>Sleep(TIMEFOR_THREAD_EXIT);//給其他線程時間退出</p><p>WaitForSingleObject(hServerEvent, INFINITE);//等待清理資源線程發送的事件</p><p>return;<br />}</p><p>/**<br /> * 顯示提示資訊<br /> */<br />voidShowTipMsg(BOOL bFirstInput)<br />{<br />if (bFirstInput)//第一次<br />{<br />cout << endl;<br />cout << endl;<br />cout << "**********************" << endl;<br />cout << "* *" << endl;<br />cout << "* s(S): Start server *" << endl;<br />cout << "* *" << endl;<br />cout << "**********************" << endl;<br />cout << "Please input:" << endl;</p><p>}else{//退出伺服器<br />cout << endl;<br />cout << endl;<br />cout << "**********************" << endl;<br />cout << "* *" << endl;<br />cout << "* e(E): Exit server *" << endl;<br />cout << "* *" << endl;<br />cout << "**********************" << endl;<br />cout << " Please input:" << endl;<br />}<br />}<br />/**<br /> * 釋放資源<br /> */<br />void ExitServer(void)<br />{<br />DeleteCriticalSection(&csClientList);//釋放臨界區對象<br />CloseHandle(hServerEvent);//釋放事件物件控點<br />closesocket(sServer);//關閉SOCKET<br />WSACleanup();//卸載Windows Sockets DLL<br />}</p><p>/**<br /> * 建立釋放資源線程和接收用戶端請求線程<br /> */<br />BOOL CreateHelperAndAcceptThread(void)<br />{</p><p>bServerRunning = TRUE;//設定伺服器為運行狀態</p><p>//建立釋放資源線程<br />unsigned long ulThreadId;<br />hThreadHelp = CreateThread(NULL, 0, HelperThread, NULL, 0, &ulThreadId);<br />if( NULL == hThreadHelp)<br />{<br />bServerRunning = FALSE;<br />return FALSE;<br />}else{<br />CloseHandle(hThreadHelp);<br />}</p><p>//建立接收用戶端請求線程<br />hThreadAccept = CreateThread(NULL, 0, AcceptThread, NULL, 0, &ulThreadId);<br />if( NULL == hThreadAccept)<br />{<br />bServerRunning = FALSE;<br />return FALSE;<br />}else{<br />CloseHandle(hThreadAccept);<br />}</p><p>return TRUE;<br />}</p><p>/**<br /> * 顯示啟動伺服器成功與失敗訊息<br /> */<br />void ShowServerStartMsg(BOOL bSuc)<br />{<br />if (bSuc)<br />{<br />cout << "**********************" << endl;<br />cout << "* *" << endl;<br />cout << "* Server succeeded! *" << endl;<br />cout << "* *" << endl;<br />cout << "**********************" << endl;<br />}else{<br />cout << "**********************" << endl;<br />cout << "* *" << endl;<br />cout << "* Server failed ! *" << endl;<br />cout << "* *" << endl;<br />cout << "**********************" << endl;<br />}</p><p>}</p><p>/**<br /> * 顯示退出伺服器訊息<br /> */<br />void ShowServerExitMsg(void)<br />{</p><p>cout << "**********************" << endl;<br />cout << "* *" << endl;<br />cout << "* Server exit... *" << endl;<br />cout << "* *" << endl;<br />cout << "**********************" << endl;<br />}</p><p>/**<br /> * 接受用戶端串連<br /> */<br />DWORD __stdcall AcceptThread(void* pParam)<br />{<br />SOCKET sAccept;//接受用戶端串連的通訊端<br />sockaddr_in addrClient;//用戶端SOCKET地址</p><p>for (;bServerRunning;)//伺服器的狀態<br />{<br />memset(&addrClient, 0, sizeof(sockaddr_in));//初始化<br />intlenClient = sizeof(sockaddr_in);//地址長度<br />sAccept = accept(sServer, (sockaddr*)&addrClient, &lenClient);//接受客戶請求</p><p>if(INVALID_SOCKET == sAccept )<br />{<br />int nErrCode = WSAGetLastError();<br />if(nErrCode == WSAEWOULDBLOCK)//無法立即完成一個非阻擋性通訊端操作<br />{<br />Sleep(TIMEFOR_THREAD_SLEEP);<br />continue;//繼續等待<br />}else {<br />return 0;//線程退出<br />}</p><p>}<br />else//接受用戶端的請求<br />{<br />CClient *pClient = new CClient(sAccept,addrClient);//建立用戶端對象<br />EnterCriticalSection(&csClientList);//進入在臨界區<br />clientlist.push_back(pClient);//加入鏈表<br />LeaveCriticalSection(&csClientList);//離開臨界區</p><p>pClient->StartRuning();//為接受的用戶端建立接收資料和發送資料線程<br />}<br />}</p><p>return 0;//線程退出<br />}</p><p>/**<br /> * 清理資源<br /> */<br />DWORD __stdcall HelperThread(void* pParam)<br />{<br />for (;bServerRunning;)//伺服器正在運行<br />{<br />EnterCriticalSection(&csClientList);//進入臨界區</p><p>//清理已經斷開的串連用戶端記憶體空間<br />CLIENTLIST::iterator iter = clientlist.begin();<br />for (iter; iter != clientlist.end();)<br />{<br />CClient *pClient = (CClient*)*iter;<br />if (pClient->IsExit())//用戶端線程已經退出<br />{<br />clientlist.erase(iter++);//刪除節點<br />delete pClient;//釋放記憶體<br />pClient = NULL;<br />}else{<br />iter++;//指標下移<br />}<br />}</p><p>LeaveCriticalSection(&csClientList);//離開臨界區</p><p>Sleep(TIMEFOR_THREAD_HELP);<br />}</p><p>//伺服器停止工作<br />if (!bServerRunning)<br />{<br />//斷開每個串連,線程退出<br />EnterCriticalSection(&csClientList);<br />CLIENTLIST::iterator iter = clientlist.begin();<br />for (iter; iter != clientlist.end();)<br />{<br />CClient *pClient = (CClient*)*iter;<br />//如果用戶端的串連還存在,則中斷連線,線程退出<br />if (pClient->IsConning())<br />{<br />pClient->DisConning();<br />}<br />++iter;<br />}<br />//離開臨界區<br />LeaveCriticalSection(&csClientList);</p><p>//給串連用戶端線程時間,使其自動結束<br />Sleep(TIMEFOR_THREAD_SLEEP);</p><p>//進入臨界區<br />EnterCriticalSection(&csClientList);</p><p>//確保為每個用戶端分配的記憶體空間都回收。<br />//如果不加入while這層迴圈,可能存在這樣的情況。當pClient->IsExit()時,該線程還沒有退出。<br />//那麼就需要從鏈表的開始部分重新判斷。<br />while ( 0 != clientlist.size())<br />{<br />iter = clientlist.begin();<br />for (iter; iter != clientlist.end();)<br />{<br />CClient *pClient = (CClient*)*iter;<br />if (pClient->IsExit())//用戶端線程已經退出<br />{<br />clientlist.erase(iter++);//刪除節點<br />delete pClient;//釋放記憶體空間<br />pClient = NULL;<br />}else{<br />iter++;//指標下移<br />}<br />}<br />//給串連用戶端線程時間,使其自動結束<br />Sleep(TIMEFOR_THREAD_SLEEP);<br />}<br />LeaveCriticalSection(&csClientList);//離開臨界區</p><p>}</p><p>clientlist.clear();//清空鏈表</p><p>SetEvent(hServerEvent);//通知主線程退出</p><p>return 0;<br />}