顺序服务器:accept(2)调用的行为

时间:2013-03-02 19:58:59

标签: c sockets unix

我在非并发单进程实现中有一个带TCP套接字的服务器。

int main(int argc, char** argv) {

    int sock_ds, acc_sock_ds, opt, client_addr_l;
    unsigned short port;
    struct sockaddr_in server_addr, client_addr;


    /*Parsing command line: port-number retrieving*/
    /*...*/

    printf("Port number retrieved (%d), server is starting ...\n", port);

    /*TCP Socket creation*/
    sock_ds = socket(AF_INET, SOCK_STREAM, 0); 
    if(sock_ds == -1){
        fprintf(stderr, "Socket creation error\n");
        exit(EXIT_FAILURE);
    }

    /*Server address binding*/
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = INADDR_ANY;
    //use setsockopt(2) with OS_REUSEADR ???
    if(bind(sock_ds, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1){
        fprintf(stderr, "Address binding error\n");
        exit(EXIT_FAILURE);
    }

    /*Server with passive socket*/
    if(listen(sock_ds, SOMAXCONN) == -1){
        fprintf(stderr, "Listen call error: %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    printf("Server is ready. Waiting for connections.\n");

        /*Busy-waiting server*/
while(1){

    memset(&client_addr, 0, sizeof(client_addr));
    acc_sock_ds = accept(sock_ds, &client_addr, &client_addr_l);
    /*Connect error management*/
    if(acc_sock_ds == -1){
         errsv = errno;
         if(errsv == 12 || errsv == 23 ){
            /*Fatal errors ENOMEM, ENFILE*/
            fprintf(stderr, "Fatal error on accept\n");
            exit(EXIT_FAILURE);
         }
         else if(errsv == 103 || errsv == 4 || errsv == 71 || errsv == 92 
                 || errsv == 112 || errsv == 113 || errsv == 95 || errsv == 101)
             /*ECONNABORTED, EINTR, EPROTO, ENOPROTOOPT, 
              * EHOSTDOWN, EHOSTUNREACH, EOPNOTSUPP, ENETUNREACH*/
             continue;
         else if(errsv == 100 || errsv == 64){
             /* ENETDOWN - ENONET */
             /*start timeout...*/
             continue;
         }
    }
  }
}

在此测试中,我预计服务器将在失败时继续拨打电话accept(2)。我知道有一些终端错误条件如EBADF。我应该根据errno的值提供不同的行为(终止流程)吗?服务器必须停止哪些值并且可以继续等待?

代码已编辑。我从未通过errno进行错误处理。如果有错误或建议请通知他们。我注意到缺少网络意味着两个条件ENETDOWN - ENONET。我是否应该期望这些情况超时以防止停滞?

2 个答案:

答案 0 :(得分:2)

查看MacOS X上为accept(2)记录的错误代码(对于大多数符合POSIX标准的系统,它应该是一致的):

  • EBADF - 程序员错误 - 如果原始socket调用成功则无法发生
  • ECONNABORTED - 再试一次
  • EFAULT - 程序员错误 - 除非&client_addr无效
  • 才会发生
  • EINTR - 呼叫中断,再试一次
  • EINVAL - “套接字不愿接受连接” - 我不确定会发生什么情况。 可能是由于没有先调用listen(2)(程序员错误),尽管在某些操作系统上显然也可能是namelen参数为负(也是程序员错误)< / LI>
  • EMFILE - 程序员错误 - 你用完了每个进程的FD,但除非你忘了关闭它们,否则不会发生这种情况
  • ENFILE - 系统耗尽了FD - 这可能是致命的
  • ENOMEM - 系统内存耗尽 - 这可能是致命的
  • ENOTSOCK - 程序员错误 - 除非你传递的是非套接字的FD,否则不会发生
  • EOPNOTSUPP - 程序员错误 - 除非你传递的插座不是SOCK_STREAM
  • 否则不会发生
  • EWOULDBLOCK - 程序员错误 - 您的套接字已配置为非阻止

此外,Linux系统似乎还可以生成ENETDOWNEPROTOENOPROTOOPTEHOSTDOWNENONETEHOSTUNREACHEOPNOTSUPPENETUNREACH。这些是你应该循环的网络错误。

总之,你应该:

  • 退出ENFILEENOMEM
  • 的计划
  • 循环ECONNABORTEDEINTR,以及上面的列表来自Linux
  • 在您调试其他任何事情时,
  • assert() - 这些运行时错误应该只是由于代码中的逻辑错误而发生,而不是因为与网络相关的事件。

答案 1 :(得分:1)

文体:

  • 使用符号常量而不是将它们放在注释中
  • 当意外错误至少报告错误号
  • 也处理默认/意外情况
BTW:下面的大多数象征性常数似乎都不存在。


#include <errno.h>
#include <strings.h>

 /* Busy-waiting server */
while(1){

    memset(&client_addr, 0, sizeof(client_addr));
    acc_sock_ds = accept(sock_ds, &client_addr, &client_addr_l);
    /*Connect error management*/
    if(acc_sock_ds == -1){
        switch(errsv=errno) {
            /*Fatal errors ENOMEN, ENFILE, all others*/
        default :
        case ENOMEM :
        case ENFILE :
            fprintf(stderr, "Fatal error on accept %d(%s)\n"
                , errsv, strerror(errsv)
                );
            exit(EXIT_FAILURE);
            /* normal NON-ERROR error conditions */
        case ECONNABORTED :
        case EINTR :
        case EPROTO :
        case ENOPROTOOPT :
        case EHOSTDOWN :
        case EHOSTUNREACH :
        case EOPNOTSUPP :
        case ENETUNREACH :
             continue;
        case ENETDOWN :
        case ENONET :
             /*start timeout...*/
             continue;
         }
    }
  }
}