Chinaunix首页 | 论坛 | 博客
  • 博客访问: 232287
  • 博文数量: 96
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 0
  • 用 户 组: 普通用户
  • 注册时间: 2016-07-14 11:43
文章分类

全部博文(96)

文章存档

2016年(41)

2015年(55)

我的朋友

分类: C/C++

2015-11-27 14:43:05


点击(此处)折叠或打开

  1. #include <pthread.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <stdio.h>
  6. #include <fcntl.h>

  7. #include <arpa/inet.h>
  8. #include <sys/epoll.h>
  9. #include <sys/errno.h>
  10. #include <sys/socket.h>

  11. #define THREAD_MAX 20
  12. #define LISTEN_MAX 20
  13. #define SERVER_IP "127.0.0.1"

  14. typedef struct
  15. {
  16.     char ip4[128];
  17.     int port;
  18.     int fd;
  19. } LISTEN_INFO;

  20. //服务器参数
  21. static LISTEN_INFO s_listens[LISTEN_MAX];

  22. //线程池参数
  23. static unsigned int s_thread_para[THREAD_MAX][8];//线程参数
  24. static pthread_t s_tid[THREAD_MAX];//线程ID
  25. pthread_mutex_t s_mutex[THREAD_MAX];//线程锁

  26. //私有函数
  27. static int init_thread_pool(void);//初始化数据
  28. static int init_listen4(char *ip4, int port, int max_link); //初始化监听

  29. //线程函数
  30. void * test_server4(unsigned int thread_para[]);

  31. //设置文件描述符为NonBlock
  32. bool setNonBlock(int fd)
  33. {
  34.     int flags = fcntl(fd, F_GETFL, 0);
  35.     flags |= O_NONBLOCK;
  36.     if(-1 == fcntl(fd, F_SETFL, flags))
  37.     {
  38.         return false;
  39.     }
  40.     return true;
  41. }
  42.  
  43. int main(int argc, char *argv[])//客户端驱动
  44. {
  45.     //临时变量
  46.     int i, j, rc;

  47.     int sock_listen; //监听套接字
  48.     int sock_cli; //客户端连接
  49.     int listen_index;

  50.     int epfd;
  51.     int nfds;
  52.     struct epoll_event ev;
  53.     struct epoll_event events[LISTEN_MAX];
  54.      
  55.     socklen_t addrlen; //地址信息长度
  56.     struct sockaddr_in addr4; //IPv4地址结构

  57.     //线程池初始化
  58.     rc = init_thread_pool();
  59.     if (0 != rc) exit(-1);

  60.     //初始化服务监听
  61.     for(i = 0; i < LISTEN_MAX; i++)
  62.     {
  63.         sprintf(s_listens[i].ip4, "%s", SERVER_IP);
  64.         s_listens[i].port = 40000 + i;
  65.         //创建监听
  66.         rc = init_listen4(s_listens[i].ip4, s_listens[i].port, 64);
  67.         if (0 > rc)
  68.         {
  69.             fprintf(stderr, "无法创建服务器监听于%s:%d\r\n", s_listens[i].ip4, s_listens[i].port);
  70.             exit(-1);
  71.         }
  72.         else
  73.         {
  74.             fprintf(stdout, "已创建服务器监听于%s:%d\r\n", s_listens[i].ip4, s_listens[i].port);
  75.         }
  76.         s_listens[i].fd = rc;
  77.     }
  78.      
  79.     //设置集合
  80.     epfd = epoll_create(8192);
  81.     for (i = 0; i < LISTEN_MAX; i++)
  82.     {
  83.         //加入epoll事件集合
  84.         ev.events = EPOLLIN | EPOLLET;
  85.         ev.data.u32 = i;//记录listen数组下标
  86.         if (epoll_ctl(epfd, EPOLL_CTL_ADD, s_listens[i].fd, &ev) < 0)
  87.         {
  88.             fprintf(stderr, "向epoll集合添加套接字失败(fd =%d)\r\n", rc);
  89.             exit(-1);
  90.         }
  91.     }
  92.      
  93.     //服务循环
  94.     for( ; ; )
  95.     {
  96.         //等待epoll事件
  97.         nfds = epoll_wait(epfd, events, LISTEN_MAX, -1);
  98.         //处理epoll事件
  99.         for(i = 0; i < nfds; i++)
  100.         {
  101.             //接收客户端连接
  102.             listen_index = events[i].data.u32;
  103.             sock_listen = s_listens[listen_index].fd;
  104.             addrlen = sizeof(struct sockaddr_in);
  105.             bzero(&addr4, addrlen);
  106.              
  107.             sock_cli = accept(sock_listen, (struct sockaddr *)&addr4, &addrlen);
  108.             if(0 > sock_cli)
  109.             {
  110.                 fprintf(stderr, "接收客户端连接失败\n");
  111.                 continue;
  112.             }
  113.             else
  114.             {
  115.                 char *myIP = inet_ntoa(addr4.sin_addr);
  116.                 printf("accept a connection from %s...\n", myIP);
  117.             }
  118.              
  119.             setNonBlock(sock_cli);
  120.             //查询空闲线程对
  121.             for(j = 0; j < THREAD_MAX; j++)
  122.             {
  123.                 if (0 == s_thread_para[j][0]) break;
  124.             }
  125.             if (j >= THREAD_MAX)
  126.             {
  127.                 fprintf(stderr, "线程池已满, 连接将被放弃\r\n");
  128.                 shutdown(sock_cli, SHUT_RDWR);
  129.                 close(sock_cli);
  130.                 continue;
  131.             }
  132.             //复制有关参数
  133.             s_thread_para[j][0] = 1;//设置活动标志为"活动"
  134.             s_thread_para[j][1] = sock_cli;//客户端连接
  135.             s_thread_para[j][2] = listen_index;//服务索引
  136.             //线程解锁
  137.             pthread_mutex_unlock(s_mutex + j);
  138.         }//end of for(i;;)
  139.     }//end of for(;;)

  140.     exit(0);
  141. }

  142. static int init_thread_pool(void)
  143. {
  144.     int i, rc;

  145.     //初始化线程池参数
  146.     for(i = 0; i < THREAD_MAX; i++)
  147.     {
  148.         s_thread_para[i][0] = 0;//设置线程占用标志为"空闲"
  149.         s_thread_para[i][7] = i;//线程池索引
  150.         pthread_mutex_lock(s_mutex + i);// 这个地方为什么要加锁?不加锁创建监听有时会不成功
  151.     }

  152.     //创建线程池
  153.     for(i = 0; i < THREAD_MAX; i++)
  154.     {
  155.         rc = pthread_create(s_tid + i, 0, (void* (*)(void *))test_server4, (void *)(s_thread_para[i]));
  156.         if (0 != rc)
  157.         {
  158.             fprintf(stderr, "线程创建失败\n");
  159.             return(-1);
  160.         }
  161.     }

  162.     //成功返回
  163.     return(0);
  164. }

  165. static int init_listen4(char *ip4, int port, int max_link)
  166. {
  167.     //临时变量
  168.     int sock_listen4;
  169.     struct sockaddr_in addr4;
  170.     unsigned int optval;
  171.     struct linger optval1;

  172.     //初始化数据结构
  173.     bzero(&addr4, sizeof(addr4));
  174.     //inet_pton将点分十进制IP转换为整数
  175.     inet_pton(AF_INET, ip4, &(addr4.sin_addr));
  176.     addr4.sin_family = AF_INET;
  177.     //htons将无符号short从主机字节序(x86:Big-Endian)转换为网络字节序
  178.     addr4.sin_port = htons(port);
  179.      
  180.     //创建流类型的SOCKET
  181.     sock_listen4 = socket(AF_INET, SOCK_STREAM, 0);
  182.     if (0 > sock_listen4)
  183.     {
  184.         fprintf(stderr, "创建socket异常, sock_listen4:%d\n", sock_listen4);
  185.         perror("创建socket异常");
  186.         return(-1);
  187.     }
  188.      
  189.     //设置SO_REUSEADDR选项(服务器快速重起)
  190.     optval = 0x1;
  191.     setsockopt(sock_listen4, SOL_SOCKET, SO_REUSEADDR, &optval, 4);

  192.     //设置SO_LINGER选项(防范CLOSE_WAIT挂住所有套接字)
  193.     optval1.l_onoff = 1;
  194.     optval1.l_linger = 60;
  195.     setsockopt(sock_listen4, SOL_SOCKET, SO_LINGER, &optval1, sizeof(struct linger));

  196.     if (0 > bind(sock_listen4, (struct sockaddr *)&addr4, sizeof(addr4)))
  197.     {
  198.         fprintf(stderr, "bind socket异常, sock_listen4:%d\n", sock_listen4);
  199.         perror("bind socket异常");
  200.         close(sock_listen4);
  201.         return(-1);
  202.     }

  203.     if (0 > listen(sock_listen4, max_link))
  204.     {
  205.         fprintf(stderr, "listen socket异常, sock_listen4:%d\n", sock_listen4);
  206.         perror("listen socket异常");
  207.         close(sock_listen4);
  208.         return(-1);
  209.     }

  210.     return (sock_listen4);
  211. }

  212. void * test_server4(unsigned int thread_para[])
  213. {
  214.     //临时变量
  215.     int sock_cli; //客户端连接
  216.     int pool_index; //线程池索引
  217.     int listen_index; //监听索引

  218.     char buff[32768]; //传输缓冲区
  219.     int i, j, len;
  220.     char *p;

  221.     //线程脱离创建者
  222.     pthread_detach(pthread_self());
  223.     pool_index = thread_para[7];

  224. wait_unlock:
  225.     pthread_mutex_lock(s_mutex + pool_index);//等待线程解锁

  226.     //线程变量内容复制
  227.     sock_cli = thread_para[1];//客户端连接
  228.     listen_index = thread_para[2];//监听索引

  229.     //接收请求
  230.     len = recv(sock_cli, buff, sizeof(buff), MSG_NOSIGNAL);
  231.     printf("%s\n", buff);
  232.      
  233.     //构造响应
  234.     p = buff;
  235.     //HTTP头
  236.     p += sprintf(p, "HTTP/1.1 200 OK\r\n");
  237.     p += sprintf(p, "Content-Type: text/html\r\n");
  238.     p += sprintf(p, "Connection: closed\r\n\r\n");
  239.     //页面
  240.     p += sprintf(p, "\r\n\r\n");
  241.     p += sprintf(p, " ; charset=UTF-8\" http-equiv=\"Content-Type\">\r\n");
  242.     p += sprintf(p, "\r\n");
  243.     p += sprintf(p, "background-color: rgb(229, 229, 229);\">\r\n");

  244.     p += sprintf(p, " \r\n");
  245.     p += sprintf(p, "

    连接状态

    \r\n"
    );
  246.     p += sprintf(p, "

    服务器地址 %s:%d

    \r\n"
    , s_listens[listen_index].ip4, s_listens[listen_index].port);
  247.     j = 0;
  248.     for(i = 0; i < THREAD_MAX; i++)
  249.     {
  250.         if (0 != s_thread_para[i][0]) j++;
  251.     }
  252.     p += sprintf(p, "

    线程池状态

    \r\n"
    );
  253.     p += sprintf(p, "

    线程池总数 %d 活动线程总数 %d

    \r\n"
    , THREAD_MAX, j);
  254.     p += sprintf(p, "\r\n");
  255.     len = p - buff;
  256.      
  257.     //发送响应
  258.     send(sock_cli, buff, len, MSG_NOSIGNAL);
  259.     memset(buff, 0, 32768);
  260.      
  261.     //释放连接
  262.     shutdown(sock_cli, SHUT_RDWR);
  263.     close(sock_cli);

  264.     //线程任务结束
  265.     thread_para[0] = 0;//设置线程占用标志为"空闲"
  266.     goto wait_unlock;

  267.     pthread_exit(NULL);
  268. }

补充:1. c中没有定义bool,需要自己添加定义 
                 typedef int bool
                #define true 1
                #define true 0
            2. 编译过程中需要 添加 -lpthread
                 gcc threadEpoll.c -lpthread

 
阅读(811) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~