`
famoushz
  • 浏览: 2953514 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

一个简短的epoll服务器示例, 监听5000个端口, 使用线程池

阅读更多
文章地址:
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 <stdio.h>  
  4. #include <string.h>  
  5. #include <stdlib.h>  
  6. #include <time.h>  
  7. #include <unistd.h>  
  8.   
  9. #include <sys/epoll.h>  
  10. #include <sys/stat.h>  
  11. #include <sys/types.h>  
  12. #include <sys/socket.h>  
  13. #include <netinet/in.h>  
  14. #include <arpa/inet.h>  
  15. #include <unistd.h>  
  16. #include <netdb.h>  
  17. #include <pthread.h>  
  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, "<html>\r\n<head>\r\n");  
  229.    p += sprintf(p, "<meta content=\"text/html; charset=UTF-8\" http-equiv=\"Content-Type\">\r\n");  
  230.    p += sprintf(p, "</head>\r\n");  
  231.    p += sprintf(p, "<body style=\"background-color: rgb(229, 229, 229);\">\r\n");  
  232.      
  233.    p += sprintf(p, "<center>\r\n");  
  234.    p += sprintf(p, "<H3>连接状态</H3>\r\n");  
  235.    p += sprintf(p, "<p>服务器地址 %s:%d</p>\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, "<H3>线程池状态</H3>\r\n");  
  241.    p += sprintf(p, "<p>线程池总数 %d 活动线程总数 %d</p>\r\n", THREAD_MAX, j);  
  242.    p += sprintf(p, "</center></body></html>\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. }  

分享到:
评论

相关推荐

    epollExample.txt.gz_epoll C++_epoll file server_epoll server_epo

    一个简短的epoll服务器示例, 监听5000个端口, 使用线程池 运行这个程序需要预先设置栈内存和文件描述符上限, 否则运行失败

    WSAEventSelect 线程池 实现服务器示例

    在本文中,我们将深入探讨如何使用`WSAEventSelect`函数和线程池技术来构建一个高效的服务器示例。`WSAEventSelect`是Windows Socket API(Winsock)中的一个关键函数,它允许应用程序通过事件对象(如内建的Windows...

    完成端口服务器简单示例

    本示例是使用VC++实现的一个简单完成端口服务器,旨在帮助开发者理解如何利用完成端口进行异步I/O操作。下面将详细介绍完成端口的工作原理以及如何在VC++环境中实现这一模型。 完成端口是Windows系统提供的一种I/O...

    完成端口结合线程池类库,源码,实例

    具体实现时,服务器端首先创建一个完成端口,并为每个监听套接字关联该端口。当客户端连接请求到达时,服务器接受连接并将新建立的套接字也关联到同一完成端口。接着,线程池中的线程会在适当的时候处理完成端口上的...

    TCPSOCKET 线程池通信客户端+服务器端示例代码

    在TCP SOCKET通信中,客户端首先创建一个`TcpClient`实例,然后连接到服务器的指定端口。服务器端创建`TcpListener`,设置监听的IP地址和端口号,并启动监听。当有客户端连接时,服务器端会调用`AcceptTcpClient()`...

    基于C语言编写的高并发Epoll服务器.zip

    "基于C语言编写的高并发Epoll服务器"是一个典型的示例,它展示了如何利用C语言的强大功能来构建能够处理大量并发连接的高效服务器。Epoll是Linux内核提供的一种I/O多路复用技术,它在处理大量并发连接时表现出了卓越...

    完成端口使用说明及多个例子

    使用CreateIoCompletionPort函数可以创建一个完成端口。通常,你需要指定一个文件句柄(如套接字),将其关联到完成端口,以便后续的I/O操作能够通过完成端口进行通知。 ```cpp HANDLE CreateIoCompletionPort(IN ...

    QTCPSocket线程池方法

    在给定的代码示例中,`myserver.cpp` 和 `mytask.cpp` 可能包含了使用线程池处理TCP连接的实现,而对应的头文件 `myserver.h` 和 `mytask.h` 定义了相关类和接口。 `QTcpSocket` 是一个用于进行双向字节流通信的类...

    Linux网络编程(四)——epoll+多线程实现简单的聊天(linux 服务器端 windows客户端)

    当新的连接到来时,服务器会为每个连接创建一个新的线程或者使用线程池处理。这里,多线程技术被用来并行处理多个客户端连接,以提高服务效率。每个线程都会有一个独立的epoll实例,这样可以避免因一个连接阻塞而...

    完成端口echo示例

    下面我们将详细探讨完成端口在Socket编程中的应用,以及如何构建一个简单的Echo服务器示例。 首先,理解完成端口的工作原理至关重要。完成端口是一种多线程调度机制,它将I/O操作的完成通知与线程池中的工作线程...

    完成端口(IOCP)模型示例

    在服务端,首先需要使用`CreateIoCompletionPort`函数创建一个完成端口。这个端口可以关联到多个I/O设备(如套接字),用于接收来自这些设备的完成事件。创建时需要指定线程池大小,决定如何调度处理I/O完成的线程。...

    完成端口服务器程序例子代码

    在本示例代码中,`Server_IOCP`可能包含了实现一个基于完成端口的服务器程序的核心逻辑。下面将详细解释完成端口的工作原理以及如何在实际编程中应用。 完成端口是一种多线程调度机制,它允许应用程序通过一个或多...

    Socket网络编程学习笔记之---使用线程池提高性能

    "SocketTest3"可能是一个包含示例代码的文件,它演示了如何将Socket与线程池结合来处理网络连接。这个文件可能包含以下内容: - Server端:创建ServerSocket监听端口,接收到连接请求后,不直接创建新线程,而是将...

    Jetty java程序指定一个端口,开通一个TCP服务

    本篇将详细讲解如何使用Jetty来指定一个端口,开通一个TCP服务。 首先,理解Jetty的基本结构。Jetty的核心组件包括Server、Connector和Handler。Server是整个Jetty服务器的入口点,Connector负责处理网络连接,而...

    JAVA服务器端Socket线程池

    在Java中,主要涉及两个核心类:`ServerSocket`用于创建服务端监听端口并接收客户端连接;`Socket`用于表示客户端与服务端之间的一个连接。 #### 三、线程池技术概述 线程池是一种基于池化思想的设计模式,用于...

    计算机软件-商业源码-服务器监听.zip

    在TCP/IP协议栈中,服务器必须先启动并监听一个或多个端口,以便客户端能够找到并与其建立连接。这个过程涉及到网络编程的基本概念,如套接字(socket)编程和多路复用技术。 源码软件的分享对于开发者来说是非常...

    高性能完成端口服务器&客户端

    在“高性能完成端口服务器&客户端”项目中,你将找到一个使用完成端口技术实现的服务器和客户端示例。 首先,让我们详细了解一下完成端口的工作原理: 1. **创建完成端口**:开发者首先需要调用`...

    IOCP完成端口模型示例代码.zip

    在服务器端,通常会创建一个完成端口,然后绑定多个套接字到这个端口上。当客户端发起连接请求时,服务器端会接受连接,并将新建立的套接字关联到IOCP。当数据到达或需要发送数据时,服务器会调用异步发送和接收函数...

Global site tag (gtag.js) - Google Analytics