處理大并發之一 對epoll的理解,epoll客戶端服務端代碼

http://blog.csdn.net/zhuxiaoping54532/article/details/56494313

處理大并發之一

epoll的理解epoll客戶端服務端代碼

序言:

該博客是一系列的博客,首先從最基礎的epoll說起,然后研究libevent源碼及使用方法,最后研究nginx和node.js,關于select,poll這里不做說明,只說明其相對于epoll的不足,其實select和poll我也沒用過,因為我選擇了epoll。

說起epoll,做過大并發的估計都不陌生,之前做了個STB的工具,用的就是epoll處理并發,測試1.5W的并發(非簡單的發消息,包括多個服務端和客戶端的處理)是通過的,epoll的功能強大,讓我有一定的體會,在nginx和node.js中利用的就是libevent,而libevent使用的就是epoll,如果系統不支持epoll,會選擇最佳的方式(select,poll或kqueue中的一種)。

基礎資料:

epoll名詞解釋:

看下百科名片是怎么解釋epoll的吧,(http://baike.baidu.com/view/1385104.htm)epoll是Linux內核為處理大批量句柄而作了改進的poll,是Linux下多路復用IO接口select/poll的增強版本,它能顯著減少程序在大量并發連接中只有少量活躍的情況下的系統CPU利用率。

關于具體的說明可以參考網上資料,我這里羅列下epoll相對于其他多路復用機制(select,poll)的優點吧:

epoll優點:

1.?支持一個進程打開大數目的socket描述符。

2.?IO效率不隨FD數目增加而線性下降,傳統的select/poll每次調用都會線性掃描全部的集合,導致效率呈現線性下降。

3.?使用mmap加速內核與用戶空間的消息傳遞。無論是select,poll還是epoll都需要內核把FD消息通知給用戶空間,如何避免不必要的內存拷貝就很重要,在這點上,epoll是通過內核于用戶空間mmap同一塊內存實現的。

select和poll的缺點:

1.?每次調用時要重復地從用戶態讀入參數。

2.?每次調用時要重復地掃描文件描述符。

3.?每次在調用開始時,要把當前進程放入各個文件描述符的等待隊列。在調用結束后,又把進程從各個等待隊列中刪除。

分析libevent的時候,發現一個博客介紹epoll的不錯,網址是:http://blog.csdn.net/sparkliang/article/details/4770655

epoll用的的數據結構和函數

數據結構?

typedef?union?epoll_data?{?
????????????????void?*ptr;?
????????????????int?fd;?
????????????????__uint32_t?u32;?
????????????????__uint64_t?u64;?
????????}?epoll_data_t;?

????????struct?epoll_event?{?
????????????????__uint32_t?events;??????/*?epoll?events?*/?
????????????????epoll_data_t?data;??????/*?user?data?variable?*/?
????????};?

結構體epoll_event?被用于注冊所感興趣的事件和回傳所發生待處理的事件.?
其中epoll_data?聯合體用來保存觸發事件的某個文件描述符相關的數據.?
例如一個client連接到服務器,服務器通過調用accept函數可以得到于這個client對應的socket文件描述符,可以把這文件描述符賦給epoll_data的fd字段以便后面的讀寫操作在這個文件描述符上進行。epoll_event?結構體的events字段是表示感興趣的事件和被觸發的事件可能的取值為:?
EPOLLIN?:表示對應的文件描述符可以讀;?
EPOLLOUT:表示對應的文件描述符可以寫;?
EPOLLPRI:表示對應的文件描述符有緊急的數據可讀?
EPOLLERR:表示對應的文件描述符發生錯誤;?
EPOLLHUP:表示對應的文件描述符被掛斷;?
EPOLLET:表示對應的文件描述符有事件發生;?

ET和LT模式

LT(level?triggered)是缺省的工作方式,并且同時支持block和no-block?socket.在這種做法中,內核告訴你一個文件描述符是否就緒了,然后你可以對這個就緒的fd進行IO操作。如果你不作任何操作,內核還是會繼續通知你的,所以,這種模式編程出錯誤可能性要小一點。傳統的select/poll都是這種模型的代表。

ET?(edge-triggered)是高速工作方式,只支持no-block?socket。在這種模式下,當描述符從未就緒變為就緒時,內核通過epoll告訴你。然后它會假設你知道文件描述符已經就緒,并且不會再為那個文件描述符發送更多的就緒通知,直到你做了某些操作導致那個文件描述符不再為就緒狀態了(比如,你在發送,接收或者接收請求,或者發送接收的數據少于一定量時導致了一個EWOULDBLOCK?錯誤)。但是請注意,如果一直不對這個fd作IO操作(從而導致它再次變成未就緒),內核不會發送更多的通知(only?once),不過在TCP協議中,ET模式的加速效用仍需要更多的benchmark確認。

ET和LT的區別在于LT事件不會丟棄,而是只要讀buffer里面有數據可以讓用戶讀,則不斷的通知你。而ET則只在事件發生之時通知。可以簡單理解為LT是水平觸發,而ET則為邊緣觸發。
ET模式僅當狀態發生變化的時候才獲得通知,這里所謂的狀態的變化并不包括緩沖區中還有未處理的數據,也就是說,如果要采用ET模式,需要一直read/write直到出錯為止,很多人反映為什么采用ET模式只接收了一部分數據就再也得不到通知了,大多因為這樣;LT模式是只要有數據沒有處理就會一直通知下去的.

函數



詳解:

1.?int?epoll_create(int?size);

創建一個epoll的句柄,size用來告訴內核這個監聽的數目一共有多大。需要注意的是,當創建好epoll句柄后,它就是會占用一個fd值,在linux下如果查看/proc/進程id/fd/,是能夠看到這個fd的,所以在使用完epoll后,必須調用close()關閉,否則可能導致fd被耗盡。

2.?int?epoll_ctl(int?epfd,?int?op,?int?fd,?struct?epoll_event?*event);

epoll的事件注冊函數,它不同與select()是在監聽事件時告訴內核要監聽什么類型的事件,而是在這里先注冊要監聽的事件類型。第一個參數是epoll_create()的返回值,第二個參數表示動作,用三個宏來表示:
EPOLL_CTL_ADD:注冊新的fdepfd中;
EPOLL_CTL_MOD:修改已經注冊的fd的監聽事件;
EPOLL_CTL_DEL:從epfd中刪除一個fd
第三個參數是需要監聽的fd,第四個參數是告訴內核需要監聽什么事

3.?int?epoll_wait(int?epfd,?struct?epoll_event?*events,int?maxevents,?int?timeout);

等待事件的產生,參數events用來從內核得到事件的集合,maxevents告之內核這個events有多大,這個?maxevents的值不能大于創建epoll_create()時的size,參數timeout是超時時間(毫秒,0會立即返回,-1將不確定,也有說法說是永久阻塞)。該函數返回需要處理的事件數目,如返回0表示已超時。

資料總結這么多,其實都是一些理論,關鍵還在于代碼,下面附上我使用epoll開發的服務端和客戶端,該代碼我會上傳到我的資源里,可以免費下載。鏈接:

如果代碼有bug,可以告訴我。

程序demo

首先對服務端和客戶端做下說明:

我想實現的是客戶端和服務端并發的程序,客戶端通過配置并發數,說明有多少個用戶去連接服務端。

客戶端會發送消息:"Client:?i?send?message?Hello?Server!”,其中i表示哪一個客戶端;收到消息:"Recv?Server?Msg?Content:%s\n"。

例如:

發送:Client:?1?send?message?"Hello?Server!"

接收:Recv?Derver?Msg?Content:Hello,?client?fd:?6

服務端收到后給客戶端回復消息:"Hello,?client?fd:?i",其中i表示服務端接收的fd,用戶區別是哪一個客戶端。接收客戶端消息:"Terminal?Received?Msg?Content:%s\n"

例如:

發送:Hello,?client?fd:?6

接收:Terminal?Received?Msg?Content:Client:?1?send?message?"Hello?Server!"

備注:這里在接收到消息后,直接打印出消息,如果需要對消息進行處理(如果消息處理比較占用時間,不能立即返回,可以將該消息放入一個隊列中,然后開啟一個線程從隊列中取消息進行處理,這樣的話不會因為消息處理而阻塞epoll)。libenent好像對這種有2中處理方式,一個就是回調,要求回調函數,不占用太多的時間,基本能立即返回,另一種好像也是一個隊列實現的,這個還需要研究。

服務端代碼說明:

服務端在綁定監聽后,開啟了一個線程,用于負責接收客戶端連接,加入到epoll中,這樣只要accept到客戶端的連接,就將其add?EPOLLIN到epoll中,然后進入循環調用epoll_wait,監聽到讀事件,接收數據,并將事件修改為EPOLLOUT;反之監聽到寫事件,發送數據,并將事件修改為EPOLLIN。

?


cepollserver.cpp

  1. #include?"cepollserver.h"??
  2. ??
  3. using?namespace?std;??
  4. ??
  5. CEpollServer::CEpollServer()??
  6. {??
  7. }??
  8. ??
  9. CEpollServer::~CEpollServer()??
  10. {??
  11. ????close(m_isock);??
  12. }??
  13. ??
  14. bool?CEpollServer::InitServer(const?char*?pIp,?int?iPort)??
  15. {??
  16. ????m_iEpollFd?=?epoll_create(_MAX_SOCKFD_COUNT);??
  17. ??
  18. ????//設置非阻塞模式??
  19. ????int?opts?=?O_NONBLOCK;??
  20. ????if(fcntl(m_iEpollFd,F_SETFL,opts)<0)??
  21. ????{??
  22. ????????printf("設置非阻塞模式失敗!\n");??
  23. ????????return?false;??
  24. ????}??
  25. ??
  26. ????m_isock?=?socket(AF_INET,SOCK_STREAM,0);??
  27. ????if?(?0?>?m_isock?)??
  28. ????{??
  29. ????????printf("socket?error!\n");??
  30. ????????return?false;??
  31.   }??
  32.   ??
  33.   sockaddr_in?listen_addr;??
  34.   ????listen_addr.sin_family=AF_INET;??
  35.   ????listen_addr.sin_port=htons?(?iPort?);??
  36.   ????listen_addr.sin_addr.s_addr=htonl(INADDR_ANY);??
  37.   ????listen_addr.sin_addr.s_addr=inet_addr(pIp);??
  38.   ??
  39.   ????int?ireuseadd_on?=?1;//支持端口復用??
  40.   ????setsockopt(m_isock,?SOL_SOCKET,?SO_REUSEADDR,?&ireuseadd_on,?sizeof(ireuseadd_on)?);??
  41.   ??
  42.   ????if?(?bind?(?m_isock,?(?sockaddr?*?)?&listen_addr,sizeof?(?listen_addr?)?)?!=0?)??
  43.   ????{??
  44.   ????????printf("bind?error\n");??
  45.   ????????return?false;??
  46.   ????}??
  47.   ??
  48.   ????if?(?listen?(?m_isock,?20)?<0?)??
  49.   ????{??
  50.   ????????printf("listen?error!\n");??
  51.   ????????return?false;??
  52.   ????}??
  53.   ????else??
  54.   ????{??
  55.   ????????printf("服務端監聽中...\n");??
  56.   ????}??
  57.   ??
  58.   ????//?監聽線程,此線程負責接收客戶端連接,加入到epoll中??
  59.   ????if?(?pthread_create(?&m_ListenThreadId,?0,?(?void?*?(?*?)?(?void?*?)?)?ListenThread,?this?)?!=?0?)??
  60.   ????{??
  61.   ????????printf("Server?監聽線程創建失敗!!!");??
  62.   ????????return?false;??
  63.   ????}??
  64.   }??
  65.   //?監聽線程??
  66.   void?CEpollServer::ListenThread(?void*?lpVoid?)??
  67.   {??
  68.   ????CEpollServer?*pTerminalServer?=?(CEpollServer*)lpVoid;??
  69.   ????sockaddr_in?remote_addr;??
  70.   ????int?len?=?sizeof?(remote_addr);??
  71.   ????while?(?true?)??
  72.   ????{??
  73.   ????????int?client_socket?=?accept?(pTerminalServer->m_isock,?(?sockaddr?*?)?&remote_addr,(socklen_t*)&len?);??
  74.   ????????if?(?client_socket?<?0?)??
  75.   ????????{??
  76.   ????????????printf("Server?Accept失敗!,?client_socket:?%d\n",?client_socket);??
  77.   ????????????continue;??
  78.   ????????}??
  79.   ????????else??
  80.   ????????{??
  81.   ????????????struct?epoll_event????ev;??
  82.   ????????????ev.events?=?EPOLLIN?|?EPOLLERR?|?EPOLLHUP;??
  83.   ????????????ev.data.fd?=?client_socket;?????//記錄socket句柄??
  84.   ????????????epoll_ctl(pTerminalServer->m_iEpollFd,?EPOLL_CTL_ADD,?client_socket,?&ev);??
  85.   ????????}??
  86.   ????}??
  87.   }??
  88.   ??
  89.   void?CEpollServer::Run()??
  90.   {??
  91.   ????while?(?true?)??
  92.   ????{??
  93.   ????????struct?epoll_event????events[_MAX_SOCKFD_COUNT];??
  94.   ????????int?nfds?=?epoll_wait(?m_iEpollFd,?events,??_MAX_SOCKFD_COUNT,?-1?);??
  95.   ????????for?(int?i?=?0;?i?<?nfds;?i++)??
  96.   ????????{??
  97.   ????????????int?client_socket?=?events[i].data.fd;??
  98.   ????????????char?buffer[1024];//每次收發的字節數小于1024字節??
  99.   ????????????memset(buffer,?0,?1024);??
  100.   ????????????if?(events[i].events?&?EPOLLIN)//監聽到讀事件,接收數據??
  101.   ????????????{??
  102.   ????????????????int?rev_size?=?recv(events[i].data.fd,buffer,?1024,0);??
  103.   ????????????????if(?rev_size?<=?0?)??
  104.   ????????????????{??
  105.   ????????????????????cout?<<?"recv?error:?recv?size:?"?<<?rev_size?<<?endl;??
  106.   ????????????????????struct?epoll_event?event_del;??
  107.   ????????????????????event_del.data.fd?=?events[i].data.fd;??
  108.   ????????????????????event_del.events?=?0;??
  109.   ????????????????????epoll_ctl(m_iEpollFd,?EPOLL_CTL_DEL,?event_del.data.fd,?&event_del);??
  110.   ????????????????}??
  111.   ????????????????else??
  112.   ????????????????{??
  113.   ????????????????????printf("Terminal?Received?Msg?Content:%s\n",buffer);??
  114.   ????????????????????struct?epoll_event????ev;??
  115.   ????????????????????ev.events?=?EPOLLOUT?|?EPOLLERR?|?EPOLLHUP;??
  116.   ????????????????????ev.data.fd?=?client_socket;?????//記錄socket句柄??
  117.   ????????????????????epoll_ctl(m_iEpollFd,?EPOLL_CTL_MOD,?client_socket,?&ev);??
  118.   ????????????????}??
  119.   ????????????}??
  120.   else?if(events[i].events?&?EPOLLOUT)//監聽到寫事件,發送數據??
  121.   ????????????{??
  122.   ????????????????char?sendbuff[1024];??
  123.   ????????????????sprintf(sendbuff,?"Hello,?client?fd:?%d\n",?client_socket);??
  124.   ????????????????int?sendsize?=?send(client_socket,?sendbuff,?strlen(sendbuff)+1,?MSG_NOSIGNAL);??
  125.   ????????????????if(sendsize?<=?0)??
  126.   ????????????????{??
  127.   ????????????????????struct?epoll_event?event_del;??
  128.   ????????????????????event_del.data.fd?=?events[i].data.fd;??
  129.   ????????????????????event_del.events?=?0;??
  130.   ????????????????????epoll_ctl(m_iEpollFd,?EPOLL_CTL_DEL,?event_del.data.fd,?&event_del);??
  131.   ????????????????}??
  132.   ????????????????else??
  133.   ????????????????{??
  134.   ????????????????????printf("Server?reply?msg?ok!?buffer:?%s\n",?sendbuff);??
  135.   ????????????????????struct?epoll_event????ev;??
  136.   ????????????????????ev.events?=?EPOLLIN?|?EPOLLERR?|?EPOLLHUP;??
  137.   ????????????????????ev.data.fd?=?client_socket;?????//記錄socket句柄??
  138.   ????????????????????epoll_ctl(m_iEpollFd,?EPOLL_CTL_MOD,?client_socket,?&ev);??
  139.   ????????????????}??
  140.   ????????????}??
  141.   ????????????else??
  142.   ????????????{??
  143.   ????????????????cout?<<?"EPOLL?ERROR\n"?<<endl;??
  144.   ????????????????epoll_ctl(m_iEpollFd,?EPOLL_CTL_DEL,?events[i].data.fd,?&events[i]);??
  145.   ????????????}??
  146.   ????????}??
  147.   ????}??
  148.   }??


main.cpp


客戶端代碼:

說明:測試是兩個并發進行測試,每一個客戶端都是一個長連接。代碼中在連接服務器(ConnectToServer)時將用戶IDsocketid關聯起來。用戶IDsocketid是一一對應的關系。

cepollclient.h

  1. #ifndef?_DEFINE_EPOLLCLIENT_H_??
  2. #define?_DEFINE_EPOLLCLIENT_H_??
  3. #define?_MAX_SOCKFD_COUNT?65535??
  4. ??
  5. #include<iostream>??
  6. #include?<sys/epoll.h>??
  7. #include?<sys/socket.h>??
  8. #include?<netinet/in.h>??
  9. #include?<fcntl.h>??
  10. #include?<arpa/inet.h>??
  11. #include?<errno.h>??
  12. #include?<sys/ioctl.h>??
  13. #include?<sys/time.h>??
  14. #include?<string>??
  15. ??
  16. using?namespace?std;??
  17. ??
  18. /**?
  19. ?*?@brief?用戶狀態?
  20. ?*/??
  21. typedef?enum?_EPOLL_USER_STATUS_EM??
  22. {??
  23. ????????FREE?=?0,??
  24. ????????CONNECT_OK?=?1,//連接成功??
  25. ????????SEND_OK?=?2,//發送成功??
  26. ????????RECV_OK?=?3,//接收成功??
  27. }EPOLL_USER_STATUS_EM;??
  28. ??
  29. /*@brief?
  30. ?*@CEpollClient?class?用戶狀態結構體?
  31. ?*/??
  32. struct?UserStatus??
  33. {??
  34. ????????EPOLL_USER_STATUS_EM?iUserStatus;//用戶狀態??
  35. ????????int?iSockFd;//用戶狀態關聯的socketfd??
  36. ????????char?cSendbuff[1024];//發送的數據內容??
  37. ????????int?iBuffLen;//發送數據內容的長度??
  38. ????????unsigned?int?uEpollEvents;//Epoll?events??
  39. };??
  40. ??
  41. class?CEpollClient??
  42. {??
  43. ????????public:??
  44. ??
  45. ????????????????/**?
  46. ?????????????????*?@brief?
  47. ?????????????????*?函數名:CEpollClient?
  48. ?????????????????*?描述:構造函數?
  49. ?????????????????*?@param?[in]?iUserCount??
  50. ?????????????????*?@param?[in]?pIP?IP地址?
  51. ?????????????????*?@param?[in]?iPort?端口號?
  52. ?????????????????*?@return?無返回?
  53. ?????????????????*/??
  54. ????????????????CEpollClient(int?iUserCount,?const?char*?pIP,?int?iPort);??
  55. ??
  56. /**?
  57. ?????????????????*?@brief?
  58. ?????????????????*?函數名:CEpollClient?
  59. ?????????????????*?描述:析構函數?
  60. ?????????????????*?@return?無返回?
  61. ?????????????????*/??
  62. ????????????????~CEpollClient();??
  63. ??
  64. ????????????????/**?
  65. ?????????????????*?@brief?
  66. ?????????????????*?函數名:RunFun?
  67. ?????????????????*?描述:對外提供的接口,運行epoll類?
  68. ?????????????????*?@return?無返回值?
  69. ?????????????????*/??
  70. ????????????????int?RunFun();??
  71. ??
  72. ????????private:??
  73. ??
  74. ????????????????/**?
  75. ?????????????????*?@brief?
  76. ?????????????????*?函數名:ConnectToServer?
  77. ?????????????????*?描述:連接到服務器?
  78. ?????????????????*?@param?[in]?iUserId?用戶ID?
  79. ?????????????????*?@param?[in]?pServerIp?連接的服務器IP?
  80. ?????????????????*?@param?[in]?uServerPort?連接的服務器端口號?
  81. ?????????????????*?@return?成功返回socketfd,失敗返回的socketfd為-1?
  82. ?????????????????*/??
  83. ????????????????int?ConnectToServer(int?iUserId,const?char?*pServerIp,unsigned?short?uServerPort);??
  84. ??
  85. /**?
  86. ?????????????????*?@brief?
  87. ?????????????????*?函數名:SendToServerData?
  88. ?????????????????*?描述:給服務器發送用戶(iUserId)的數據?
  89. ?????????????????*?@param?[in]?iUserId?用戶ID?
  90. ?????????????????*?@return?成功返回發送數據長度?
  91. ?????????????????*/??
  92. ????????????????int?SendToServerData(int?iUserId);??
  93. ??
  94. ????????????????/**?
  95. ?????????????????*?@brief?
  96. ?????????????????*?函數名:RecvFromServer?
  97. ?????????????????*?描述:接收用戶回復消息?
  98. ?????????????????*?@param?[in]?iUserId?用戶ID?
  99. ?????????????????*?@param?[in]?pRecvBuff?接收的數據內容?
  100. ?????????????????*?@param?[in]?iBuffLen?接收的數據長度?
  101. ?????????????????*?@return?成功返回接收的數據長度,失敗返回長度為-1?
  102. ?????????????????*/??
  103. ????????????????int?RecvFromServer(int?iUserid,char?*pRecvBuff,int?iBuffLen);??
  104. ??
  105. ????????????????/**?
  106. ?????????????????*?@brief?
  107. ?????????????????*?函數名:CloseUser?
  108. ?????????????????*?描述:關閉用戶?
  109. ?????????????????*?@param?[in]?iUserId?用戶ID?
  110. ?????????????????*?@return?成功返回true?
  111. ?????????????????*/??
  112. ????????????????bool?CloseUser(int?iUserId);??
  113. ??
  114. /**?
  115. ?????????????????*?@brief?
  116. ?????????????????*?函數名:DelEpoll?
  117. ?????????????????*?描述:刪除epoll事件?
  118. ?????????????????*?@param?[in]?iSockFd?socket?FD?
  119. ?????????????????*?@return?成功返回true?
  120. ?????????????????*/??
  121. ????????????????bool?DelEpoll(int?iSockFd);??
  122. ????????private:??
  123. ??
  124. ????????????????int????m_iUserCount;//用戶數量;??
  125. ????????????????struct?UserStatus?*m_pAllUserStatus;//用戶狀態數組??
  126. ????????????????int????m_iEpollFd;//需要創建epollfd??
  127. ????????????????int????m_iSockFd_UserId[_MAX_SOCKFD_COUNT];//將用戶ID和socketid關聯起來??
  128. ????????????????int????m_iPort;//端口號??
  129. ????????????????char???m_ip[100];//IP地址??
  130. };??
  131. ??
  132. #endif??


cepollclient.cpp

  1. #include?"cepollclient.h"??
  2. ??
  3. CEpollClient::CEpollClient(int?iUserCount,?const?char*?pIP,?int?iPort)??
  4. {??
  5. ????strcpy(m_ip,?pIP);??
  6. ????m_iPort?=?iPort;??
  7. ????m_iUserCount?=?iUserCount;??
  8. ????m_iEpollFd?=?epoll_create(_MAX_SOCKFD_COUNT);??
  9. ????m_pAllUserStatus?=?(struct?UserStatus*)malloc(iUserCount*sizeof(struct?UserStatus));??
  10. ????for(int?iuserid=0;?iuserid<iUserCount?;?iuserid++)??
  11. ????{??
  12. ????????m_pAllUserStatus[iuserid].iUserStatus?=?FREE;??
  13. ????????sprintf(m_pAllUserStatus[iuserid].cSendbuff,?"Client:?%d?send?message?\"Hello?Server!\"\r\n",?iuserid);??
  14. ????????m_pAllUserStatus[iuserid].iBuffLen?=?strlen(m_pAllUserStatus[iuserid].cSendbuff)?+?1;??
  15. ????????m_pAllUserStatus[iuserid].iSockFd?=?-1;??
  16. ????}??
  17. ????memset(m_iSockFd_UserId,?0xFF,?sizeof(m_iSockFd_UserId));??
  18. }??
  19. ??
  20. CEpollClient::~CEpollClient()??
  21. {??
  22. ????free(m_pAllUserStatus);??
  23. }??
  24. int?CEpollClient::ConnectToServer(int?iUserId,const?char?*pServerIp,unsigned?short?uServerPort)??
  25. {??
  26. ????if(?(m_pAllUserStatus[iUserId].iSockFd?=?socket(AF_INET,SOCK_STREAM,0)?)?<?0?)??
  27. ????{??
  28. ????????cout?<<"[CEpollClient?error]:?init?socket?fail,?reason?is:"<<strerror(errno)<<",errno?is:"<<errno<<endl;??
  29. ????????m_pAllUserStatus[iUserId].iSockFd?=?-1;??
  30. ????????return??m_pAllUserStatus[iUserId].iSockFd;??
  31. ????}??
  32. ??
  33. ????struct?sockaddr_in?addr;??
  34. ????bzero(&addr,?sizeof(addr));??
  35. ????addr.sin_family?=?AF_INET;??
  36. ????addr.sin_port?=?htons(uServerPort);??
  37. ????addr.sin_addr.s_addr?=?inet_addr(pServerIp);??
  38. ??
  39. ????int?ireuseadd_on?=?1;//支持端口復用??
  40. ????setsockopt(m_pAllUserStatus[iUserId].iSockFd,?SOL_SOCKET,?SO_REUSEADDR,?&ireuseadd_on,?sizeof(ireuseadd_on));??
  41. ??
  42. ????unsigned?long?ul?=?1;??
  43. ????ioctl(m_pAllUserStatus[iUserId].iSockFd,?FIONBIO,?&ul);?//設置為非阻塞模式??
  44. ??
  45. ????connect(m_pAllUserStatus[iUserId].iSockFd,?(const?sockaddr*)&addr,?sizeof(addr));??
  46. ????m_pAllUserStatus[iUserId].iUserStatus?=?CONNECT_OK;??
  47. ????m_pAllUserStatus[iUserId].iSockFd?=?m_pAllUserStatus[iUserId].iSockFd;??
  48. ??
  49. ????return?m_pAllUserStatus[iUserId].iSockFd;??
  50. }??
  51. int?CEpollClient::SendToServerData(int?iUserId)??
  52. {??
  53. ????sleep(1);//此處控制發送頻率,避免狂打日志,正常使用中需要去掉??
  54. ????int?isendsize?=?-1;??
  55. ????if(?CONNECT_OK?==?m_pAllUserStatus[iUserId].iUserStatus?||?RECV_OK?==?m_pAllUserStatus[iUserId].iUserStatus)??
  56. ????{??
  57. ????????isendsize?=?send(m_pAllUserStatus[iUserId].iSockFd,?m_pAllUserStatus[iUserId].cSendbuff,?m_pAllUserStatus[iUserId??
  58. ].iBuffLen,?MSG_NOSIGNAL);??
  59. ????????if(isendsize?<?0)??
  60. ????????{??
  61. ????????????cout?<<"[CEpollClient?error]:?SendToServerData,?send?fail,?reason?is:"<<strerror(errno)<<",errno?is:"<<errno<??
  62. <endl;??
  63. ????????}??
  64. ????????else??
  65. ????????{??
  66. ????????????printf("[CEpollClient?info]:?iUserId:?%d?Send?Msg?Content:%s\n",?iUserId,?m_pAllUserStatus[iUserId].cSendbuff??
  67. );??
  68. ????????????m_pAllUserStatus[iUserId].iUserStatus?=?SEND_OK;??
  69. ????????}??
  70. ????}??
  71. ????return?isendsize;??
  72. }??
  73. int?CEpollClient::RecvFromServer(int?iUserId,char?*pRecvBuff,int?iBuffLen)??
  74. {??
  75. ????int?irecvsize?=?-1;??
  76. ????if(SEND_OK?==?m_pAllUserStatus[iUserId].iUserStatus)??
  77. ????{??
  78. ????????irecvsize?=?recv(m_pAllUserStatus[iUserId].iSockFd,?pRecvBuff,?iBuffLen,?0);??
  79. ????????if(0?>?irecvsize)??
  80. ????????{??
  81. ????????????cout?<<"[CEpollClient?error]:?iUserId:?"?<<?iUserId?<<?"RecvFromServer,?recv?fail,?reason?is:"<<strerror(errn??
  82. o)<<",errno?is:"<<errno<<endl;??
  83. ????????}??
  84. ????????else?if(0?==?irecvsize)??
  85. ????????{??
  86. ????????????cout?<<"[warning:]?iUserId:?"<<?iUserId?<<?"RecvFromServer,?STB收到數據為0,表示對方斷開連接,irecvsize:"<<ire??
  87. cvsize<<",iSockFd:"<<?m_pAllUserStatus[iUserId].iSockFd?<<?endl;??
  88. ????????}??
  89. ????????else??
  90. ????????{??
  91. ????????????printf("Recv?Server?Msg?Content:%s\n",?pRecvBuff);??
  92. ????????????m_pAllUserStatus[iUserId].iUserStatus?=?RECV_OK;??
  93. ????????}??
  94. ????}??
  95. ????return?irecvsize;??
  96. }??
  97. ??
  98. bool?CEpollClient::CloseUser(int?iUserId)??
  99. {??
  100. ????close(m_pAllUserStatus[iUserId].iSockFd);??
  101. ????m_pAllUserStatus[iUserId].iUserStatus?=?FREE;??
  102. ????m_pAllUserStatus[iUserId].iSockFd?=?-1;??
  103. ????return?true;??
  104. }??
  105. ??????
  106. int?CEpollClient::RunFun()??
  107. {??
  108. ????int?isocketfd?=?-1;??
  109. ????for(int?iuserid=0;?iuserid<m_iUserCount;?iuserid++)??
  110. ????{??
  111. ????????struct?epoll_event?event;??
  112. ????????isocketfd?=?ConnectToServer(iuserid,?m_ip,?m_iPort);??
  113. ????????if(isocketfd?<?0)??
  114. ????????????cout?<<"[CEpollClient?error]:?RunFun,?connect?fail"?<<endl;??
  115. ????????m_iSockFd_UserId[isocketfd]?=?iuserid;//將用戶ID和socketid關聯起來??
  116. ??
  117. ????????event.data.fd?=?isocketfd;??
  118. ????????event.events?=?EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP;??
  119. ??
  120. ????????m_pAllUserStatus[iuserid].uEpollEvents?=?event.events;??
  121. ????????epoll_ctl(m_iEpollFd,?EPOLL_CTL_ADD,?event.data.fd,?&event);??
  122.   }??
  123.   while(1)??
  124.   ????{??
  125.   ????????struct?epoll_event?events[_MAX_SOCKFD_COUNT];??
  126.   ????????char?buffer[1024];??
  127.   ????????memset(buffer,0,1024);??
  128.   ????????int?nfds?=?epoll_wait(m_iEpollFd,?events,?_MAX_SOCKFD_COUNT,?100?);//等待epoll事件的產生??
  129.   ????????for?(int?ifd=0;?ifd<nfds;?ifd++)//處理所發生的所有事件??
  130.   ????????{??
  131.   ????????????struct?epoll_event?event_nfds;??
  132.   ????????????int?iclientsockfd?=?events[ifd].data.fd;??
  133.   ????????????cout?<<?"events[ifd].data.fd:?"?<<?events[ifd].data.fd?<<?endl;??
  134.   ????????????int?iuserid?=?m_iSockFd_UserId[iclientsockfd];//根據socketfd得到用戶ID??
  135.   ????????????if(?events[ifd].events?&?EPOLLOUT?)??
  136.   ????????????{??
  137.   ????????????????int?iret?=?SendToServerData(iuserid);??
  138.   ????????????????if(?0?<?iret?)??
  139.   ????????????????{??
  140.   ????????????????????event_nfds.events?=?EPOLLIN|EPOLLERR|EPOLLHUP;??
  141.   ????????????????????event_nfds.data.fd?=?iclientsockfd;??
  142.   ????????????????????epoll_ctl(m_iEpollFd,?EPOLL_CTL_MOD,?event_nfds.data.fd,?&event_nfds);??
  143.   ????????????????}??
  144.   ????????????????else??
  145.   ????????????????{??
  146.   ????????????????????cout?<<"[CEpollClient?error:]?EpollWait,?SendToServerData?fail,?send?iret:"<<iret<<",iuserid:"<<iuser??
  147.   id<<",fd:"<<events[ifd].data.fd<<endl;??
  148.   ????????????????????DelEpoll(events[ifd].data.fd);??
  149.   ????????????????????CloseUser(iuserid);??
  150.   ????????????????}??
  151.   ????????????}??
  152.   else?if(?events[ifd].events?&?EPOLLIN?)//監聽到讀事件,接收數據??
  153.   ????????????{??
  154.   ????????????????int?ilen?=?RecvFromServer(iuserid,?buffer,?1024);??
  155.   ????????????????if(0?>?ilen)??
  156.   ????????????????{??
  157.   ????????????????????cout?<<"[CEpollClient?error]:?RunFun,?recv?fail,?reason?is:"<<strerror(errno)<<",errno?is:"<<errno<<e??
  158.   ndl;??
  159.   ????????????????????DelEpoll(events[ifd].data.fd);??
  160.   ????????????????????CloseUser(iuserid);??
  161.   ????????????????}??
  162.   ????????????????else?if(0?==?ilen)??
  163.   ????????????????{??
  164.   ????????????????????cout?<<"[CEpollClient?warning:]?server?disconnect,ilen:"<<ilen<<",iuserid:"<<iuserid<<",fd:"<<events[??
  165.   ifd].data.fd<<endl;??
  166.   ????????????????????DelEpoll(events[ifd].data.fd);??
  167.   ????????????????????CloseUser(iuserid);??
  168.   ????????????????}??
  169.   ????????????????else??
  170.   ????????????????{??
  171.   ????????????????????m_iSockFd_UserId[iclientsockfd]?=?iuserid;//將socketfd和用戶ID關聯起來??
  172.   ????????????????????event_nfds.data.fd?=?iclientsockfd;??
  173.   ????????????????????event_nfds.events?=?EPOLLOUT|EPOLLERR|EPOLLHUP;??
  174.   ????????????????????epoll_ctl(m_iEpollFd,?EPOLL_CTL_MOD,?event_nfds.data.fd,?&event_nfds);??
  175.   ????????????????}??
  176.   ????????????}??
  177.   ????????????else??
  178.   ????????????{??
  179.   ????????????????cout?<<"[CEpollClient?error:]?other?epoll?error"<<endl;??
  180.   ????????????????DelEpoll(events[ifd].data.fd);??
  181.   ????????????????CloseUser(iuserid);??
  182.   ????????????}??
  183.   ????????}??
  184.   }??
  185.   }??
  186.   ??
  187.   bool?CEpollClient::DelEpoll(int?iSockFd)??
  188.   {??
  189.   ????bool?bret?=?false;??
  190.   ????struct?epoll_event?event_del;??
  191.   ????if(0?<?iSockFd)??
  192.   ????{??
  193.   ????????event_del.data.fd?=?iSockFd;??
  194.   ????????event_del.events?=?0;??
  195.   ????????if(?0?==?epoll_ctl(m_iEpollFd,?EPOLL_CTL_DEL,?event_del.data.fd,?&event_del)?)??
  196.   ????????{??
  197.   ????????????bret?=?true;??
  198.   ????????}??
  199.   ????????else??
  200.   ????????{??
  201.   ????????????cout?<<"[SimulateStb?error:]?DelEpoll,epoll_ctl?error,iSockFd:"<<iSockFd<<endl;??
  202.   ????????}??
  203.   ????????m_iSockFd_UserId[iSockFd]?=?-1;??
  204.   ????}??
  205.   ????else??
  206.   ????{??
  207.   ????????bret?=?true;??
  208.   ??
  209.   ????}??
  210.   ????return?bret;??
  211.   }??


main.cpp


運行結果:

客戶端:

服務端:

如是轉載,請指明原出處:http://blog.csdn.net/feitianxuxue,謝謝合作!



本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/news/383767.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383767.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383767.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

epoll詳解

http://blog.csdn.net/majianfei1023/article/details/45772269歡迎轉載&#xff0c;轉載請注明原文地址&#xff1a;http://blog.csdn.net/majianfei1023/article/details/45772269一.基本概念&#xff1a;1.epoll是什么&#xff1a;epoll是Linux內核為處理大批量文件描述符而…

數據分割-并查集+set

小w來到百度之星的賽場上&#xff0c;準備開始實現一個程序自動分析系統。 這個程序接受一些形如xixj 或 xi≠xj 的相等/不等約束條件作為輸入&#xff0c;判定是否可以通過給每個 w 賦適當的值&#xff0c;來滿足這些條件。 輸入包含多組數據。 然而粗心的小w不幸地把每組數據…

linux c++線程池的實現

http://blog.csdn.net/zhoubl668/article/details/8927090?t1473221020107 線程池的原理大家都知道&#xff0c;直接上代碼了^_^ Thread.h [cpp] view plaincopy #ifndef __THREAD_H #define __THREAD_H #include <vector> #include <string> #inc…

樹啟發式合并入門

所謂啟發式合并&#xff0c;就是一種符合直覺的合并方法&#xff1a;將小的子樹合并在大的子樹上。 這些問題一般是相似的問題背景&#xff1a;都是樹上的計數問題&#xff0c;都不能直接從上往下進行暴力&#xff0c;都需要從下往上計數時對子樹信息進行運算從而得到父親節點的…

鏈棧基本操作

http://blog.csdn.net/jwentao01/article/details/46765517###;棧基本概念&#xff1a; 棧&#xff08;stack&#xff09;是限定在表尾進行插入和刪除操作的線性表&#xff08;或單鏈表&#xff09;。 //只能在一段進行插入和刪除&#xff0c;因此不存在&#xff0c;在中間進行…

Linux網絡編程---I/O復用模型之select

https://blog.csdn.net/men_wen/article/details/53456435Linux網絡編程—I/O復用模型之select 1. IO復用模型 IO復用能夠預先告知內核&#xff0c;一旦發現進程指定的一個或者多個IO條件就緒&#xff0c;它就通知進程。IO復用阻塞在select或poll系統調用上&#xff0c;而不是阻…

UVa12633-Super Rooks on Chessboard-容斥+FFT

題目大意就是給你一個R*C的棋盤&#xff0c;上面有超級兵&#xff0c;這種超級兵會攻擊 同一行、同一列、同一主對角線的所有元素&#xff0c;現在給你N個超級兵的坐標&#xff0c;需要你求出有多少方塊是不能被攻擊到的(R,C,N<50000) 遇到這種計數問題就要聯想到容斥&#…

Linux網絡編程---I/O復用模型之poll

https://blog.csdn.net/men_wen/article/details/53456474Linux網絡編程—I/O復用模型之poll 1.函數poll poll系統調用和select類似&#xff0c;也是在指定時間內輪詢一定數量的文件描述符&#xff0c;以測試其中是否有就緒者。 #include <poll.h>int poll(struct pollfd…

FFT模板

整理了一下&#xff0c;自己寫了一下模板 const double PIacos(-1.0); struct complex {double r,i;complex(double _r0,double _i0):r(_r),i(_i){}complex operator (const complex &b) {return complex(rb.r,ib.i);}complex operator -(const complex &b) {return c…

Linux網絡編程---I/O復用模型之epoll

https://blog.csdn.net/men_wen/article/details/53456474 Linux網絡編程—I/O復用模型之epoll 1. epoll模型簡介 epoll是Linux多路服用IO接口select/poll的加強版&#xff0c;e對應的英文單詞就是enhancement&#xff0c;中文翻譯為增強&#xff0c;加強&#xff0c;提高&…

POJ 1741tree-點分治入門

學習了一下點分治&#xff0c;如果理解有誤還請不吝賜教。 為了快速求得樹上任意兩點之間距離滿足某種關系的點對數&#xff0c;我們需要用到這種算法。 點分治是樹上的一種分治算法&#xff0c;依靠樹和子樹之間的關系進行分治從而降低復雜度。 和其他樹上的算法有一些區別…

基于單鏈表的生產者消費者問題

『生產者與消費者問題分析』「原理」生產者生產產品&#xff0c;消費者消費產品。產品如果被消費者消費完了&#xff0c;同時生產者又沒有生產出產品&#xff0c;消費者 就必須等待。同樣的&#xff0c;如果生產者生產了產品&#xff0c;而消費者沒有去消費&#x…

C++智能指針(一)智能指針的簡單介紹

https://blog.csdn.net/nou_camp/article/details/70176949C智能指針 在正式了解智能指針前先看一下下面的一段代碼 #include<iostream> using namespace std; class A { public:A():_ptr(NULL), _a(0){}~A(){} public:int* _ptr;int _a; };void test() {A a;int *p1 ne…

聰聰可可-點分治

聰聰和可可是兄弟倆&#xff0c;他們倆經常為了一些瑣事打起來&#xff0c;例如家中只剩下最后一根冰棍而兩人都想吃、兩個人都想玩兒電腦&#xff08;可是他們家只有一臺電腦&#xff09;……遇到這種問題&#xff0c;一般情況下石頭剪刀布就好了&#xff0c;可是他們已經玩兒…

C++智能指針(二)模擬實現三種智能指針

https://blog.csdn.net/nou_camp/article/details/70186721在上一篇博客中提到了Auto_ptr(C智能指針&#xff08;一&#xff09;)&#xff0c;下面進行模擬實現Auto_ptr 采用類模板實現 #include<iostream> using namespace std; template<class T> class Autoptr …

Prime Distance On Tree-樹分治+FFT

題目描述 Problem description. You are given a tree. If we select 2 distinct nodes uniformly at random, what’s the probability that the distance between these 2 nodes is a prime number? Input The first line contains a number N: the number of nodes in this…

C++智能指針(三)總結

https://blog.csdn.net/nou_camp/article/details/70195795 在上一篇博客中&#xff08;C智能指針&#xff08;二&#xff09;&#xff09;模擬實現了三種智能指針。 其中最好的就是shared_ptr,但是這并不代表它就是最完美的&#xff0c;它也有問題&#xff0c;這個問題就是循環…

POJ2114-Boatherds-樹分治

題目描述 Boatherds Inc. is a sailing company operating in the country of Trabantustan and offering boat trips on Trabantian rivers. All the rivers originate somewhere in the mountains and on their way down to the lowlands they gradually join and finally th…

c++11 你需要知道這些就夠了

https://blog.csdn.net/tangliguantou/article/details/50549751c11新特性舉著火把尋找電燈今天我就權當拋磚引玉&#xff0c;如有不解大家一起探討。有部分內容是引用自互聯網上的內容&#xff0c;如有問題請聯系我。T&& 右值引用 std::move 右值引用出現之前我們只能…

HDU5977-Garden of Eden-樹分治+FWT

題目描述 When God made the first man, he put him on a beautiful garden, the Garden of Eden. Here Adam lived with all animals. God gave Adam eternal life. But Adam was lonely in the garden, so God made Eve. When Adam was asleep one night, God took a rib fro…