Linux下多线程epoll编程实例

文章来源:http://blog.csdn.net/susubuhui/article/details/37906287

  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. }  

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值