1.poll io服务器(单线程处理所有请求)

server.c

#include#include <netinet/in.h>#include #include #include #include #include #include #include #include <sys/select.h>#include #include <string.h>#include #include #include #include  #include  /* inet(3) functions */#include /* poll function */#include #include "pollio_server.h"static struct pollfd client_fds[MAX_SELECT_CLIENT_NUM] = {0};static reg_rcv_handle_call_back callbackfunc = NULL;static int server_create(int port);static void *poll_loop(void *arg);/*****************************************
 *func:server_init
 *describe:初始化服务器端
 *para: port 端口号
 *return:成功则返回0,error则返回RET_VAL_FAILED
 *****************************************/int server_init(const int port, const reg_rcv_handle_call_back func)
{    int cnt = 10;    static int ret_fd = -1;    int ret;    for (int i = 0; i < MAX_SELECT_CLIENT_NUM; i++)
    {
        client_fds[i].fd=-1;
    }    while (cnt--)
    {
        ret_fd = server_create(port);        if (RET_VAL_FAILED != ret_fd)
        {            if (NULL == callbackfunc)
                callbackfunc = func;            break;
        }
        usleep(100 * 1000);
    }    if (ret_fd < 0)
    {        return RET_VAL_FAILED;
    }
    cnt = 10;    while (cnt--)
    {
        pthread_t loop_thread_id;
        ret = pthread_create(&loop_thread_id, NULL, poll_loop, &ret_fd);        if (0 == ret)
        {
            pthread_detach(loop_thread_id);
            printf("init done ,wait client connect...\n");            return RET_VAL_SUCCESS;
        }
    }    return RET_VAL_FAILED;
}/*****************************************
 *func:server_create
 *describe:创建监听的fd
 *para: port 端口号
 *return: 成功则返回监听的fd,error则返回RET_VAL_FAILED
 *****************************************/int server_create(int in_port)
{    struct addrinfo hints;    struct addrinfo *result, *rp;    int server_fd, retval;
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;     // Return IPv4 and IPv6 choices.
    hints.ai_socktype = SOCK_STREAM; // We want a TCP socket.
    hints.ai_flags = AI_PASSIVE;     // All interfaces.
    char port[16]={0};
    snprintf(port,sizeof(port),"%d",in_port);    //数填写的套接口地址结构,port可以是http ftp字符
    retval = getaddrinfo(NULL, port, &hints, &result);    if (retval != 0)
    {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(retval));        return RET_VAL_FAILED;
    }    for (rp = result; rp != NULL; rp = rp->ai_next)
    {
        server_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);        if (server_fd == RET_VAL_FAILED)            continue;        ////设置端口复用,防止被占用,服务器退出时再次绑定不会影响再次绑定
        int opt = 1;        if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(int)) < 0)
        {
            printf(" set server_fd option SO_REUSEADDR error !");              goto failed;
        }        //bind soucket
        retval = bind(server_fd, rp->ai_addr, rp->ai_addrlen);        if (retval == 0)
        {            // We managed to bind successfully!
            break;
        }        goto failed;
    }    if (rp == NULL)
    {
        fprintf(stderr, "server_fd could not  bind\n");          goto failed;
    }
    freeaddrinfo(result);    //设置不阻塞模式
    int flags, s;
    flags = fcntl(server_fd, F_GETFL, 0);    if (flags == RET_VAL_FAILED)
    {
        perror("server_fd fcntl set error ,F_GETFL\n");          goto failed;
    }

    flags |= O_NONBLOCK;
    s = fcntl(server_fd, F_SETFL, flags);    if (s == RET_VAL_FAILED)
    {
        perror("server_fd fcntl set error F_SETFL\n");       goto failed;
    }    //绑定端口号
    retval = listen(server_fd, SOMAXCONN);    if (retval == RET_VAL_FAILED)
    {
        perror("server_fd listen error\n");        goto failed;
    }
    client_fds[0].fd=server_fd;//监听fd
    client_fds[0].events = POLLRDNORM;    return server_fd;
failed:
    close(server_fd);    return RET_VAL_FAILED;
}void *poll_loop(void *arg)
{    int listen_fd = *((int *)arg);    int max_fd = 0;    int retval;    int wait_timeout_ms=1000;//1s    //select多路复用
    while (1)
    {
        max_fd = listen_fd;        for (int i = 1; i < MAX_SELECT_CLIENT_NUM; i++)
        {            if (client_fds[i].fd > 0)
            {                if (max_fd <= client_fds[i].fd)
                {
                    max_fd = client_fds[i].fd;
                }
            }
        }
        retval = poll(client_fds,max_fd+1,wait_timeout_ms);        if (retval > 0)
        {            if ((client_fds[0].revents)&POLLRDNORM) //有新的客户端连接            {                struct sockaddr_in client_address;
                socklen_t address_len;                int client_sock_fd = accept(listen_fd, (struct sockaddr *)&client_address, &address_len);                if (client_sock_fd > 0)
                {                    int flags = RET_VAL_FAILED;                    //一个客户端到来分配一个fd,超过CLI_NUM以后跳出for循环,flags重新被赋值为RET_VAL_FAILED
                    for (int i = 1; i < MAX_SELECT_CLIENT_NUM; i++)
                    {                        if (client_fds[i].fd <= 0)
                        {
                            flags = i;
                            client_fds[i].fd = client_sock_fd;
                            client_fds[i].events= POLLRDNORM;
                            client_fds[i].revents = 0;//目前还没有任何事件返回,置为零
                            break;
                        }
                    }                    if (flags != RET_VAL_FAILED)
                    {
                        printf("new user client[%d]=%d add sucessfully!\n", flags, client_sock_fd);
                    }                    else //flags=RET_VAL_FAILED                    {
                        printf("the client nums is more than %d, can't login.close now!\n", MAX_SELECT_CLIENT_NUM);
                        close(client_sock_fd);
                    }
                }
            }            else
            {                //deal with the message
                for (int i = 1; i < MAX_SELECT_CLIENT_NUM; i++)
                {                        if ((client_fds[i].revents)&POLLRDNORM)
                        {                            char rcv_buff[MAX_RCV_BUFF_SIZE] = {0};                            int len = recv(client_fds[i].fd, rcv_buff, MAX_RCV_BUFF_SIZE, MSG_DONTWAIT);                            if (len > 0)
                            {                                int send_len = send(client_fds[i].fd, rcv_buff, len, MSG_DONTWAIT);
                                printf("message form client[%d]=%d,rcv len%d,send_len=%d\n", i, client_fds[i].fd, len, send_len);                                if (NULL != callbackfunc)
                                    callbackfunc(client_fds[i].fd, rcv_buff, len);                                continue;
                            }                            else if (len <= 0)
                            {                                if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
                                {                                    continue;
                                }
                                close(client_fds[i].fd);
                                client_fds[i].fd = -1;                                if (0 == len)
                                {
                                    printf("clien[%d] exit by itself!bye\n", i);
                                }                                else
                                {                                    //printf("clien[%d] is error!\n", i);                                }
                            }
                        }
                    
                }
            }
        }        if (retval < 0)
        {
            printf("poll error:%s\n", strerror(errno));            continue;
        }        else if (0 == retval)
        {            //printf("poll  timeout!\n");
            continue;
        }
    }
}void get_all_client(int out_clients[], const int size)
{    if (size > MAX_SELECT_CLIENT_NUM)
    {        for (int i = 0; i < MAX_SELECT_CLIENT_NUM; i++)
        {
            out_clients[i] = client_fds[i].fd;
        }
    }    else
    {        for (int i = 0; i < size; i++)
        {
            out_clients[i] = client_fds[i].fd;
        }
    }
}

server.h

#ifndef _pollio_server_H_#define _pollio_server_H_#ifdef __cplusplus 
extern "C"{#endif#ifndef  RET_VAL_SUCCESS 
#define  RET_VAL_SUCCESS (0)#endif#ifndef  RET_VAL_FAILED#define  RET_VAL_FAILED (-1)#endif#ifndef  MAX_SELECT_CLIENT_NUM#define  MAX_SELECT_CLIENT_NUM (50200)//数量无限制#endif#ifndef  MAX_RCV_BUFF_SIZE#define  MAX_RCV_BUFF_SIZE (4096*100)#endiftypedef struct _client_info_t
{    int fd;    int login_id;
}client_info_t;
typedef int (*reg_rcv_handle_call_back)(const int fd,const void *data,const int data_len);/*****************************************
 *func:server_init
 *describe:初始化服务器端
 *para: port 端口号
 *return:成功则返回0,error则返回-1
 *****************************************/int server_init(const int port,const reg_rcv_handle_call_back func);void get_all_client(int out_clients[],const int size);

#ifdef __cplusplus
}#endif#endif

main.c

#include#include #include <netinet/in.h>#include #include #include #include #include #include #include #include <sys/select.h>#include #include <string.h>#include #include #include #include "pollio_server.h"int sysUsecTime(char *pTime)
{ struct timeval tv; struct timezone tz; struct tm *p;
 gettimeofday(&tv, &tz);
 p = localtime(&tv.tv_sec);
 sprintf(pTime, "%04d-%02d-%02d-%02d:%02d:%02d:.%06ld", 1900 + p->tm_year, 1 + p->tm_mon, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec, tv.tv_usec); return 0;
}int handle_call_back(const int fd,const void *data,const int data_len)
{  static int cat_fd=5;  if(cat_fd==fd)
 {   char pTime[64]={0};
   sysUsecTime(pTime);
   printf("[%s]:fd=%d,len=%d\n",pTime,fd,data_len);
 }

}int main(int argc, char **argv)
{
    server_init(11111, handle_call_back);    int out_clients[1024]={0};    while (1)
    {        #if 0
        get_all_client(out_clients, 1024);        int print_flag=-1; 
        for (int i = 0; i < 1024; i++)
        {            if (out_clients[i] > 0)
            {                if(print_flag<0)
                    printf("client:");
                print_flag=1;
                printf("client[%d]=%d ",i ,out_clients[i]);
            }
        }           if(print_flag>0)
        printf("\n");        #endif
        sleep(5);
    }
}

 

2.epoll io 服务器(单线程)

#include#include <netinet/in.h>#include #include #include #include #include #include #include #include <sys/select.h>#include #include <string.h>#include #include #include #include #include /* inet(3) functions */#include #include #include "epollio_server.h"#define MAX_EPOOL_EVENTS (1024)static int client_fds[MAX_SELECT_CLIENT_NUM] = {0};static reg_rcv_handle_call_back callbackfunc = NULL;static int server_create(int port);static int epoll_init(int listen_fd);static void *epool_loop(void *arg);/*****************************************
 *func:server_init
 *describe:初始化服务器端
 *para: port 端口号
 *return:成功则返回0,error则返回RET_VAL_FAILED
 *****************************************/int server_init(const int port, const reg_rcv_handle_call_back func)
{    int cnt = 10;    static int ret_fd = -1;    int ret;    for (int i = 0; i < MAX_SELECT_CLIENT_NUM; i++)
    {
        client_fds[i] = -1;
    }    while (cnt--)
    {
        ret_fd = server_create(port);        if (RET_VAL_FAILED != ret_fd)
        {            if (NULL == callbackfunc)
                callbackfunc = func;            break;
        }
        usleep(100 * 1000);
    }    if (ret_fd < 0)
    {        return RET_VAL_FAILED;
    }
    cnt = 10;    while (cnt--)
    {        int ret =epoll_init(ret_fd);        if (RET_VAL_FAILED != ret)
        {
            printf("init done ,wait client connect...\n");            return RET_VAL_SUCCESS;
        }
        usleep(100 * 1000);
    }    return RET_VAL_FAILED;
}/*****************************************
 *func:server_create
 *describe:创建监听的fd
 *para: port 端口号
 *return: 成功则返回监听的fd,error则返回RET_VAL_FAILED
 *****************************************/int server_create(int in_port)
{    struct addrinfo hints;    struct addrinfo *result, *rp;    int server_fd, retval;
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;     // Return IPv4 and IPv6 choices.
    hints.ai_socktype = SOCK_STREAM; // We want a TCP socket.
    hints.ai_flags = AI_PASSIVE;     // All interfaces.
    char port[16] = {0};
    snprintf(port, sizeof(port), "%d", in_port);    //数填写的套接口地址结构,port可以是http ftp字符
    retval = getaddrinfo(NULL, port, &hints, &result);    if (retval != 0)
    {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(retval));        return RET_VAL_FAILED;
    }    for (rp = result; rp != NULL; rp = rp->ai_next)
    {
        server_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);        if (server_fd == RET_VAL_FAILED)            continue;        ////设置端口复用,防止被占用,服务器退出时再次绑定不会影响再次绑定
        int opt = 1;        if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(int)) < 0)
        {
            printf(" set server_fd option SO_REUSEADDR error !");            goto failed;
        }        //bind soucket
        retval = bind(server_fd, rp->ai_addr, rp->ai_addrlen);        if (retval == 0)
        {            // We managed to bind successfully!
            break;
        }        goto failed;
    }    if (rp == NULL)
    {
        fprintf(stderr, "server_fd could not  bind\n");        goto failed;
    }
    freeaddrinfo(result);    //设置不阻塞模式
    int flags, s;
    flags = fcntl(server_fd, F_GETFL, 0);    if (flags == RET_VAL_FAILED)
    {
        perror("server_fd fcntl set error ,F_GETFL\n");        goto failed;
    }

    flags |= O_NONBLOCK;
    s = fcntl(server_fd, F_SETFL, flags);    if (s == RET_VAL_FAILED)
    {
        perror("server_fd fcntl set error F_SETFL\n");        goto failed;
    }    //绑定端口号
    retval = listen(server_fd, SOMAXCONN);    if (retval == RET_VAL_FAILED)
    {
        perror("server_fd listen error\n");        goto failed;
    }
    client_fds[0] = server_fd; //监听fd
    return server_fd;
failed:
    close(server_fd);    return RET_VAL_FAILED;
}int epoll_init(int listen_fd)
{    static int listenfd=0;
    listenfd=listen_fd;    int cnt = 10;    int ret = -1;    while (cnt--)
    {
        pthread_t loop_thread_id;
        ret = pthread_create(&loop_thread_id, NULL, epool_loop, &listenfd);        if (0 == ret)
        {
            pthread_detach(loop_thread_id);            return RET_VAL_SUCCESS;
        }
        usleep(10 * 1000);
    }    return RET_VAL_FAILED;
}void *epool_loop(void *arg)
{    int listen_fd = *((int *)arg);    int max_fd = 0;    int retval;    int wait_timeout_ms = 1000; //1s

    int ret = -1;    int efd;    struct epoll_event event;    struct epoll_event *events;    //除了参数size被忽略外,此函数和epoll_create完全相同
    efd = epoll_create1(0);    if (efd == -1)
    {
        perror("epoll_create error,exit");
            exit(-1);
    }    event.data.fd = *((int *)arg);    event.events = EPOLLIN | EPOLLET; //读入,边缘触发方式
    ret = epoll_ctl(efd, EPOLL_CTL_ADD, listen_fd, &event);    if (ret == -1)
    {
        perror("epoll_ctl error,exit");
        exit(-1);
    }    /* Buffer where events are returned */
    events = calloc(MAX_EPOOL_EVENTS, sizeof(event));    //select多路复用
    while (1)
    {        int n = 0, i = 0;
        n = epoll_wait(efd, events, MAX_EPOOL_EVENTS, -1);        for (i = 0; i < n; i++)
        {            if ((events[i].events & EPOLLIN))
            {                if (listen_fd != events[i].data.fd)
                {                    int len=-1;                    do
                    {                        char rcv_buff[MAX_RCV_BUFF_SIZE] = {0};
                        len = recv(events[i].data.fd, rcv_buff, MAX_RCV_BUFF_SIZE, MSG_DONTWAIT);                        if (len > 0)
                        {                            int send_len = send(events[i].data.fd, rcv_buff, len, MSG_DONTWAIT);                            //printf("message form client=%d,rcv len%d,send_len=%d\n", events[i].data.fd, len, send_len);
                            if (NULL != callbackfunc)
                                callbackfunc(events[i].data.fd, rcv_buff, len);                            continue;
                        }                        else if (len <= 0)
                        {                            if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
                            {                                continue;
                            }
                            close(events[i].data.fd);                            for(int k=0;k<MAX_SELECT_CLIENT_NUM;k++)
                            {                                if( client_fds[k]==events[i].data.fd)
                                {
                                    client_fds[k]=-1;                                    break;
                                }
                            }                            if (0 == len)
                            {
                                printf("clien[%d] exit by itself!bye\n", events[i].data.fd);
                            }                            else
                            {
                                printf("clien[%d] is error!\n", events[i].data.fd);
                            }
                            events[i].data.fd = -1;
                        }

                    } while (len==MAX_RCV_BUFF_SIZE);
                }                else
                {                    struct sockaddr in_addr;
                    socklen_t in_len;                    int infd;                    char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
                    in_len = sizeof in_addr;
                    infd = accept(listen_fd, &in_addr, &in_len);                    if (infd == -1)
                    {                        if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
                        {                            /* We have processed all incoming connections. */
                            continue;
                        }                        else
                        {
                            perror("accept error\n");                            continue;
                        }
                    }                    //将地址转化为主机名或者服务名
                    ret = getnameinfo(&in_addr, in_len, hbuf, sizeof hbuf, sbuf, sizeof sbuf, NI_NUMERICHOST | NI_NUMERICSERV); //主机地址和服务地址,flag参数:以数字名返回
                    if (ret == 0)
                    {
                        printf("Accepted connection on descriptor %d (host=%s, port=%s)\n", infd, hbuf, sbuf);
                    }                    else if (ret == -1)
                    {
                        perror("accept:getnameinfo error \n");
                        close(infd);                        continue;
                    }                    event.data.fd = infd;                    event.events = EPOLLIN | EPOLLET;
                    ret = epoll_ctl(efd, EPOLL_CTL_ADD, infd, &event);                    if (ret == -1)
                    {
                        perror("epoll_ctl error ,");
                        close(infd);                        continue;
                    }                    for(int k=0;k<MAX_SELECT_CLIENT_NUM;k++)
                    {                        if(-1== client_fds[k])
                        {
                            client_fds[k]=infd;                            break;
                        }
                       
                    }
                }
            }            else if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP))
            {                /* An error has occured on this fd, or the socket is not
                ready for reading (why were we notified then?) */
                fprintf(stderr, "epoll error\n");
                close(events[i].data.fd);                for(int k=0;k<MAX_SELECT_CLIENT_NUM;k++)
                {                    if( client_fds[k]==events[i].data.fd)
                    {
                        client_fds[k]=-1;                        break;
                    }
                }                continue;
            }
        }
    }    free(events);
    close(listen_fd);
}void get_all_client(int out_clients[], const int size)
{    if (size > MAX_SELECT_CLIENT_NUM)
    {        for (int i = 0; i < MAX_SELECT_CLIENT_NUM; i++)
        {
            out_clients[i] = client_fds[i];
        }
    }    else
    {        for (int i = 0; i < size; i++)
        {
            out_clients[i] = client_fds[i];
        }
    }
}

3.测试客户端

#include#include #include <string.h>#include #include #include #include <netinet/in.h>#include #include #include #include #include <netinet/in.h>#include #include #include #include #include #include #include #include #include <sys/select.h>#include #include <string.h>#define MAXLINE 4096*100int sockfd[1000] = {0};char recvLine[MAXLINE], sendLine[MAXLINE];void *recv_func(void *arg)
{
    fd_set ser_fdset;    int max_fd =2;    struct timeval mytime;    while (1)
    {
         max_fd =2;
        FD_ZERO(&ser_fdset);        for (int i = 0; i < 1000; i++)
        {            if (sockfd[i] > 0)
            {
                FD_SET(sockfd[i], &ser_fdset);                if(sockfd[i]>max_fd)
                {
                    max_fd=sockfd[i];
                }
                
            }
            
        }
        mytime.tv_sec = 1;        int ret = select(max_fd+1, &ser_fdset, NULL, NULL, &mytime);        if (ret < 0)
        {
            perror("select failure\n");            continue;
        }        else if (ret == 0)
        {            //printf("time out! \n");
            continue;
        }        else
        {            for (int i = 0; i < 1000; i++)
            {                if (FD_ISSET(sockfd[i], &ser_fdset))
                {                    int len = -1;
                    memset(recvLine, 0, sizeof(recvLine));                    if (sockfd[i] > 0)
                    {
                        len = recv(sockfd[i], recvLine, sizeof(recvLine), MSG_DONTWAIT);
                        printf("recv len=%d\n", len);
                    }
                }
            }
        }
    }
}int main(int argc, char **argv)
{    int n;    struct sockaddr_in servaddr;    while (1)
    {        static int i = 0;        if ((sockfd[i] = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
            printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);            return 0;
        }
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(11111);        if (inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr) <= 0)
        {
            printf("inet_pton error for 127.0.0.1\n");            return 0;
        }        if (connect(sockfd[i], (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
        {            //客户端需要调用connect()连接服务器,connect和bind的参数形式一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址。connect()成功返回0,出错返回-1
            printf("connect error: %s(errno: %d)\n", strerror(errno), errno);
            sockfd[i]=-1;
        }
        usleep(1 * 100);
        i++;        if (i >= 1000)            break;
    }
    printf("send msg to server: \n");
    memset(sendLine, 0, sizeof(sendLine));    for (int i = 0; i < MAXLINE; i++)
    {
        sendLine[i] = 'c';
    }

    sendLine[MAXLINE - 1] = 0;    int len = strlen(sendLine);
    pthread_t recv_pth;
    pthread_create(&recv_pth, NULL, recv_func, NULL);
    pthread_detach(recv_pth);    while (1)
    {        for (int i = 0; i < 1000; i++)
        {            if (sockfd[i] > 0)
            {            
                int ret = send(sockfd[i], sendLine, len, MSG_DONTWAIT);                if (ret < 0)
                {
                    printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
                }
                printf("send len=%d\n", ret);
                usleep(3);
            }
            
        }
    }    for (int i = 0; i < 1000; i++)
        close(sockfd[i]);    return 0;
}

测试结果:

1000个客户端连接,每3微秒发100K数据

poll epoll简单的回显服务器_poll io服务器

poll epoll简单的回显服务器_poll io服务器_02

100个客户端连接,每3微秒发100K数据

 poll epoll简单的回显服务器_poll io服务器_03

 

poll epoll简单的回显服务器_poll io服务器_04