Не удалось получить данные после подключения, получив ошибку Bad Descriptor

Я работаю над многопоточным серверным приложением на C с использованием Pthreads (Linux). Моя система – двойная boot.Windows 7, а также Ubuntu. Я перезапустил свой компьютер и загрузился в Windows из Ubuntu. Перед перезагрузкой моего серверного приложения работало Fine. После того, как я снова загрузился в ubuntu из окон. и начал мой сервер, я начал получать следующую ошибку при подключении клиента.

recv failed: Bad file descriptor 

Вот мой код:

main.C

 #include  #include  #include  //for socket,AF_INET,SOCK_STREAM #include  //for sockaddr_in #include  //for memset() #include  #include  #include  #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){ 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); } в #include  #include  #include  //for socket,AF_INET,SOCK_STREAM #include  //for sockaddr_in #include  //for memset() #include  #include  #include  #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){ 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  #include  #include  #include  #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; } 

Я попытался перезагрузить свой компьютер, но эта же проблема сохраняется. Пожалуйста, помогите.

    (Проблема, которую вы наблюдаете, не имеет никакого отношения к запуску на машине с двойной загрузкой.)

    Код, показанный здесь, представляет собой гонку, используя адрес той же переменной для каждого входящего соединения, чтобы передать дескриптор сокета функции streamа.

    Гонка происходит, если accept() вернется быстрее во второй раз. Чтобы поститься для обработчика streamа, чтобы сохранить то, что передал ему адрес, указывает на выполнение через:

     int sock = *(int*)socket_desc; 

    В вашем specfic случае с прослушивающим сокетом, установленным на неблокирование, это самый likley – это случай, когда только одно соединение вошло, обработчик получил адрес 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* функции thread-функции для передачи 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; ... 

      Это обычное, но несколько «хакерское» решение, основанное на том, что указатель имеет хотя бы ширину, чем 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() на <0 для ошибки, поскольку 0 является допустимым значением для дескриптора файла / сокета.