0

我在非并发单进程实现中有一个带有 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是由缺乏网络暗示的。对于这些情况,我是否应该期望超时以防止停滞?

4

2 回答 2

2

查看 MacOS X 上记录的错误代码accept(2)(并且对于大多数任何 POSIX 兼容系统都应该是一致的):

  • EBADFsocket- 程序员错误 - 如果原始调用成功则不会发生
  • ECONNABORTED- 再试一次
  • EFAULT&client_addr- 程序员错误 - 除非无效,否则不会发生
  • EINTR- 通话中断,重试
  • EINVAL- “套接字不愿意接受连接” - 我不确定这会如何发生。这可能是由于没有listen(2)首先调用(程序员错误),尽管显然在某些操作系统上它也可能是由namelen参数为负数引起的(也是程序员错误)
  • EMFILE- 程序员错误 - 您用完了每个进程的 FD,但除非您忘记关闭它们,否则不应该发生这种情况
  • ENFILE- 系统耗尽了 FD - 这可能是致命的
  • ENOMEM- 系统内存不足 - 这可能是致命的
  • ENOTSOCK- 程序员错误 - 除非你传递了一个不是套接字的 FD,否则不会发生
  • EOPNOTSUPP- 程序员错误 - 除非你传递一个不是SOCK_STREAM
  • EWOULDBLOCK- 程序员错误 - 你的套接字被配置为非阻塞

此外,Linux 系统似乎也可以生成ENETDOWN, EPROTO, ENOPROTOOPT, EHOSTDOWN, ENONET, EHOSTUNREACH, EOPNOTSUPP, 和ENETUNREACH. 这些是您应该循环的网络错误。

总之,您可能应该:

  • 退出程序ENFILEENOMEM
  • ECONNABORTED在and上循环EINTR,上面的列表来自 Linux
  • assert()在您调试其他任何事情的过程中 - 这些运行时错误应该只因代码中的逻辑错误而发生,而不是因为与网络相关的事件。
于 2013-03-02T20:29:43.273 回答
1

风格:

  • 使用符号常量而不是将它们放在注释中
  • 发生意外错误时至少报告错误号
  • 也处理默认/意外情况

顺便说一句:下面的大多数符号常量似乎都不存在。


#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;
         }
    }
  }
}
于 2013-03-03T12:23:34.260 回答