Linux下多线程epoll编程实例

155 阅读6分钟

文章来源:blog.csdn.net/susubuhui/a…

  1. Linux下多线程epoll编程,在高并发下测试通过,可以支持10000用户同时在线,测试服务器为Linode的vps服务器,操作系统为Centos64  

  2.   

  3. // cs_network.cpp  

  4.   

  5. // created by ccc  

  6.   

  7. #include "config.h"  

  8. #include "cs_network.h"  

  9. #include <iostream>  

  10. #include <sys/socket.h>  

  11.   

  12. #define VERSION_SOLARIS 0  

  13.   

  14. #if VERSION_SOLARIS  

  15.  #include <port.h>  

  16. #else  

  17.  #include <sys/epoll.h>  

  18. #endif  

  19.   

  20. #include <netinet/in.h>  

  21. #include <arpa/inet.h>  

  22. #include <fcntl.h>  

  23. #include <unistd.h>  

  24. #include <stdio.h>  

  25. #include <errno.h>  

  26. #include <stdlib.h>  

  27. #include <string.h>  

  28. #include <pthread.h>  

  29. #include "cs_data_inspect.h"  

  30. #include "cs_heart_thread.h"  

  31. #include "cs_gdata.h"  

  32. #include "cs_work_thread.h"  

  33.   

  34. #define SERV_PORT  5565 // 服务器端口  

  35. #define LISTENQ          128   // listen sock 参数  

  36. #define MAX_EPOLL_EVENT_COUNT      500   // 同时监听的 epoll 数  

  37. #define MAX_READ_BUF_SIZE    1024 * 8 // 最大读取数据 buf  

  38.   

  39. #define SOCKET_ERROR -1  

  40.   

  41. static int epfd;  

  42. static int listenfd;  

  43.   

  44. static pthread_t tids[NETWORK_READ_THREAD_COUNT];  

  45. static pthread_mutex_t mutex_thread_read;  

  46. static pthread_cond_t cond_thread_read;  

  47. static void *thread_read_tasks(void *args);  

  48.   

  49. static st_io_context_task *read_tasks_head = NULL;  

  50. static st_io_context_task *read_tasks_tail = NULL;  

  51.   

  52. //  

  53.   

  54.   

  55. static void append_read_task(st_context *context)  

  56. {  

  57.  st_io_context_task *new_task = NULL;  

  58.   

  59.  new_task = new st_io_context_task();  

  60.  new_task->context = context;  

  61.   

  62.  pthread_mutex_lock(&mutex_thread_read);  

  63.   

  64.  if(read_tasks_tail == NULL)  

  65.  {  

  66.   read_tasks_head = new_task;  

  67.   read_tasks_tail = new_task;  

  68.  }     

  69.  else  

  70.  {     

  71.   read_tasks_tail->next= new_task;  

  72.   read_tasks_tail = new_task;  

  73.  }    

  74.   

  75.  pthread_cond_broadcast(&cond_thread_read);  

  76.  pthread_mutex_unlock(&mutex_thread_read);   

  77. }  

  78.   

  79. void _setnonblocking(int sock)  

  80. {  

  81.  int opts;  

  82.  opts = fcntl(sock, F_GETFL);  

  83.  if(opts < 0){  

  84.   log("fcntl(sock,GETFL)");  

  85.   exit(1);  

  86.  }  

  87.   

  88.  opts = opts | O_NONBLOCK;  

  89.  if(fcntl(sock, F_SETFL, opts)<0){  

  90.   log("fcntl(sock,SETFL,opts)");  

  91.   exit(1);  

  92.  }  

  93. }  

  94.   

  95.   

  96. void* get_network_event(void *param)  

  97. {  

  98.  long network_event_id;  

  99.   

  100.  int i, sockfd;  

  101.   

  102.  network_event_id = (long) param;  

  103.   

  104.  log("begin thread get_network_event: %ld", network_event_id);  

  105.   

  106.  st_context *context = NULL;  

  107.   

  108. #if VERSION_SOLARIS  

  109.  uint_t nfds;  

  110.  port_event_t now_ev, ev, events[MAX_EPOLL_EVENT_COUNT];  

  111. #else  

  112.  unsigned nfds;  

  113.  struct epoll_event now_ev, ev, events[MAX_EPOLL_EVENT_COUNT];  

  114. #endif  

  115.   

  116.   

  117. #if VERSION_SOLARIS  

  118.  struct timespec timeout;  

  119.  timeout.tv_sec = 0;  

  120.  timeout.tv_nsec = 50000000;  

  121. #endif  

  122.   

  123.  while(1)   

  124.  {  

  125. #if VERSION_SOLARIS  

  126.   nfds = MAX_EPOLL_EVENT_COUNT;  

  127.   if (port_getn(epfd, events, MAX_EPOLL_EVENT_COUNT, &nfds, &timeout) != 0){  

  128.   

  129.    if (errno != ETIME){  

  130.     log("port_getn error");  

  131.     return false;  

  132.    }  

  133.   }  

  134.   

  135.   if (nfds == 0){  

  136.    continue;  

  137.   }  

  138.   else{  

  139.    // log("on port_getn: %d", nfds);  

  140.   }  

  141. #else  

  142.   //等待epoll事件的发生  

  143.   nfds = epoll_wait(epfd, events, MAX_EPOLL_EVENT_COUNT, 100000);  

  144. #endif  

  145.   

  146.   //处理所发生的所有事件  

  147.   for(i = 0; i < nfds; i++)  

  148.   {  

  149.    now_ev = events[i];  

  150.   

  151. #if VERSION_SOLARIS  

  152.    context = (st_context *)now_ev.portev_user;  

  153. #else  

  154.    context = (st_context *)now_ev.data.ptr;  

  155. #endif  

  156.   

  157. #if VERSION_SOLARIS  

  158.    if (now_ev.portev_source != PORT_SOURCE_FD){  

  159.     continue;  

  160.    }  

  161.   

  162.    if(now_ev.portev_object == listenfd)  

  163. #else  

  164.    if(context->fd == listenfd)  

  165. #endif  

  166.    {  

  167. #if VERSION_SOLARIS  

  168.     // 重新关联listen fd  

  169.     port_associate(epfd, PORT_SOURCE_FD, listenfd, POLLIN, context);  

  170. #endif  

  171.   

  172.     //append_read_task(NULL, true);  

  173.     int connfd;  

  174.     struct sockaddr_in clientaddr = {0};  

  175.     socklen_t clilen = sizeof(clientaddr);  

  176.   

  177.     connfd = accept(listenfd, (sockaddr *)&clientaddr, &clilen);  

  178.   

  179.     if(connfd == -1){  

  180.      log("connfd == -1 [%d]", errno);  

  181.      continue;  

  182.     }  

  183.   

  184.     _setnonblocking(connfd);  

  185.     int nRecvBuf=128*1024;//设置为32K  

  186.     setsockopt(connfd, SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));  

  187.     //发送缓冲区  

  188.     int nSendBuf=128*1024;//设置为32K  

  189.     setsockopt(connfd, SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));  

  190.   

  191.     int nNetTimeout=1000;//1秒  

  192.     //发送时限  

  193.     setsockopt(connfd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&nNetTimeout, sizeof(int));  

  194.   

  195.     //接收时限  

  196.     setsockopt(connfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&nNetTimeout, sizeof(int));  

  197.   

  198.     const char *remote_addr = inet_ntoa(clientaddr.sin_addr);  

  199.     int  remote_port = ntohs(clientaddr.sin_port);  

  200.   

  201.     context = query_context(connfd, remote_addr, remote_port);  

  202.   

  203.     mutex_lock(mutex_id_pool.mutex);  

  204.     context->id = add_client(context);  

  205.     mutex_unlock(mutex_id_pool.mutex);  

  206.   

  207.  //#if IS_DEBUG  

  208.  //   log("new obj fd: %d, id: %d context:%8X", connfd, context->id, context);  

  209.  //#endif  

  210.   

  211. #if VERSION_SOLARIS  

  212.     port_associate(epfd, PORT_SOURCE_FD, connfd, POLLIN, context);  

  213. #else  

  214.     struct epoll_event ev;  

  215.   

  216.     ev.events = EPOLLIN | EPOLLET;  

  217.     ev.data.ptr = context;  

  218.     epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);  

  219. #endif  

  220.    }  

  221.   

  222. #if VERSION_SOLARIS  

  223.    else if(now_ev.portev_events == POLLIN)  

  224.    {  

  225.     //log("on: now_ev.portev_events == POLLIN");  

  226.     append_read_task(context);  

  227.    }  

  228.    else{  

  229.     log("unknow portev_events: %d", now_ev.portev_events);  

  230.    }  

  231. #else  

  232.    else if(now_ev.events & EPOLLIN)  

  233.    {  

  234.     append_read_task(context);  

  235.    }  

  236.    else if(now_ev.events & EPOLLOUT)  

  237.    {   

  238.     sockfd = context->fd;  

  239.   

  240.     struct epoll_event ev;  

  241.   

  242.     ev.events = EPOLLIN | EPOLLET;  

  243.     ev.data.ptr = context;  

  244.     epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);  

  245.    }  

  246.    else if(now_ev.events & EPOLLHUP)  

  247.    {  

  248.     log("else if(now_ev.events & EPOLLHUP)");  

  249.     del_from_network(context);  

  250.    }  

  251.    else  

  252.    {  

  253.     log("[warming]other epoll event: %d", now_ev.events);  

  254.    }  

  255. #endif  

  256.   }  

  257.  }  

  258.   

  259.  return NULL;  

  260. }  

  261.   

  262. bool start_network()  

  263. {  

  264.  int i, sockfd;  

  265.  int optionVal = 0;  

  266.   

  267.  st_context *context = NULL;  

  268.   

  269. #if VERSION_SOLARIS  

  270.  uint_t nfds;  

  271.  port_event_t ev;  

  272. #else  

  273.  unsigned nfds;  

  274.  struct epoll_event ev;  

  275. #endif  

  276.   

  277.  pthread_mutex_init(&mutex_thread_read, NULL);  

  278.   

  279.  pthread_cond_init(&cond_thread_read, NULL);  

  280.   

  281.  int ret;  

  282.  pthread_attr_t tattr;  

  283.   

  284.  /* Initialize with default */  

  285.  if(ret = pthread_attr_init(&tattr)){  

  286.   perror("Error initializing thread attribute [pthread_attr_init(3C)] ");  

  287.   return (-1);  

  288.  }  

  289.   

  290.  /* Make it a bound thread */  

  291.  if(ret = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM)){  

  292.   perror("Error making bound thread [pthread_attr_setscope(3C)] ");  

  293.   return (-1);  

  294.  }  

  295.   

  296.  //初始化用于读线程池的线程,开启两个线程来完成任务,两个线程会互斥地访问任务链表  

  297.  for (i = 0; i < NETWORK_READ_THREAD_COUNT; i++){  

  298.   

  299.   pthread_create(&tids[i], &tattr, thread_read_tasks, NULL);  

  300.   //log("new read task thread %8X created.", tids[i]);  

  301.  }  

  302.   

  303. #if VERSION_SOLARIS  

  304.  epfd = port_create();  

  305. #else  

  306.  epfd = epoll_create(MAX_EPOLL_EVENT_COUNT);  

  307. #endif  

  308.   

  309.  struct sockaddr_in serveraddr;  

  310.   

  311.  if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){  

  312.   

  313.   log("can't create socket.\n");  

  314.   

  315.   return false;  

  316.  }  

  317.   

  318.  //把socket设置为非阻塞方式  

  319.  _setnonblocking(listenfd);  

  320.   

  321.  optionVal = 0;  

  322.  setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,  &optionVal, sizeof(optionVal));  

  323.   

  324.  {  

  325.   //设置与要处理的事件相关的文件描述符  

  326.   context = query_context(listenfd, "localhost", SERV_PORT);  

  327.   

  328. #if VERSION_SOLARIS  

  329.   //注册epoll事件  

  330.   port_associate(epfd, PORT_SOURCE_FD, listenfd, POLLIN, context);  

  331. #else  

  332.   ev.data.ptr = context;  

  333.   ev.events = EPOLLIN;  

  334.   epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);  

  335. #endif  

  336.  }  

  337.   

  338.  memset(&serveraddr, 0, sizeof(serveraddr));  

  339.  serveraddr.sin_family = AF_INET;  

  340.  serveraddr.sin_port = htons(SERV_PORT);  

  341.  serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);  

  342.   

  343.  if (bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr))  == -1){  

  344.   

  345.   log("bind error: %d\n", errno);  

  346.   

  347.   return false;  

  348.  }  

  349.   

  350.  //开始监听  

  351.  if (listen(listenfd, LISTENQ) == -1){  

  352.   

  353.   log("listen error: %d\n", errno);  

  354.   

  355.   return false;  

  356.  }  

  357.   

  358.  log("");  

  359.  log("**************************************");  

  360.  log("********   cserver start ok   ********");  

  361.  log("**************************************");  

  362.    

  363.  pthread_t tids_get_network_event[NETWORK_READ_THREAD_COUNT];  

  364.   

  365.  for (int i = 1; i <= 2; i++){  

  366.   pthread_create(&tids_get_network_event[i], &tattr, get_network_event, (void*)i);  

  367.  }  

  368.   

  369.  get_network_event(NULL);  

  370.   

  371.  return true;  

  372. }  

  373.   

  374. void shutdown_network()  

  375. {  

  376.  log("begin shutdown network ...");  

  377. }  

  378.   

  379. void *thread_read_tasks(void *args)  

  380. {  

  381.  bool is_listenfd;  

  382.  st_context *context;  

  383.   

  384.  while(1)  

  385.  {  

  386.   //互斥访问任务队列  

  387.   pthread_mutex_lock(&mutex_thread_read);  

  388.   

  389.   //等待到任务队列不为空  

  390.   while(read_tasks_head == NULL)  

  391.    pthread_cond_wait(&cond_thread_read, &mutex_thread_read); //线程阻塞,释放互斥锁,当等待的条件等到满足时,它会再次获得互斥锁  

  392.   

  393.   context     = read_tasks_head->context;  

  394.   

  395.   //从任务队列取出一个读任务  

  396.   st_io_context_task *tmp = read_tasks_head;  

  397.   read_tasks_head = read_tasks_head->next;  

  398.   delete tmp;  

  399.   

  400.   if (read_tasks_head == NULL){  

  401.    read_tasks_tail = NULL;  

  402.   }  

  403.   

  404.   pthread_mutex_unlock(&mutex_thread_read);  

  405.   

  406.   {  

  407.    char buf_read[MAX_READ_BUF_SIZE];  

  408.    int  read_count;  

  409.   

  410.    read_count = recv(context->fd, buf_read, sizeof(buf_read), 0);  

  411.   

  412.    //log("read id[%d]errno[%d]count[%d]", context->id, errno, read_count);  

  413.   

  414.    if (read_count < 0)  

  415.    {  

  416.     if (errno == EAGAIN){  

  417.      continue;  

  418.     }  

  419.   

  420.     log("1 recv < 0: errno: %d", errno);  

  421.     //if (errno == ECONNRESET){

      

  422.     // log("client[%s:%d] disconnect ", context->remote_addr, context->remote_port);  

  423.     //}  

  424.   

  425.     del_from_network(context);  

  426.     continue;  

  427.    }  

  428.    else if (read_count == 0)  

  429.    {  

  430.     //客户端关闭了,其对应的连接套接字可能也被标记为EPOLLIN,然后服务器去读这个套接字  

  431.     //结果发现读出来的内容为0,就知道客户端关闭了。  

  432.     log("client close connect! errno[%d]", errno);  

  433.   

  434.     del_from_network(context);  

  435.     continue;  

  436.    }   

  437.    else  

  438.    {  

  439.     do   

  440.     {  

  441.      if (! on_recv_data(context, buf_read, read_count)){  

  442.       context->is_illegal = true;  

  443.   

  444.       log("当前客户数据存在异常");  

  445.       del_from_network(context);  

  446.       break;  

  447.      }  

  448.   

  449.      read_count = read(context->fd, buf_read, sizeof(buf_read));  

  450.   

  451.      if (read_count <= 0){  

  452.       if (errno == EINTR)  

  453.        continue;  

  454.       if (errno == EAGAIN){  

  455. #if VERSION_SOLARIS  

  456.        port_associate(epfd, PORT_SOURCE_FD, context->fd, POLLIN, context);  

  457. #endif  

  458.        break;  

  459.       }  

  460.   

  461.   

  462.       log("2 error read_count < 0, errno[%d]", errno);  

  463.   

  464.       del_from_network(context);  

  465.       break;  

  466.      }  

  467.     }  

  468.     while(1);  

  469.    }  

  470.   }  

  471.  }  

  472.   

  473.  log("thread_read_tasks end ....");  

  474. }  

  475.   

  476. void del_from_network(st_context *context, bool is_card_map_locked)  

  477. {  

  478.  gdata.lock();  

  479.  if (gdata.manager == context){  

  480.   gdata.manager = NULL;  

  481.  }  

  482.  gdata.unlock();  

  483.   

  484.  context->lock();  

  485.   

  486.  if (! context->valide){  

  487.   log("del_from_network is not valide");  

  488.   context->unlock();  

  489.   return;  

  490.  }  

  491.   

  492.  // 断开连接  

  493.  int fd = context->fd;  

  494.   

  495.  if (fd != -1){  

  496. #if VERSION_SOLARIS  

  497.   port_dissociate(epfd, PORT_SOURCE_FD, fd);  

  498. #else  

  499.   struct epoll_event ev;  

  500.   ev.data.fd = fd;  

  501.   epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev);  

  502. #endif  

  503.   close_socket(fd);  

  504.   context->fd = -1;  

  505.  }  

  506.   

  507.  context->valide = false;  

  508.  log("client[%d] is invalide", context->id);  

  509.  context->unlock();  

  510.   

  511.  mutex_lock(mutex_id_pool.mutex);  

  512.  del_client(context->id);  

  513.  mutex_unlock(mutex_id_pool.mutex);  

  514. }  

  515.   

  516. bool context_write(st_context *context, const char* buf, int len)  

  517. {  

  518.  if (len <= 0) return true;  

  519.   

  520.  int fd = context->fd;  

  521.   

  522.  if (fd == -1){  

  523.   return false;  

  524.  }  

  525.   

  526.  int nleft = len;  

  527.  int nsend;  

  528.   

  529.  bool result = true;  

  530.   

  531.     while(nleft > 0){  

  532.         nsend = write(fd, &buf[len - nleft], nleft);  

  533.   

  534.         if (nsend < 0) {  

  535.   

  536.             if(errno == EINTR) continue;  

  537.    if(errno == EAGAIN) {  

  538.     break;  

  539.    }  

  540.   

  541.             result = false;  

  542.    break;  

  543.         }  

  544.         else if (nsend == 0){  

  545.   

  546.             result = false;  

  547.    break;  

  548.         }  

  549.         nleft -= nsend;  

  550.     }  

  551.   

  552.  return result;  

  553. }  

\