2

如果在连续调用套接字函数期间未更新 errno 值,问题可能会发生什么?

socket (AF_INET, -1, 0);
socket (AF_INET, SOCK_STREAM, -1);

第一个应该有 errno = EINVAL 第二个应该有 errno = EPROTONOSUPPORT


从@JonathanLeffler 下面提供的代码中,cygwin 的输出如下:

*$ ./socket.exe
Error from socket(AF_INET, -1, 0): 124 (Socket type not supported)
-- errno = 124 (Socket type not supported)
Error from socket(AF_INET, SOCK_STREAM, -1): 123 (Protocol not supported)
-- errno = 123 (Protocol not supported)
socket(AF_INET, SOCK_STREAM, 0) succeeded (fd = 3)
-- errno = 123 (Protocol not supported)*

在创建套接字之前,编辑了下面的上述代码以创建套接字 fd0

int fd0 = socket(AF_INET, SOCK_STREAM, 0);
if (fd0 < 0)
    printf("Error from socket(AF_INET, SOCK_STREAM, 0): %d (%s)\n", errno, strerror(errno));
else
{
    printf("socket(AF_INET, SOCK_STREAM, 0) succeeded (fd = %d)\n", fd0);
    close(fd0);
}
printf("-- errno = %d (%s)\n", errno, strerror(errno));

int fd1 = socket(AF_INET, -1, 0);
.....

结果如下:

*$ ./socket.exe
socket(AF_INET, SOCK_STREAM, 0) succeeded (fd = 3)
-- errno = 0 (No error)
Error from socket(AF_INET, -1, 0): 124 (Socket type not supported)
-- errno = 124 (Socket type not supported)
Error from socket(AF_INET, SOCK_STREAM, -1): 123 (Protocol not supported)
-- errno = 123 (Protocol not supported)
socket(AF_INET, SOCK_STREAM, 0) succeeded (fd = 3)
-- errno = 123 (Protocol not supported)*

预计第一个和最后一个套接字创建应该具有相同的 errno 值。


但是如何解释这个输出呢?第 5 和第 6 个套接字创建具有相同的 errno 值,但错误原因不同。

$ ./test_select.exe
[1] ierr = 124, iSocket = -1 socket(AF_INET, -1, 0);
 : Socket type not supported
[2] ierr = 124, iSocket = -1 socket(AF_INET, -1, 0);
 : Socket type not supported
[3] ierr = 124, iSocket = 3 socket(AF_INET, SOCK_STREAM, 0);
 : Socket type not supported
[4] ierr = 124, iSocket = -1 socket (AF_INET, -1, 0);
 : Socket type not supported
[5] ierr = 123, iSocket = -1 socket (AF_INET, SOCK_STREAM, -1);
 : Protocol not supported
[6] ierr = 123, iSocket = -1 socket (AF_INET, -1, 0)
 : Protocol not supported
[7] ierr = 124, iSocket = -1 socket(AF_INET, SOCK_STREAM, -1)
 : Protocol not supported

实际代码如下:

   sockfd = socket(AF_INET, -1, 0);
    err = errno;
    printf("[1] err = %d, sockfd = %d socket(AF_INET, -1, 0);\n", err, sockfd);
    perror(" ");
    sockfd = socket(AF_INET, -1, 0);
     err = errno;
    printf("[2] err = %d, sockfd = %d socket(AF_INET, -1, 0);\n", err, sockfd);
    perror(" ");
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
    err = errno;
    printf("[3] err = %d, sockfd = %d socket(AF_INET, SOCK_STREAM, 0);\n", err, sockfd);
    perror(" ");
    close(sockfd);
    sockfd = socket(AF_INET, -1, 0);
    err = errno;
    printf("[4] err = %d, sockfd = %d socket (AF_INET, -1, 0);\n", err, sockfd);
    perror(" ");
    sockfd = socket(AF_INET, SOCK_STREAM, -1);
    err = errno;
    printf("[5] err = %d, sockfd = %d socket (AF_INET, SOCK_STREAM, -1);\n", err, sockfd);
    perror(" ");
    sockfd = socket(AF_INET, -1, 0);
    err = errno;
    printf("[6] err = %d, sockfd = %d socket (AF_INET, -1, 0)\n", err, sockfd);
    perror(" ");
    err = errno;
    sockfd = socket(AF_INET, SOCK_STREAM, -1);
    printf("[7] err = %d, sockfd = %d socket(AF_INET, SOCK_STREAM, -1)\n", err, sockfd);
    perror(" ");
4

2 回答 2

11

errno不会被标准库函数清除,只会设置。此外,它可以由任何未以其他方式记录以禁止该行为的标准库函数虚假设置。

本质上,唯一正确的使用方法errno是仅在标准库函数返回指示错误的值后立即检查它,并将其保存在另一个变量中,如果您需要保留它以备后用。

对于select大多数系统调用,指示错误的返回值为 -1,这意味着您应该只检查errno它是否返回 -1。

于 2012-04-18T14:18:23.297 回答
9

没有库函数设置errno为零。如果你想让它归零,你的程序必须这样做。

errno只有当一个函数说它失败时,你才能有意义地测试信息(即使那样,只有当它的手册页表明它设置了errno)。您可以通过成功的功能找到errno设置为非零。例如,在 Solaris 上,errno当写入文件而不是终端时,您经常会在标准 I/O 调用之后找到 ENOTTY。

在您的示例中,无法保证给定的对函数的错误调用将设置多个可能的错误值中的哪一个。只要报告了一个有效的错误条件,系统就可以决定它报告哪个错误。因此,您需要给出一个令人信服的示例,说明为什么第二次调用应该返回 EINVAL 而不是 EBADF;您所写的问题没有包含足够的信息来让我们发表意见。

另请注意,唯一安全的声明方法errno是通过标头:#include <errno.h>. 特别是在线程环境中,它通常不是简单extern int errno;的 . 例如,在 Mac OS X 上,它可以定义为:

extern int * __error(void);
#define errno (*__error())

也就是说,__error是一个返回指向整数的指针的函数,并且errno是一个取消引用该指针的宏,成为一个可修改的左值。


代码分析

给定的代码是:

socket (AF_INET, -1, 0);
socket (AF_INET, SOCK_STREAM, -1);

转换成可运行的代码,这可能变成:

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

int main(void)
{
    int fd1 = socket(AF_INET, -1, 0);
    if (fd1 < 0)
        printf("Error from socket(AF_INET, -1, 0): %d (%s)\n", errno, strerror(errno));
    else
    {
        printf("socket(AF_INET, -1, 0) succeeded (fd = %d)\n", fd1);
        close(fd1);
    }
    printf("-- errno = %d (%s)\n", errno, strerror(errno));

    int fd2 = socket(AF_INET, SOCK_STREAM, -1);
    if (fd2 < 0)
        printf("Error from socket(AF_INET, SOCK_STREAM, -1): %d (%s)\n", errno, strerror(errno));
    else
    {
        printf("socket(AF_INET, SOCK_STREAM, -1) succeeded (fd = %d)\n", fd2);
        close(fd2);
    }
    printf("-- errno = %d (%s)\n", errno, strerror(errno));

    int fd3 = socket(AF_INET, SOCK_STREAM, 0);
    if (fd3 < 0)
        printf("Error from socket(AF_INET, SOCK_STREAM, 0): %d (%s)\n", errno, strerror(errno));
    else
    {
        printf("socket(AF_INET, SOCK_STREAM, 0) succeeded (fd = %d)\n", fd3);
        close(fd3);
    }
    printf("-- errno = %d (%s)\n", errno, strerror(errno));

    return(0);
}

在 Mac OS X 10.7.3 上运行,这会产生:

Error from socket(AF_INET, -1, 0): 43 (Protocol not supported)
-- errno = 43 (Protocol not supported)
Error from socket(AF_INET, SOCK_STREAM, -1): 43 (Protocol not supported)
-- errno = 43 (Protocol not supported)
socket(AF_INET, SOCK_STREAM, 0) succeeded (fd = 3)
-- errno = 43 (Protocol not supported)

正如我之前所说,返回的错误取决于系统。

于 2012-04-18T14:19:35.510 回答