C Linux服务器套接字-错误的文件描述符

C Linux服务器套接字-错误的文件描述符,c,linux,sockets,C,Linux,Sockets,我在Linux下的服务器套接字有问题。由于我不知道的原因,服务器套接字消失了,我在等待输入连接的select调用中得到了一个错误的文件描述符。当我在不同线程中关闭不相关的套接字连接时,总是会出现此问题。这发生在内核为2.6.36的嵌入式Linux上 有人知道为什么会这样吗?服务器套接字消失导致错误的文件描述符,这正常吗 编辑: 另一个套接字代码实现了一个VNC服务器,并在完全不同的线程中运行。其他代码中唯一的特殊之处是使用了setjmp/longjmp,但这应该不是问题 创建服务器套接字的代码如

我在Linux下的服务器套接字有问题。由于我不知道的原因,服务器套接字消失了,我在等待输入连接的select调用中得到了一个
错误的文件描述符。当我在不同线程中关闭不相关的套接字连接时,总是会出现此问题。这发生在内核为2.6.36的嵌入式Linux上

有人知道为什么会这样吗?服务器套接字消失导致
错误的文件描述符
,这正常吗

编辑: 另一个套接字代码实现了一个VNC服务器,并在完全不同的线程中运行。其他代码中唯一的特殊之处是使用了
setjmp/longjmp
,但这应该不是问题

创建服务器套接字的代码如下所示:

int server_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(1234);

const int optionval = 1;
setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &optionval, sizeof(optionval));

if (bind(server_socket, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
    perror("bind");
    return 0;
}

if (listen(server_socket, 1) < 0) {
    perror("listen");
    return 0;
}
编辑: 当问题发生时,我当前只需重新启动服务器,但我不明白为什么服务器套接字id会突然变成无效的文件描述符:

int error = 0;
socklen_t len = sizeof (error);
int retval = getsockopt (server_socket, SOL_SOCKET, SO_ERROR, &error, &len );
if (retval < 0) {
    close(server_socket);
    goto server_start;
}
int错误=0;
socklen_t len=sizeof(错误);
int retval=getsockopt(服务器套接字、SOL套接字、SO\u错误、错误和len);
如果(返回值<0){
关闭(服务器_套接字);
转到服务器\u启动;
}

在Linux中,一旦您创建了一个连接并将其关闭,那么您必须等待一段时间才能建立新的连接。 在Linux中,套接字不会在关闭套接字后立即释放端口号


重新使用套接字,然后出现错误的文件描述符。

如果不区分代码中的两种错误情况,则两者都可能失败
选择
接受
。我的猜测是,您刚刚有一个超时,选择返回
0

  • else
    分支中打印
    retval
    errno
  • 分别调查
    accept
    的返回值
  • 确保在每次系统调用之前将
    errno
    重置为
    0
套接字(文件描述符)通常会遇到与
C
中的原始指针相同的管理问题。每当关闭套接字时,不要忘记将
-1
赋值给保持描述符值的变量:

close(socket);
socket = -1;
正如您对
C
指针所做的那样

free(buffer);
buffer = NULL;
如果您忘记了这样做,您可以在以后关闭套接字两次,就像在内存是指针时关闭内存两次一样

另一个问题可能与人们通常忘记的事实有关:UNIX环境中的文件描述符从
0
开始。如果代码中的某个地方

struct FooData {
    int foo;
    int socket;
    ...
}

// Either
FooData my_data_1 = {0};
// Or
FooData my_data_2;
memset(&my_data_2, 0, sizeof(my_data_2));

在这两种情况下,
my_data_1
my_data_2
都有一个有效的描述符(
socket
)值。稍后,负责释放
FooData
结构的某段代码可能会盲目地
close()
此描述符,它恰好是服务器的侦听套接字(
0
)。

1-关闭套接字:

close(sockfd);
2-从选择集中清除套接字文件描述符:

FD_CLR(sockfd,&master); //opposite of FD_SET

你发布的代码没有问题,错误一定在别处。例如,在关闭套接字之后是否使用它?线程的具体使用位置是什么?上面的代码在一个线程中运行。另一个代码位于另一个模块中,该模块也运行线程。关闭那里的连接会杀死这里的服务器。我没有想到服务器套接字在我不关闭它的情况下会变得无效。我敢打赌,您的代码中的某个错误正在导致您
关闭
您稍后选择
打开的同一个套接字。@trenki它不能。某个地方有一个bug导致您关闭与侦听套接字具有相同的文件描述符值,或者有一个bug覆盖了保存侦听套接字描述符的变量。您可以在strace下运行程序,例如
strace-f-e accept、socket、close、shutdown./yourserver
并查看是否使用与侦听socket相同的文件描述符值调用close(),或者是否突然开始将不同的文件描述符传递给accept()。保留的不是IP地址(这会很严重)但是港口number@JensGustedtya是端口号。只有当(a)您正在创建一个客户端连接,但在这里没有发生,(b)您绑定到一个特定的出站端口号,这是不需要做的,在这里也没有发生,并且(c)您是发起关闭的终端时,这才是真的-1表示完全无关。
FD_CLR(sockfd,&master); //opposite of FD_SET