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

问题描述:

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

有谁知道为什么会发生这种情况?服务器插座是否可以简单地消失导致Bad file descriptor是否正常?

编辑: 其他套接字代码实现了一个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; 
} 

我等待进来的连接使用下面的代码:

static int WaitForConnection(int server_socket, struct timeval *timeout) 
{ 
    fd_set read_fds; 

    FD_ZERO(&read_fds); 
    int max_sd = server_socket; 
    FD_SET(server_socket, &read_fds); 

    // This select will result in 'EBADFD' in the error case. 
    // Even though the server socket was not closed with 'close'. 
    int res = select(max_sd + 1, &read_fds, NULL, NULL, timeout); 
    if (res > 0) { 
     struct sockaddr_in caddr; 
     socklen_t clen = sizeof(caddr); 
     return accept(server_socket, (struct sockaddr *) &caddr, &clen); 
    } 

    return -1; 
} 

编辑: 当问题的情况下发生我目前只是重新启动服务器,但我不明白为什么服务器套接字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; 
} 
+2

没有什么错,你发布的代码,误差必须在其他地方。例如,关闭它之后是否使用了套接字? – 2012-07-31 07:22:14

+0

准确使用的线程在哪里? – 2012-07-31 07:32:10

+0

上面的代码在一个线程中运行。另一个代码在另一个也运行线程的模块中。关闭连接将杀死服务器。我没有想过如果我没有关闭服务器套接字就会失效。 – trenki 2012-07-31 07:42:46

在Linux中,一旦你创建了一个连接并且它被关闭了,那么你必须等待一段时间才能建立新的连接。 与Linux一样,套接字不释放端口号。只要你关闭了插座。

OR

你重用插座,再不济的文件描述符要来了。

+0

它不是保留的IP地址(这将是严重的),但端口号 – 2012-07-31 08:09:47

+0

@JensGustedt是其端口号。 – 2012-07-31 08:24:19

+0

这只有在以下情况下才成立:(a)您创建的客户端连接不在此处发生,(b)您绑定到特定的出站端口号,不需要这样做,哪些也不会发生在这里,和(三)你是结束开始关闭。 -1完全和完全不相关。 – EJP 2014-07-25 23:34:27

您不区分代码中的两个错误情况,两者都可能失败selectaccept。我的猜测是,你只是有一个时间,并选择返回0

  • 打印retvalerrnoelse分支
  • 调查accept返回值seperately
  • 确保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_1my_data_2有一个有效的描述符(socket)值。后来,一些代码,负责释放FooData结构可能会盲目close()这个描述符,这恰好是你服务器的侦听套接字(0)。

+0

谢谢!你的提示确实帮助我找到了一个邪恶的错误。 – jkow 2017-03-30 16:01:23

+0

@jkow我很高兴这是有帮助的。别客气。 – GreenScape 2017-03-30 17:57:30

1-闭上你的插座:

close(sockfd); 

2-清楚你的套接字文件从选择一套描述:

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