epoll server实例

ServerEpoll

epoll [enhancement poll]

this is a async server base on epoll at Linux
这是一个在Linux系统上基于epoll机制的异步服务器

class EpollService 主要功能就是,创建一个后台服务器监听指定端口号,并且接受来自客户端的连接请求。

EpollService 使用两个 epoll 机制来完成对客户端连接的监听以及对客户端数据的读取。
其中 监听端口的线程运行在主线程(listen thread)中,在开启监听epoll前开启一个读取线程(receive thread)。

监听线程 和 读取线程之间使用管道 pipe 完成数据的传输工作。即读取到新的客户端连接之后,将相关信息打包成 pipemsg(管道数据)通过管道pipe机制发送给读取线程。

读取线程主要监听两种事件:
1、管道的读取事件:
从管道读取到新的pipemsg后将新连接的相关信息存入 std::map fd_info 中,并将该clientfd的读取事件添加到读取epollfd中。
2、客户端的读取事件
从客户端读取到数据后,对数据进行处理。目前打印出相关信息。
如果客户端主动断开连接,读取后的数据长度为0,并将相关信息从std::map fd_info中移除。

流程图

epoll server实例_第1张图片

源码

#ifndef EPOLL_SERVER

#include "Service.h"
#include 
#include 
#include 

class EpollService :public Service
{
private:
    int listenEpollfd;
    int listenfd;
    int port_;
    int backlog_;
    struct epoll_event server_ev;
    struct sockaddr_in svraddr;
    bool service_;

    int pipe_[2];
    pthread_t receiveTrd;
    bool receiveLoopStart;
    std::map fd_info;

    int receiveEpollfd;

public:
    explicit EpollService(int port,int backlog = 10);
    ~EpollService();
    void startService() override;
    void stopService() override;

private:
    bool createListen();
    bool reListen();

private:
    bool startReceiveThread();
    static void *ReceiveLoop(void *args);
};

#endif // !EPOLL_SERVER

#include "EpollService.h"
#include "Tool.h"
#include 
#include 
#include 
#include  // inet_ntoa
#include 
#include 

EpollService::EpollService(int port, int backlog)
    : port_(port),
      backlog_(backlog),
      service_(false),
      receiveLoopStart(false)
{
    stdin_ = STDIN_FILENO;
    listenEpollfd = epoll_create(1024);
    receiveEpollfd = epoll_create(1024);

    epoll_event stdin_ev;
    stdin_ev.data.fd = stdin_;
    stdin_ev.events = EPOLLIN;
    epoll_ctl(listenEpollfd,EPOLL_CTL_ADD,stdin_,&stdin_ev);
    // create pipe for transpotr client message
    pipe(pipe_);
    createListen();
}

EpollService::~EpollService()
{
    if(receiveLoopStart == true){
        printf("join receive thread\n");
        if(0 !=pthread_join(receiveTrd,NULL)){
            printf("join reveive thread fail\n");
        }
    }

    for(auto iter = fd_info.begin();iter != fd_info.end();iter++){
        close(iter->first);
        printf("close [%d]\n",iter->first);
    }

    //关闭监听描述字
    if (listenfd > 0){
        close(listenfd);
    }
    //关闭创建的epoll
    if (listenEpollfd > 0){
        close(listenEpollfd);
    }

}

bool EpollService::createListen()
{
    //创建监听socket
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0){
        printf("createListen, socket fail: [%d]\n", listenEpollfd);
        close(listenEpollfd);
        return false;
    }
    //把监听socket设置为非阻塞方式
    setnonblocking(listenfd);
    //设置监听socket为端口重用
    setreuseaddr(listenfd);
    server_ev.data.fd = listenfd;
    // 使用 边沿触发模式处理监听事件
    server_ev.events = EPOLLIN | EPOLLET;
    if (0 != epoll_ctl(listenEpollfd, EPOLL_CTL_ADD, listenfd, &server_ev)){
        printf("createListen, epoll_ctl fail: listenEpollfd [%d] listenfd [%d]\n", listenEpollfd, listenfd);
        close(listenfd);
        close(listenEpollfd);
        return false;
    }
    bzero(&svraddr, sizeof(svraddr));
    svraddr.sin_family = AF_INET;
    svraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    svraddr.sin_port = htons(port_);
    bind(listenfd, (sockaddr *)&svraddr, sizeof(svraddr));
    //监听,准备接收连接
    if (0 != listen(listenfd, backlog_)){
        printf("createListen, listen fail\n");
        close(listenfd);
        close(listenEpollfd);
        return false;
    }
}

void EpollService::startService()
{
    service_ = true;

    // start receive thread loop
    if(startReceiveThread() == false){
        receiveLoopStart = false;
        printf("start receive thread fail\n");
        return;
    }else{
        receiveLoopStart = true;
    }

    printf("start epoll server service\n");
    const int MAXEVENTS = 1024;           //最大事件数
    struct epoll_event events[MAXEVENTS]; //监听事件数组
    socklen_t clilen;
    int nfds;
    const char *response = "this is message from epoll server";
    while (service_)
    {
        nfds = epoll_wait(listenEpollfd, events, MAXEVENTS, -1);
        for (int i = 0; i < nfds && service_; ++i)
        {
            if (events[i].data.fd == listenfd) //是本监听socket上的事件
            {
                printf("AcceptThread, events: [%d]\n", events[i].events);
                if (events[i].events & EPOLLIN) //有连接到来
                {
                    // loop accept
                    do{
                        struct sockaddr_in cliaddr;
                        clilen = sizeof(struct sockaddr);
                        int connfd = accept(listenfd, (sockaddr *)&cliaddr, &clilen);
                        if (connfd > 0)
                        {
                            printf("AcceptThread, accept: [%d],connect: [%s]:[%d]\n", connfd, inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));
                            // 传递
                            static pipemsg_t msg;
                            msg.op = 0;
                            msg.fd = connfd;
                            msg.addr = cliaddr;
                            write(pipe_[1],&msg,sizeof(msg));
                            write(connfd, response, strlen(response));
                        }else{
                            printf("AcceptThread, accept fail: connfd = [%d]\n", connfd);
                            if (errno == EAGAIN) //没有连接需要接收了
                            {
                                break;
                            }
                            else if (errno == EINTR) //可能被中断信号打断,,经过验证对非阻塞socket并未收到此错误,应该可以省掉该步判断
                            {
                                ;
                            }
                            else //其它情况可以认为该描述字出现错误,应该关闭后重新监听
                            {
                                // 如果重启失败,则退出
                                if(false == reListen()){
                                    service_ = false;
                                    return;
                                }
                            }
                        }
                    } while (service_);
                    // end loop accept
                }else if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP){
                    if(false == reListen()){
                        service_ = false;
                        return;
                    }
                }
            }// end if

            if(events[i].data.fd == stdin_){
                char buffer[80];
                bzero(buffer,sizeof(buffer));
                int length = read(stdin_,buffer,sizeof(buffer));
                buffer[length-1] = '\0';
                if(strncmp(buffer,"over",4) == 0){
                    printf("from stdin read over\n");
                    stopService();
                }else if(strncmp(buffer,"list",4)==0){
                    printf("list begin\n");
                    for(auto iter=fd_info.begin();iter!=fd_info.end();++iter){
                        printf("infomation: [%d] [%s]:[%d]\n",iter->first,inet_ntoa(iter->second.addr.sin_addr),ntohs(iter->second.addr.sin_port));
                    }
                    printf("list end\n");
                }
            }
        }// end for
    }
}

void
EpollService::stopService(){
    if(receiveLoopStart){
        printf("stop service pthread cancel\n");
        pthread_cancel(receiveTrd);
    }
    service_ = false;
}

bool 
EpollService::reListen()
{
    //此时说明该描述字已经出错了,需要重新创建和监听
    close(listenfd);
    epoll_ctl(listenEpollfd, EPOLL_CTL_DEL, listenfd, &server_ev);
    return createListen();
}

bool 
EpollService::startReceiveThread(){
    //创建线程时采用的参数
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);                 //设置绑定的线程,以获取较高的响应速度
    printf("start receive thread\n");

    epoll_event readPipeEV;
    readPipeEV.data.fd = pipe_[0];
    // read pipe need ET mode ???
    readPipeEV.events = EPOLLIN;
    // add read pipe on receiveEpollfd
    int ret = epoll_ctl(receiveEpollfd,EPOLL_CTL_ADD,pipe_[0],&readPipeEV);
    if(ret != 0){
        printf("add epollin event on read pipe fail\n");
        return false;
    }
    // use static method for start_routine
    if(0 !=pthread_create(&receiveTrd,&attr,ReceiveLoop,this)){
        printf("create receive thread fail\n");
        return false;
    }
    return true;
}

// in static method we can use private argument in this class
void *
EpollService::ReceiveLoop(void *args){
    EpollService *server = (EpollService *)args;
    int nfds = 0;
    int ret = 0;
    pipemsg_t msg;
    const int maxEvents = 1024;
    epoll_event events[maxEvents];
    const int bufferSize = 1024;
    char buffer[bufferSize];
    bzero(buffer,bufferSize);
    while(server->service_){
        
        nfds = epoll_wait(server->receiveEpollfd,events,maxEvents,-1);
        if(nfds>0){
            for(int i=0;iservice_;++i){
                if(events[i].data.fd == server->pipe_[0]){
                    // read new client from pipe and add to receiveEpollfd
                    int length = read(server->pipe_[0],&msg,sizeof(msg));
                    if(length == sizeof(msg)){
                        printf("receive thread read [%d] from pipe connection [%s]:[%d]\n",msg.fd,inet_ntoa(msg.addr.sin_addr),ntohs(msg.addr.sin_port));
                        // add to receiveEpollfd
                        epoll_event clientEv;
                        clientEv.data.fd = msg.fd;
                        clientEv.events = EPOLLIN;
                        ret = epoll_ctl(server->receiveEpollfd,EPOLL_CTL_ADD,msg.fd,&clientEv);
                        if(ret != 0){
                            printf("add client [%d] on receive epollfd fail\n",msg.fd);
                            // ignore the later insert into map
                            continue;
                        }
                        // msg => clientInfo_t => std::map
                        static clientInfo_t client_info;
                        client_info.addr = msg.addr;
                        server->fd_info[msg.fd] = client_info;
                    }else{
                        printf("read from pipe and length = [%d]\n",length);
                    }
                }else{
                    // read from client
                    bzero(buffer,bufferSize);
                    int length = read(events[i].data.fd,buffer,bufferSize);
                    if(length>0){
                        printf("receive from client [%d] , message is :[%s]\n",events[i].data.fd,buffer);
                    }else if(length == 0){
                        printf("receive from client but length is [%d], client [%d] close connection\n",length,events[i].data.fd);
                        close(events[i].data.fd);
                        // remove from receiveEpollfd
                        epoll_event clientEv;
                        clientEv.data.fd = msg.fd;
                        clientEv.events = EPOLLIN;
                        epoll_ctl(server->receiveEpollfd,EPOLL_CTL_DEL,events[i].data.fd,&clientEv);
                        server->fd_info.erase(events[i].data.fd);
                    }else{
                        printf("receive from client but length is [%d]\n",length);
                    }
                }

            }// end for loop
        }// end nfds > 0
        else{
            printf("nfds = [%d]\n",nfds);
        }

    }

    return NULL;
}

你可能感兴趣的:(C++,Linux,epoll,server)