连接出现错误文件描述符错误后无法接收数据 [英] Unable to receive data after connection getting Bad file Descriptor error

查看:243
本文介绍了连接出现错误文件描述符错误后无法接收数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Pthreads(Linux)在C语言中的多线程服务器应用程序上工作. 我的系统是双启动.Windows7和Ubuntu.我重新启动PC并从Ubuntu引导到Windows,在重新启动服务器应用程序之前,它工作正常.之后,我再次从Windows引导到ubuntu.并启动了我的服务器,当客户端连接时,我开始收到以下错误消息.

I am working on a Multi-threaded Server application in C using Pthreads(Linux). My system is dual boot.Windows 7 as well as Ubuntu. I restarted my PC and booted into Windows from Ubuntu, Before restarting my server application was working Fine.After i again booted to ubuntu from windows. and started my server i start getting following Error when a client connects.

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

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;
} 

我尝试重新启动PC,但仍然存在相同的问题.请帮助.

I tried by restarting my PC but same problem persists.Please Help.

推荐答案

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

(The issue you observe has nothing to do with running on a dual-boot machine.)

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

The code as shown introduces a race by using the address of the same variable for each incoming connection to pass the socket descriptor to the thread function.

如果accept()第二次返回更快,则会发生比赛.为了快速使线程处理程序存储通过执行所指向的地址,

The race occures if accept() would return faster a second time. To fast for the thread-handler to store what the address it got passed pointed to via executing:

int sock = *(int*)socket_desc;

在您的特定情况下,侦听套接字设置为非阻塞,这种情况非常简单,当只有一个连接进入时,处理程序将传入connfd的地址,然后并行调用accept()在(非阻塞)侦听套接字上,没有传入连接正在等待,并且accept()立即返回并将connfd设置为-1.这种情况发生得比以前的连接的处理程序调用

In your specfic case with the listening socket set to non-blocking this most likley is the case, when only one connection came in, the handler got the address of connfd passed in and in parallel accept() gets called again on the (non-blocking) listening socket, no incoming connection is waiting and accept() returns immediately and sets connfd to -1. This happens faster then the handler for the previous connection could have called

int sock = *(int*)socket_desc;

,并在此存储-1.

要观察这种效果(不解决问题),请按如下所示修改您的代码:

To observer this effect (not to fix the issue) modify your code as follows:

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)
  {
    ...

然后,处理程序将像这样开始:

The handler then would begin like this:

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

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

To fix your issue there are two approaches:

  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)
  {
    ...

然后,处理程序将像这样开始:

The handler then would begin like this:

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

  ...

这是一个常见但有点棘手"的解决方案,它依赖于指针的宽度至少与int一样宽.

This is a common, but somewhat "hacky" solution, which relies on a pointer being at least as wide as an int.

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

Use a seperate instance of an int to store the result of 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)
  {
    ...

然后,处理程序将像这样开始:

The handler then would begin like this:

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

  ...

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

This is a clean and portable approach, with the cost of an additional pair of calls to malloc()/free().

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

Other corrections in the code shown above:

  • 将地址传递给accept()的变量定义为socklen_t,因为将accept()的第三个参数定义为socklen_t *.如果将指针传递给其他对象,并且该对象"的大小与socklen_t accept()的大小不同,则会遇到未定义的行为.

  • Define the variable whichs address is passed to accept() to be socklen_t as the 3rd parameter to accept() is defined as socklen_t *. If passing a pointer to something else and this "something" is of different size than socklen_t accept() would run into undefined behaviour.

针对<0测试accept()的结果是否有错误,因为0是文件/套接字描述符的有效值.

Test the outcome of accept() against <0 for error as 0 is a valid value for a file/socket descriptor.

这篇关于连接出现错误文件描述符错误后无法接收数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆