英雄联盟战士符文图:一个简短的epoll服务器示例, 监听5000个端口, 使用线程池 - 趋势创造者-祗偽妳...

来源:百度文库 编辑:偶看新闻 时间:2024/04/28 13:31:16

文章地址:
http://www.cublog.cn/u/17999/showart.php?id=159057
运行这个程序需要预先设置栈内存和文件描述符上限, 否则运行失败
ulimit -n 16384
ulimit -s 4096

文件名:server.c
编译: gcc server.c -Wall -O2 -pthread -o server
程序源码如下(请自行编辑宏定义SERVER_IP为自己的IP):
cpp 代码 
  1. /*Linux 2.6 x86_64 only*/  
  2.   
  3. #include   
  4. #include   
  5. #include   
  6. #include   
  7. #include   
  8.   
  9. #include   
  10. #include   
  11. #include   
  12. #include   
  13. #include   
  14. #include   
  15. #include   
  16. #include   
  17. #include   
  18.   
  19. #define THREAD_MAX 4096  
  20. #define LISTEN_MAX 5000  
  21. #define SERVER_IP "192.168.1.103"  
  22.   
  23. typedef struct {  
  24.    char    ip4[128];  
  25.    int    port;  
  26.    int    fd;  
  27. } LISTEN_INFO;  
  28.   
  29. //服务器参数  
  30. static LISTEN_INFO s_listens[LISTEN_MAX];  
  31.   
  32. //线程池参数  
  33. static unsigned int s_thread_para[THREAD_MAX][8];//线程参数  
  34. static pthread_t s_tid[THREAD_MAX];//线程ID  
  35. pthread_mutex_t s_mutex[THREAD_MAX];//线程锁  
  36.   
  37.   
  38. //私有函数  
  39. static int init_thread_pool(void);  
  40. static int init_listen4(char *ip4, int port, int max_link);  
  41.   
  42. //线程函数  
  43. void * test_server4(unsigned int thread_para[]);  
  44.   
  45. int main(int argc, char *argv[])//客户端驱动  
  46. {  
  47.    //临时变量  
  48.    int            i, j, rc;  
  49.      
  50.    int            sock_listen;    //监听套接字  
  51.    int            sock_cli;    //客户端连接  
  52.    int            listen_index;  
  53.      
  54.    int            epfd;  
  55.    int             nfds;  
  56.    struct epoll_event    ev;  
  57.    struct epoll_event    events[LISTEN_MAX];  
  58.   
  59.    socklen_t        addrlen;    //地址信息长度  
  60.    struct sockaddr_in    addr4;        //IPv4地址结构  
  61.   
  62.    //线程池初始化  
  63.    rc = init_thread_pool();  
  64.    if (0 != rc) exit(-1);  
  65.      
  66.    //初始化服务监听  
  67.    for(i = 0; i < LISTEN_MAX; i++) {  
  68.        sprintf(s_listens[i].ip4, "%s", SERVER_IP);  
  69.        s_listens[i].port = 8000 + i;  
  70.        //创建监听  
  71.        rc = init_listen4(s_listens[i].ip4, s_listens[i].port, 64);  
  72.        if (0 > rc) {  
  73.            fprintf(stderr, "无法创建服务器监听于%s:%d\r\n", s_listens[i].ip4, s_listens[i].port);  
  74.            exit(-1);  
  75.        }  
  76.        s_listens[i].fd = rc;  
  77.    }  
  78.      
  79.    //设置集合  
  80.    epfd = epoll_create(8192);  
  81.    for (i = 0; i < LISTEN_MAX; i++) {  
  82.        //加入epoll事件集合  
  83.        ev.events = EPOLLIN;  
  84.        ev.data.u32 = i;//记录listen数组下标  
  85.        if (epoll_ctl(epfd, EPOLL_CTL_ADD, s_listens[i].fd, &ev) < 0) {  
  86.            fprintf(stderr, "向epoll集合添加套接字失败(fd =%d)\r\n", rc);  
  87.            exit(-1);  
  88.        }  
  89.    }  
  90.   
  91.   
  92.    //服务循环  
  93.    for( ; ; ) {  
  94.        //等待epoll事件  
  95.        nfds = epoll_wait(epfd, events, LISTEN_MAX, -1);  
  96.        //处理epoll事件  
  97.        for(i = 0; i < nfds; i++) {  
  98.            //接收客户端连接  
  99.            listen_index = events[i].data.u32;  
  100.            sock_listen = s_listens[listen_index].fd;  
  101.            addrlen = sizeof(struct sockaddr_in);  
  102.            bzero(&addr4, addrlen);  
  103.            sock_cli = accept(sock_listen, (struct sockaddr *)&addr4, &addrlen);  
  104.            if(0 > sock_cli) {  
  105.                fprintf(stderr, "接收客户端连接失败\n");  
  106.                continue;  
  107.            }  
  108.            //查询空闲线程对  
  109.            for(j = 0; j < THREAD_MAX; j++) {  
  110.                if (0 == s_thread_para[j][0]) break;  
  111.            }  
  112.            if (j >= THREAD_MAX) {  
  113.                fprintf(stderr, "线程池已满, 连接将被放弃\r\n");  
  114.                shutdown(sock_cli, SHUT_RDWR);  
  115.                close(sock_cli);  
  116.                continue;  
  117.            }  
  118.            //复制有关参数  
  119.            s_thread_para[j][0] = 1;//设置活动标志为"活动"  
  120.            s_thread_para[j][1] = sock_cli;//客户端连接  
  121.            s_thread_para[j][2] = listen_index;//服务索引  
  122.            //线程解锁  
  123.            pthread_mutex_unlock(s_mutex + j);  
  124.        }//end of for(i;;)  
  125.    }//end of for(;;)  
  126.   
  127.    exit(0);  
  128. }  
  129.   
  130. static int init_thread_pool(void)  
  131. {  
  132.    int    i, rc;  
  133.   
  134.    //初始化线程池参数  
  135.    for(i = 0; i < THREAD_MAX; i++) {  
  136.        s_thread_para[i][0] = 0;//设置线程占用标志为"空闲"  
  137.        s_thread_para[i][7] = i;//线程池索引  
  138.        pthread_mutex_lock(s_mutex + i);//线程锁  
  139.    }  
  140.   
  141.    //创建线程池  
  142.    for(i = 0; i < THREAD_MAX; i++) {  
  143.        rc = pthread_create(s_tid + i, 0, (void *)test_server4, (void *)(s_thread_para[i]));  
  144.        if (0 != rc) {  
  145.            fprintf(stderr, "线程创建失败\n");  
  146.            return(-1);  
  147.        }  
  148.    }  
  149.      
  150.    //成功返回  
  151.    return(0);  
  152. }  
  153.   
  154. static int init_listen4(char *ip4, int port, int max_link)  
  155. {  
  156.    //临时变量  
  157.    int            sock_listen4;  
  158.    struct sockaddr_in    addr4;  
  159.    unsigned int        optval;  
  160.    struct linger        optval1;  
  161.   
  162.    //初始化数据结构  
  163.    bzero(&addr4, sizeof(addr4));  
  164.    inet_pton(AF_INET, ip4, &(addr4.sin_addr));  
  165.    addr4.sin_family = AF_INET;  
  166.    addr4.sin_port = htons(port);  
  167.      
  168.    //创建SOCKET  
  169.    sock_listen4 = socket(AF_INET, SOCK_STREAM, 0);  
  170.    if (0 > sock_listen4) return(-1);  
  171.   
  172.    //设置SO_REUSEADDR选项(服务器快速重起)  
  173.    optval = 0x1;  
  174.    setsockopt(sock_listen4, SOL_SOCKET, SO_REUSEADDR, &optval, 4);  
  175.      
  176.    //设置SO_LINGER选项(防范CLOSE_WAIT挂住所有套接字)  
  177.    optval1.l_onoff = 1;  
  178.    optval1.l_linger = 60;  
  179.    setsockopt(sock_listen4, SOL_SOCKET, SO_LINGER, &optval1, sizeof(struct linger));  
  180.   
  181.    if (0 > bind(sock_listen4, (struct sockaddr *)&addr4, sizeof(addr4))) {  
  182.        close(sock_listen4);  
  183.        return(-1);  
  184.    }  
  185.   
  186.    if (0 > listen(sock_listen4, max_link)) {  
  187.        close(sock_listen4);  
  188.        return(-1);  
  189.    }  
  190.   
  191.    return(sock_listen4);  
  192. }  
  193.   
  194.   
  195. void * test_server4(unsigned int thread_para[])  
  196. {  
  197.    //临时变量  
  198.    int        pool_index;    //线程池索引  
  199.    int        sock_cli;    //客户端连接  
  200.    int        listen_index;    //监听索引  
  201.   
  202.    char        buff[32768];    //传输缓冲区  
  203.    char        *p;  
  204.    int        i, j, len;  
  205.      
  206.    //线程脱离创建者  
  207.    pthread_detach(pthread_self());  
  208.    pool_index = thread_para[7];  
  209.   
  210. wait_unlock:  
  211.   
  212.    pthread_mutex_lock(s_mutex + pool_index);//等待线程解锁  
  213.      
  214.    //线程变量内容复制  
  215.    sock_cli = thread_para[1];//客户端连接  
  216.    listen_index = thread_para[2];//监听索引  
  217.      
  218.    //接收请求  
  219.    len = recv(sock_cli, buff, 32768, MSG_NOSIGNAL);  
  220.   
  221.    //构造响应  
  222.    p = buff;  
  223.    //HTTP头  
  224.    p += sprintf(p, "HTTP/1.1 200 OK\r\n");  
  225.    p += sprintf(p, "Content-Type: text/html\r\n");  
  226.    p += sprintf(p, "Connection: closed\r\n\r\n");  
  227.    //页面  
  228.    p += sprintf(p, "\r\n\r\n");  
  229.    p += sprintf(p, "\r\n");  
  230.    p += sprintf(p, "\r\n");  
  231.    p += sprintf(p, "\r\n");  
  232.      
  233.    p += sprintf(p, "
    \r\n");  
  234.    p += sprintf(p, "

    连接状态

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

    服务器地址 %s:%d

    \r\n"
    , s_listens[listen_index].ip4, s_listens[listen_index].port);  
  236.    j = 0;  
  237.    for(i = 0; i < THREAD_MAX; i++) {  
  238.        if (0 != s_thread_para[i][0]) j++;  
  239.    }  
  240.    p += sprintf(p, "

    线程池状态

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

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

    \r\n"
    , THREAD_MAX, j);  
  242.    p += sprintf(p, "\r\n");  
  243.    len = p - buff;  
  244.   
  245.    //发送响应  
  246.    send(sock_cli, buff, len, MSG_NOSIGNAL);  
  247.   
  248.    //释放连接  
  249.    shutdown(sock_cli, SHUT_RDWR);  
  250.    close(sock_cli);  
  251.      
  252.    //线程任务结束  
  253.    thread_para[0] = 0;//设置线程占用标志为"空闲"  
  254.    goto wait_unlock;  
  255.   
  256.    pthread_exit(NULL);  
  257. }