一个简单的epoll使用例子

核心代码,所有代码在这里下载

#include "unixnet.h"
#include "chat.h"
#include "sys/epoll.h"

/**
 * 改进说明:使用epoll进行事件回调,多用户可以处于输入命令状态(不再阻塞在登陆处)
 */

int main (int argc, char *argv[])
{
    int listen_fd;
    socklen_t cli_len;
    struct sockaddr_in cli_addr,serv_addr;
    int ret,flags;
    int re_use_addr=1;
    char recv_buf[MAXLINE];
    int i;
    //epoll 描述符
    int efd;
    struct epoll_event event;
    struct epoll_event events[MAXUSERS];
    //初始化槽位
    for(i=0; i<MAXUSERS; i++)
    {
        chater[i].slot_status =SLOT_FREED ;
        chater[i].sock_fd =-1;
        chater[i].cmd .cmd_type =-1;
    }
    /*AF_INET指定ipv4,SOCK_STREAM制定流模式,0为tcp*/
    listen_fd = socket(AF_INET,SOCK_STREAM,0);
    if(listen_fd==-1)
    {
        perror("create listen fd");
        exit(1);
    }
    //服务器地址清零
    bzero(&serv_addr ,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    //接受所有ip的请求
    serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    //设置端口
    serv_addr.sin_port=htons(SERVER_PORT );
    setsockopt(listen_fd, SOL_SOCKET,SO_REUSEADDR,(void
    *)&re_use_addr,sizeof(int));
    //绑定描述符到端口
    ret=bind(listen_fd,(struct sockaddr
    *)&serv_addr,sizeof(serv_addr));
    if(ret<0)
    {
        perror("bind server port");
        exit(1);
    }
    //开始监听端口请求,并设置最大的请求队列
    listen(listen_fd,LISTENQ);
    //设置为非阻塞io
    if((flags=fcntl(listen_fd,F_GETFL,flags)<0))
    {
        perror("F_SETFL error");
    }
    //使用位域
    flags|=O_NONBLOCK;
    if(fcntl(listen_fd,F_SETFL,flags)<0)
    {
        perror("F_SETFL error");
    }
    //创建一个epoll句柄
    efd = epoll_create(MAXUSERS);
    //对连接套接字设置监听事件,并设置触发模式,最后加入到epoll中进行回调
    event.data.fd = listen_fd;
    event.events = EPOLLET| EPOLLIN;
    epoll_ctl(efd, EPOLL_CTL_ADD, listen_fd, &event);
    printf("listen_fd %d\n",listen_fd);
    //循环处理所有事件
    while(1){
        int n,i;
        //等待直到事件到来,规律性sleep,所有不会导致cpu 100%
        n = epoll_wait(efd, events, MAXUSERS,-1);
        for(i = 0;i < n;++i){
            //如果是一个异常事件,则释放资源
            if ((events[i].events & EPOLLERR) ||
                (events[i].events & EPOLLHUP) ||
                (!(events[i].events & EPOLLIN))) {
                fprintf (stderr, "epoll error\n");
                free_slot(findSlotIndexByConnFd(events[i].data.fd));
                epoll_ctl(efd,EPOLL_CTL_DEL,events[i].data.fd,NULL);
                continue;
                //此时代表获得了一个连接建立事件,可以创建一个连接,本质上也是一个EPOLLIN
            }else if(listen_fd == events[i].data.fd){
                int infd = -1;
                //accept接受连接直到失败(由于使用ET触发所以需要使用循环以保所有事件都得到处理)
                while((infd = accept(listen_fd,(struct sockaddr *)&cli_addr,&cli_len)) > 0){
                    //获得一个空槽,并将连接设置为非阻塞
                    get_free_slot(infd, "no_login");
                    event.data.fd = infd;
                    event.events = EPOLLIN | EPOLLET;
                    epoll_ctl(efd, EPOLL_CTL_ADD, infd, &event);
                    writen(infd,message[2],strlen(message [2]));
                    printf("accept one\n");
                }
                if(infd == -1){
                    printf("%d\n",errno);
                }
                //在这里处理退出事件|用户名登陆事件|正常的cmd命令
            }else{
                int infd = events[i].data.fd;
                int index = -1;
                char *ptr;
                index = findSlotIndexByConnFd(infd);
                //从内核读数据到用户缓冲区
                ptr=&(chater[index].buffer[chater[index].next_char]);
                if(index != -1){
                    while((n=read(infd ,ptr,1))==1)
                    {
                        if(*ptr=='\n')
                        {
                            *(ptr+1)='\0';
                            chater[index].next_char =0;
                            break;
                        }
                        //在这里丢弃过长的字符串
                        if(++chater[index].next_char ==MAXLINE)
                            --chater[index].next_char ;
                        else
                            ++ptr;
                    }
                    //退出事件:当n==0代表连接丢失,当n==-1且errno!=EWOULDBLOCK代表不是因为暂时没有数据抛出错误
                    if(n == 0 || (n == -1&&errno!=EWOULDBLOCK)){
                        free_slot(findSlotIndexByConnFd(events[i].data.fd));
                        epoll_ctl(efd,EPOLL_CTL_DEL,events[i].data.fd,NULL);
                        printf("user %s use slot_index %d logout\n\n",chater[index].user_id, index);
                        continue;
                    }
                    //登陆用户名
                    if(!chater[index].user_assigned){
                        copyStringSkipSpace(chater[index].user_id,chater[index].buffer);
                        chater[index].user_assigned = 1;
                        printf("user %s use slot_index %d login\n\n",chater[index].user_id, index);
                        //处理cmd.
                    }else{
                        handle_cmd(index);
                    }
                }
            }
        }
    }
}
posted @ 2016-06-08 20:56  fcat  阅读(574)  评论(0编辑  收藏  举报