Linux epoll服务端

Linux epoll服务端

源代码:epollServer.cpp

/*******************************************************************************
* epollServer.cpp
*
* Create on 2018-10-6
*   Author: yanxinchun
*
* g++ -o epollServer epollServer.cpp
*******************************************************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#include <vector>
#include <algorithm>
#include <assert.h>

#define POLL_MAX_CN (1024)
#define ERR_EXIT(m) \
    do {            \
        perror(m);    \
        exit(EXIT_FAILURE);    \
    } while(0)

typedef std::vector<struct epoll_event>    EventList;

typedef struct _packet_st {
    int len;
    char buf[1024];
} PACKET_st;

ssize_t readn(int fd, void *buf, size_t count)
{
    size_t nleft = count;
    ssize_t nread;
    char *bufp = (char*)buf;
    
    while(nleft > 0){
        if(nread = read(fd, bufp, nleft) < 0) {
            if(errno == EINTR) {
                continue;
            }
            return -1;
        }else if(nread == 0) {
            return count - nleft;
        }
        
        bufp += nread;
        nleft -= nread;
    }
    
    return count;
}

ssize_t writen(int fd, const void *buf, size_t count)
{
    size_t nleft = count;
    ssize_t nwritten;
    char *bufp = (char*)buf;
    
    while(nleft > 0) {
        if(nwritten = write(fd, bufp, nleft) < 0) {
            if(errno == EINTR) {
                continue;
            }
            return -1;
        }else if(nwritten == 0) {
            continue;
        }
        
        bufp += nwritten;
        nleft -= nwritten;
    }
    
    return count;
}

/**
 * * activate_nonblock - 设置I/O为非阻塞模式
 * * @fd: 文件描述符
 * */
void activate_nonblock(int fd)
{
    int ret;
    int flags = fcntl(fd, F_GETFL);
    if(flags == -1) {
         ERR_EXIT("fcntl fail!");
    }
    
    flags |= O_NONBLOCK;
    ret = fcntl(fd, F_SETFL, flags);
    if(ret == -1) {
         ERR_EXIT("fcntl fail!");
    }
}

/**
 * * deactivate_nonblock - 设置I/O为阻塞模式
 * * @fd: 文件描述符
 * */
void deactivate_nonblock(int fd)
{
    int ret;
    int flags = fcntl(fd, F_GETFL);
    if(flags == -1) {
         ERR_EXIT("fcntl fail!");
    }
    
    flags &= ~O_NONBLOCK;
    ret = fcntl(fd, F_SETFL, flags);
    if(ret == -1) {
         ERR_EXIT("fcntl fail!");
    }
}

void handle_sigpipe(int sig)
{
    printf("[sigpipe] recv a sig=%d\n", sig);
}

void handle_sigchld(int sig)
{
    while(waitpid(-1, NULL, WNOHANG) > 0) ;
}

int main()
{
    int listenfd = -1;
    int epollfd = -1;
    int reuse = 0;
    struct sockaddr_in servaddr;
    std::vector<int> clients;
    struct epoll_event event;

    //往一个已经关闭的socket写入数据会触发SIGPIPE
    signal(SIGPIPE, handle_sigpipe);

    /* 在一个进程停止或终止时,将SIGPIPE信号发送给其父进程,按系统默认将忽略此信号,
       如果父进程希望被告知其子系统的这种状态,则应捕捉此信号。*/
    signal(SIGCHLD, handle_sigchld);

    if((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
        ERR_EXIT("socket fail!");
    }
    
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(8914);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    
    reuse = 1;
    if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
        ERR_EXIT("setsockopt fail!");
    }
    
    if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
        ERR_EXIT("bind fail!");
    }
    
    if(listen(listenfd, SOMAXCONN) < 0) {
        ERR_EXIT("listen fail!");
    }

    epollfd = epoll_create1(EPOLL_CLOEXEC); // 用于设置改描述符的close-on-exec(FD_CLOEXEC)标志
    assert(epollfd != -1);    // 成功是返回非负的文件描述符,失败是返回-1

    event.data.fd = listenfd;
    event.events = EPOLLIN | EPOLLET;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event);
    
    EventList events(16);
    struct sockaddr_in peeraddr;
    socklen_t peerlen = sizeof(peeraddr);
    
    int i;
    PACKET_st recvbuf;
    
    int nready = 0;
    int conn;
    while(1){
        nready = epoll_wait(epollfd, &*events.begin(), static_cast<int>(events.size()), -1);
        if(nready == -1) {
            if(errno == EINTR) {
                continue;
            }
            ERR_EXIT("epoll_wait fail!");
        }
        
        if(nready == 0) {
            continue;
        }
        
        if((size_t)nready == events.size()) {
            events.resize(events.size() * 2);
        }
        
        for(i = 0; i < nready; ++i) {
            if(events[i].data.fd == listenfd) {
                peerlen = sizeof(peeraddr);
                conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);
                if(conn == -1) {
                    ERR_EXIT("accept fail!");
                }
                
                printf("accept ip=%s, port=%d, conn=%d\n", 
                        inet_ntoa(peeraddr.sin_addr), 
                        ntohs(peeraddr.sin_port), 
                        conn);

                clients.push_back(conn);
                activate_nonblock(conn);
                
                event.data.fd = conn;
                event.events = EPOLLIN | EPOLLET;
                epoll_ctl(epollfd, EPOLL_CTL_ADD, conn, &event);
            } else if(events[i].events & EPOLLIN) {
                conn = events[i].data.fd;
                if(conn < 0) {
                    continue;
                }
                
                int n;
                int ret;
                memset(&recvbuf, 0, sizeof(recvbuf));
                ret = read(conn, &recvbuf.len, 4);
                if(ret == -1) {
                    ERR_EXIT("ser read fail 11!");
                } else if(ret < 4) {
                    printf("client close, ret = %d\n", ret);
                    shutdown(conn, SHUT_WR);
                    event = events[i];
                    epoll_ctl(epollfd, EPOLL_CTL_DEL, conn, &event);
                    clients.erase(std::remove(clients.begin(), clients.end(), conn), clients.end());
                } else {
                    n = ntohl(recvbuf.len);
                    ret = read(conn, &recvbuf.buf, n);
                    if(ret == -1) {
                        ERR_EXIT("ser read fail 22!");
                    } else if(ret < n) {
                        printf("client close 22, ret = %d\n", ret);
                        shutdown(conn, SHUT_WR);
                        event = events[i];
                        epoll_ctl(epollfd, EPOLL_CTL_DEL, conn, &event);
                        clients.erase(std::remove(clients.begin(), clients.end(), conn), clients.end());
                    }
                    
					printf("recv: len=%d, ctx=%s", ntohl(recvbuf.len), recvbuf.buf);
                    //fputs(recvbuf.buf, stdout);
					
					printf("reply: len=%d, ctx=%s\n", ntohl(recvbuf.len), recvbuf.buf);
                    write(conn, &recvbuf, 4 + n);
                }
            }
        }
    }
    
    close(listenfd);
    return 0;
}

编译运行:

[root@QIANZI-BASE keke]# g++ -o epollServer epollServer.cpp

猜你喜欢

转载自blog.csdn.net/qian_feifei/article/details/83002559