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

recv failed: Bad file descriptor

这是我的代码:

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

我尝试通过重新启动PC来尝试,但仍然存在相同的问题。请提供帮助。

最佳答案

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

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

如果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);
      ...

要解决您的问题,有两种方法:
  • 滥用线程函数的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一样宽的指针。
  • 使用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()测试<0的结果是否有错误,因为0是文件/套接字描述符的有效值。
  • 关于c - 连接出现错误文件描述符错误后无法接收数据,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30756210/

    10-12 18:05