【问题标题】:Unable to receive data after connection getting Bad file Descriptor error连接后无法接收数据收到错误文件描述符错误
【发布时间】:2015-06-10 12:20:35
【问题描述】:

我正在使用 Pthreads(Linux) 在 C 语言中开发一个多线程服务器应用程序。 我的系统是双启动的。Windows 7 和 Ubuntu。我重新启动了我的 PC 并从 Ubuntu 启动到 Windows,在重新启动我的服务器应用程序之前工作正常。之后我再次从 Windows 启动到 ubuntu。并启动了我的服务器,当客户端连接时我开始出现以下错误。

recv failed: Bad file descriptor

这是我的代码:

Main.C

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h> //for socket,AF_INET,SOCK_STREAM
#include <arpa/inet.h> //for sockaddr_in
#include <string.h>   //for memset()  
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include "extern.h" 

#define MAX_CONNECTIONS  5

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

    int sock_desc = 0, connfd = 0,listenfd =0;
    struct sockaddr_in serv_addr;
    int clntSock; 
    struct sockaddr_in echoClntAddr; 
    unsigned int clntLen; 
    char sendBuff[1025];
    char recvBuff[10025];
    int n = 0;
    pthread_t thr;



    sock_desc = socket(AF_INET, SOCK_STREAM, 0); 

    if(sock_desc < 0 )
       dieWithError("Unable to open Socket\n");  

    memset(&serv_addr,0,sizeof(serv_addr)); 

    serv_addr.sin_family = AF_INET ;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(7024);

    if(bind(sock_desc, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) 
       dieWithError("bind failed\n");

    if(listen(sock_desc,MAX_CONNECTIONS) < 0)
       dieWithError("listen failed\n");  

     file = fopen("testServer.txt", "w");

      clntSock = sizeof(struct sockaddr);
      int i =0;
      fcntl(sock_desc, F_SETFL, fcntl(sock_desc, F_GETFL, 0) | O_NONBLOCK);

      while(1)
    {

        connfd = accept(sock_desc, (struct sockaddr *)&echoClntAddr,(socklen_t*)&clntSock);

      if(connfd > 0){  
         puts("Connection accepted");

        if( pthread_create( &thr, NULL ,  connection_handler , (void*) &connfd) < 0)
        {
            perror("could not create thread");
            return 1;
        }

        puts("Handler assigned");
        }    
    }

    if (connfd < 0)
    {
        perror("accept failed");
        return 1;
    }


       return (EXIT_SUCCESS);
}

void dieWithError(char *errormsg){
     printf("%s", errormsg);  
}

HandleConnection.c

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



void *connection_handler(void *socket_desc)
{

    int sock = *(int*)socket_desc;
    t_data.t_id = sock;

    int read_size;
    char *message , client_message[2000];

    while( (read_size = recv(sock , client_message , 2000 , 0)) > 0 )
    {

       client_message[read_size] = '\0';
           t_data.msg = client_message; 


           printf("%s",client_message); 

        memset(client_message, 0, 2000);
    }

    if(read_size == 0)
    {
        puts("Client disconnected");
        fflush(stdout);
    }
    else if(read_size == -1)
    {
        perror("recv failed");
    }

    return 0;
} 

我尝试重新启动我的电脑,但同样的问题仍然存在。请帮助。

【问题讨论】:

  • @DamiánMontenegro:recv() 失败的原因是一样的,recv() 在这个问题中收到 -1 的原因比你链接的问题要微妙得多,在哪里只是一个转储错误,缺少返回,中断,等等,这里不是这种情况。所以我反对你的评论。

标签: c linux multithreading sockets


【解决方案1】:

(您观察到的问题与在双引导机器上运行无关。)

所示代码通过使用每个传入连接的相同变量的地址将套接字描述符传递给线程函数来引入竞争。

如果accept() 第二次返回得更快,就会发生比赛。为了让线程处理程序快速存储它通过执行指向的地址:

int sock = *(int*)socket_desc;

在您的特定情况下,侦听套接字设置为非阻塞,这很可能是这种情况,当只有一个连接进入时,处理程序获得了 connfd 的地址传入并并行 accept() 被再次调用在(非阻塞)监听套接字上,没有传入连接在等待,accept() 立即返回并将connfd 设置为-1。这种情况发生得比上一个连接的处理程序调用的更快

int sock = *(int*)socket_desc;

在这家商店,-1。

为了观察这种效果(不是为了解决问题)修改你的代码如下:

int result = accept(sock_desc, (struct sockaddr *)&echoClntAddr, &clntSock);
if (0 > result)
{
  perror("accept() failed");
}
else
{
  connfd = result;

  puts("Connection accepted");

  if( pthread_create( &thr, NULL ,  connection_handler , (void*) &connfd) < 0)
  {
    ...

然后处理程序会这样开始:

    void *connection_handler(void * pv)
    {
      int sock = *((int*) pv);
      fprintf("sock = %d\n", sock);
      ...

要解决您的问题,有两种方法:

  1. 滥用线程函数的void* 参数来传递int。

    socklen_t clntSock = ...;
    ...
    connfd = accept(sock_desc, (struct sockaddr *)&echoClntAddr, &clntSock);
    
    if (0 <= connfd)
    {
      if (pthread_create( &thr, NULL, connection_handler, (void *) connfd) < 0)
      {
        ...
    

    然后处理程序会这样开始:

    void *connection_handler(void * pv)
    {
      int sock = (int) pv;
    
      ...
    

    这是一种常见但有点“hacky”的解决方案,它依赖于至少与int 一样宽的指针。

  2. 使用int 的单独实例来存储accept() 的结果。

    socklen_t clntSock = ...;
    ...
    connfd = accept(sock_desc, (struct sockaddr *)&echoClntAddr, &clntSock);
    if (0 <= connfd)
    {
      int * pconnfd = malloc(sizeof *pconnfd);
      if (NULL == pconnfd)
        ... // fail and leave here
      *pconnfd = connfd;
      if (pthread_create(&thr, NULL, connection_handler, pconnfd) < 0)
      {
        ...
    

    然后处理程序会这样开始:

    void *connection_handler(void * pv)
    {
      int connfd = *((int *) pv);
      free(pv);
    
      ...
    

    这是一种干净且可移植的方法,需要额外调用一对malloc()/free()。

上面显示的代码中的其他更正:

  • 将传递给accept()的变量定义为socklen_t,因为accept()的第三个参数定义为socklen_t *。如果传递一个指向其他东西的指针并且这个“东西”的大小不同于socklen_t accept() 会遇到未定义的行为。

  • 测试accept() 与&lt;0 的结果是否有错误,因为0 是文件/套接字描述符的有效值。

【讨论】:

  • 如果我评论 fcntl(sock_desc, F_SETFL, fcntl(sock_desc, F_GETFL, 0) | O_NONBLOCK);一切正常。
  • @Simer:查看我的更新答案。对于阻塞套接字(默认),如果没有其他传入连接挂起,accept() 将阻塞,因此connfd 不会被覆盖。但是,如果当时有两个连接到达。 accept() 为第二个连接返回的值可能会为第一个连接存储。一个更难以识别的错误,例如第一个处理程序的recv() 不会失败,而只是从第二个连接中读取。丑陋。
  • 再次明确声明:使用您的代码,如图所示和注释掉将侦听套接字设置为non-blocking的调用不修复漏洞。它只是隐藏它,直到服务器在短时间内收到许多请求。
  • 嗨,我改变了 int sock = (int)socket_desc;跟随,它工作正常。 int sock = (int) socket_desc;现在还好吗。
  • @Simer:我更新了我的答案,评论了两个提议的解决方案的“清洁度”。
猜你喜欢
  • 2016-08-26
  • 1970-01-01
  • 2020-01-21
  • 1970-01-01
  • 2016-10-14
  • 2021-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多