寫這個東西主要是爲了學習libevent的基本用法,以及學習下boost的線程庫。 程序結構比較簡單: 1. 首先是創建一個監聽socke。 2. 將這個監聽的socket綁定到一個event事件上,而後等待有客戶過來鏈接。 3. 若是響應到監聽socket可讀,則accept嘗試鏈接的客戶端。 4. 開啓一個線程來處理全部和這個鏈接過來的客戶端之間的交互。(實際上什麼事情也沒作,就是cout了下每次recv的數據大小) 代碼以下: 1. 首先是程序入口,main函數 main函數主要是註冊了一個監聽使用的socket。另一旦進入了監聽狀態,就很差退出程序,因此一開始就註冊了一個信號響應函數, 專門用來響應程序退出的信號。 //建立監聽socket,而後等待這個socket有客戶來連接 //每一個連接一個線程去處理 int main(int argc, char* argv[]) { //首先處理好kill -2信號 struct sigaction sigact = {0}; sigact.sa_sigaction = On_Exit; if ( -1 == sigaction(2, &sigact, NULL)) { log("創建響應函數失敗"); return 0; } //創建一個非阻塞的socket句柄 int sockfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); if (-1 == sockfd) { log("建立監聽socket失敗", ERROR); return 0; } log("建立監聽socket成功"); //bind sockaddr_in sinAddr; sinAddr.sin_family = AF_INET; sinAddr.sin_port = htons(LISTEN_PORT); sinAddr.sin_addr.s_addr = inet_addr("172.21.169.160"); if( -1 == bind(sockfd, (sockaddr*)&sinAddr, sizeof(sockaddr_in))) { log("監聽本地端口失敗", ERROR); return 0; } //進入listen if (-1 == listen(sockfd, SOMAXCONN)) { log("監聽本地端口失敗", ERROR); return 0; } log("監聽本地端口成功"); //進入accept狀態 log("即將進入監聽狀態"); EnterAcceptWhile(sockfd); close(sockfd); log("監聽線程成功結束了"); return 0; } 2. 進入libevent的消息循環 //基於event的消息響應機制 void EnterAcceptWhile(int nFd) { if (g_pEventLoop == NULL) g_pEventLoop = event_base_new(); //綁定事件 struct event* pListenEvent = event_new(g_pEventLoop, nFd, EV_PERSIST|EV_READ, On_Sock_Accept, NULL); if (pListenEvent == NULL) { log ("創建監聽事件失敗"); return; } //加入監聽事件,持續 event_add(pListenEvent, NULL); //分派消息 int nLoopRst = 0; if (0 == (nLoopRst = event_base_dispatch(g_pEventLoop))) { log("事件循環正常中止了"); } else if (1 == nLoopRst) { log("沒有事件關聯到這個消息循環了"); } else { log("消息循環出現了錯誤", ERROR); } event_free(pListenEvent); event_base_free(g_pEventLoop); g_pEventLoop = NULL; } 3. 一旦響應到有客戶端過來鏈接,就會進入On_Sock_Accept函數。所以這個函數應該儘量的短小。 //響應客戶端鏈接 void On_Sock_Accept(int nFd, short sFlags, void* pArg) { if (!(sFlags&EV_READ)) { log("接受到了一個莫名其妙的消息", WARNING); return; } log("響應到一個客戶端過來鏈接了"); socklen_t sockLen; sockaddr sa; int nAcceptFd = accept(nFd, &sa, &sockLen); //這裏應該啓動一個線程來處理這個請求事務的,而不該該在這裏作大量的複雜操做 SocketThread st(nAcceptFd); boost::thread thread_ST(st); } 4. 在上個函數中,開了一個線程專門處理來自這個客戶端的請求。 類SocketThread的代碼以下: SocketThread::SocketThread(int nFd) { m_nFd = nFd; } void SocketThread::operator() () { if (m_nFd == -1) return; //讀取數據,等到讀取完成以後,輸出出來,最後關閉掉socket鏈接 char* pszTemp = new char[1024]; memset(pszTemp, 0, 1024); int nRecvSize = 0; while( 0 < (nRecvSize = recv(m_nFd, pszTemp, 1024, 0))) { //讀到數據了 std::cout << "thread id: " << boost::this_thread::get_id() << " 接受到了:" << nRecvSize << "字節的數據" << std::endl; } delete[] pszTemp; pszTemp = NULL; if (nRecvSize == 0) { std::cout << "客戶端已經關閉了" << std::endl; } else if (nRecvSize < 0) { std::cout << "接受客戶端數據失敗,請檢查緣由" << std::endl; } close(m_nFd); m_nFd = -1; } 5. 最後是響應信號2, 退出libevent的消息循環 其實這裏存在問題:若是退出消息循環的時候還有不少的工做者線程正在運行,應該要先讓他們把事情作完再退出的。 //響應退出消息 void On_Exit(int nSigId, siginfo_t* pSigInfo, void* pArg) { log("準備結束監聽了", WARNING); if (g_pEventLoop != NULL) { //打破監聽循環 event_base_loopbreak(g_pEventLoop); } }