zoukankan      html  css  js  c++  java
  • [转] linux epoll 问题小结

    1,server端的fd不需要设置et模式

    我们在创建socket成功后会有个listenfd,listenfd = socket(AF_INET, SOCK_STREAM, 0)

    然后会把这个fd加入epoll wait队列中,网上很多没有经过验证的代码是这样写的:

    ev.data.fd = listenfd;

    ev.events=EPOLLIN|EPOLLET;

    epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);

    这样会导致服务器在并发处理客户端连接时,丢失部分连接,或者说丢失epoll事件

    表现为:采用netstat查看网络,会看到Recv-Q大于0,但是程序执行不到accept代码段。

    这个也和具体内核有关系,但是最好代码方面不要这么写。

    正确的写法为:ev.events=EPOLLIN;   这里不用指定EPOLLET

    2,如果采用et模式,读的时候要读完数据

    3,要处理发送,接收函数的返回值和错误码。如:read,recv,send,write,errno

    write如果返回-1,可能是写缓冲满,需要等待out事件再写入

    下面贴个基本模型,代码不能运行,仅参考

    struct _sgc_epoll_socketserver_config_s{

    char * local_addr;// 监听的本地地址

    int port;// 监听的端口

    int epfd;// epoll 文件描述符

    size_t buffer_size;// 缓冲区大小,单位byte

    int maxclients;// 最多允许连接的客户端数

    sgc_epoll_client_new_handler_t handler_new;// 有新连接处理函数

    sgc_epoll_client_msg_handler_t hander_read;// 读取数据

    sgc_epoll_client_close_hander_t hander_close;// 断开

    };

    /**

     * 启动socket服务

     */

    void sgc_serversocket_start(sgc_epoll_socketserver_config_t *config)

    {

    int epfd, listenfd,connfd,readyfds,i,sockfd, clients, nreadbytes;

    int flag=1,len=sizeof(int);

    int cfg_sndbuff = 1024 * 1024;// 1M

    int cfg_revbuffer = 1024 * 1024; // 1M

    struct epoll_event ev, events[config->maxclients];

    struct sockaddr_in clientaddr;

    struct sockaddr_in serveraddr;

    socklen_t clilen = sizeof(struct sockaddr_in);

    if (config->handler_new == NULL ||

    config->hander_read == NULL){

    sgc_log_error("config error hand is NULL ");

    exit(1);

    }

    char * buffer = (char *)malloc(config->buffer_size);

    memset(buffer, 0, config->buffer_size);

    epfd = epoll_create(config->maxclients);

    config->epfd = epfd;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &flag, len))

    sgc_log_error("set sockopt SO_REUSEADDR error ");

    if (setsockopt(listenfd, SOL_SOCKET, SO_SNDBUF, &cfg_sndbuff, len))

    sgc_log_error("set sockopt SO_SNDBUF error ");

    if (setsockopt(listenfd, SOL_SOCKET, SO_SNDBUF, &cfg_revbuffer, len))

    sgc_log_error("set sockopt SO_SNDBUF error ");

    setnonblocking(listenfd);

    ev.data.fd = listenfd;

    ev.events=EPOLLIN;

    // 监听端口

    epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);

    bzero(&serveraddr, sizeof(serveraddr));

    serveraddr.sin_family = AF_INET;

    char *local_addr= config->local_addr;

    inet_aton(local_addr,&(serveraddr.sin_addr));

    serveraddr.sin_port=htons(config->port);

    if (bind(listenfd,(struct sockaddr *)&serveraddr, sizeof(serveraddr)))

    {

    sgc_log_error("bind fail %s:%d ", config->local_addr, config->port);

    exit(1);

    }

    sgc_log_debug("start listen %s:%d backlog:%d ", local_addr, config->port, LISTEN_BACKLOG);

    if (listen(listenfd, LISTEN_BACKLOG))

    {

    sgc_log_error("listen %s:%d max:%d error ", config->local_addr, config->port, config->maxclients);

    exit(1);

    }

    clients = 0;

    for (;;) {

    readyfds=epoll_wait(epfd,events,config->maxclients,EPOLL_WAIT_TIMEOUT);

    if (readyfds < 0){

    sgc_log_error("epoll_weit errno:%d %s ", errno, strerror(errno));

    }else if (readyfds > 0)

    sgc_log_debug("readyfds:%d errno:%d err:%s ", readyfds, errno, strerror(errno));

    for(i=0;i<readyfds;++i)

    {

    // 新用户连接

    if(events[i].data.fd==listenfd)

    {

    if (clients >= config->maxclients){

    sgc_log_debug("clients full,close:%s socket fd:%d clients:%d ", inet_ntoa(clientaddr.sin_addr), listenfd, clients);

    continue;

    }else{

    clients++;

    connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clilen);

    if(connfd<0){

    sgc_log_error("accept error connfd<0 fd=%d errno:%d %s ", connfd, errno, strerror(errno));

    exit(1);

    }

    setnonblocking(connfd);

    ev.data.fd=connfd;

    ev.events=EPOLLIN|EPOLLET;

    sgc_log_debug("new clients:%d, num:%d %s ", connfd, clients, inet_ntoa(clientaddr.sin_addr));

    config->handler_new(connfd, &clientaddr, config);

    epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);

    }

    }else if(events[i].events & EPOLLIN)//如果是已经连接的用户,并且收到数据,那么进行读入。

    {

    if ((sockfd = events[i].data.fd) < 0){

    sgc_log_error("EPOLLIN fd < 0 ");

    continue;

    }

    // 一次读完缓冲区

    memset(buffer, 0, config->buffer_size);

    while((nreadbytes = recv(sockfd, buffer, config->buffer_size, 0)) > 0)

    {

    sgc_log_debug("read fd:%d len:%d ", sockfd, nreadbytes);

    config->hander_read(sockfd, buffer, nreadbytes);

    memset(buffer, 0, config->buffer_size);

    }

    sgc_log_debug("read finish nreadbytes:%d fd:%d errno:%d, err:%s ", nreadbytes, sockfd, errno, strerror(errno));

    if (nreadbytes < 0){

    if (errno == ECONNRESET){

    EPOLL_CLOSE_CODE("connrset")

    }

    }else if (nreadbytes == 0){

    EPOLL_CLOSE_CODE("normal")

    }

    }else{// 其它异常情况,关闭

    sockfd = events[i].data.fd;

    EPOLL_CLOSE_CODE("exception")

    }

    }

    }

    }

  • 相关阅读:
    TCP报文指针解释,IP地址
    Linux基本操作及安装(部分)
    Windows cmd用语
    三次握手,四次挥手
    Linux命令(部分)
    Linux系统命令。
    三层交换配置流程
    网络基本内容(部分)
    20192020学期20192404《网络空间安全专业导论》第三周学习总结
    201920201学期 20192404 《网络空间安全导论》第二周学习总结
  • 原文地址:https://www.cnblogs.com/cxt-janson/p/9273440.html
Copyright © 2011-2022 走看看