Как исправить «Ошибка чтения из сокета - неверный дескриптор файла» в то время как в потоке на Linux с использованием C? - PullRequest
0 голосов
/ 04 января 2019

Я устанавливаю сервер-клиентское приложение (чат) и столкнулся с проблемой с сокетами / потоками. И сервер, и клиент работают в Linux.

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

Это сервер : main.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "manazerUctov.h"

int pocetVlakienZOP = 0;

int main(int argc, char *argv[])
{
    pthread_t* vlaknaZiadosti = 
    (pthread_t*)malloc(sizeof(pthread_t)*POCET_UZIVATELOV);

    int sockfd, newsockfd;
    socklen_t cli_len;
    struct sockaddr_in serv_addr, cli_addr;
    int n;

    // Program
    if (argc < 2) {
        fprintf(stderr, "usage %s port\n", argv[0]);
        return 1;
    }

    bzero((char*) &serv_addr, sizeof (serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(atoi(argv[1]));

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("Error creating socket\n");
        return 1;
    }
    printf("Socket OK\n");

    if (bind(sockfd, (struct sockaddr*) &serv_addr, sizeof (serv_addr)) < 0) {
        perror("Error binding socket address\n");
        return 2;
    }
    printf("Bind OK\n");

    listen(sockfd, 5);
    while (1) {

        cli_len = sizeof (cli_addr);

        newsockfd = accept(sockfd, (struct sockaddr*) &cli_addr, &cli_len);
        if (newsockfd < 0) {
            perror("ERROR on accept\n");
            return 3;
        }
        printf("Accept OK\n");
        client_data* cdata = (client_data*) malloc(sizeof (client_data));
        cdata->socket = newsockfd;

        pthread_t vlakno;
        vlaknaZiadosti[pocetVlakienZOP++] = vlakno;
        pthread_create(&vlakno, NULL, &serveClient, &cdata);
    }

    close(sockfd);
    return 0;
}

manazerUctov.h

#ifndef MANAZERUCTOV_H
#define MANAZERUCTOV_H

#ifndef POCET_UZIVATELOV
#define POCET_UZIVATELOV 256
#endif

#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
    char *buffer;
    char* msg;                         
    int socket;
} client_data;

void* serveClient(void* pdata);

#ifdef __cplusplus
}
#endif

#endif /* MANAZERUCTOV_H */

manazerUctov.c Здесь функция чтения выдает ошибку:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "manazerUctov.h"

void* serveClient(void *pdata) {
    char buffer[256];
    client_data* client = (client_data*)pdata;
    bzero(buffer, 256);

    int n = read(client->socket, buffer, 255);
    if (n < 0) {
        perror("Error reading from socket\n");
        return NULL;
    }

    printf("Read from socket\n");
    close(client->socket);
}

Когда клиент отправляет сокет, сервер принимает его и создает поток, но я получаю Ошибка чтения из сокета: неверный дескриптор файла . И программа застряла, она даже не выйдет. И, насколько мне известно, розетки еще не закрыты. До того, как я реализовал потоки, чтение из сокетов работало правильно.

РЕДАКТИРОВАТЬ: Исходный код имеет длину 500 строк, поэтому я обрезал его, и прежней ошибки больше не было, хотя другая ошибка была & cdata в pthread_create . Устранение и устранение проблемы как в усеченном, так и в исходном коде, и сервер работает.

1 Ответ

0 голосов
/ 05 января 2019

Ваш pthread_create вызов функции неверен .

Это передает указатель на client_data указатель и не , на что указывает указатель:

pthread_create(&my_thread, NULL, &my_function, &cdata);

То, что вы хотите:

pthread_create(&my_thread, NULL, &my_function, cdata);

Таким образом, вместо получения cdata->socket вы получите младшие 32 бита значения указателя в качестве дескриптора файла.


Некоторые дополнительные примечания:

Поскольку у вас есть область видимости цикла pthread_t my_thread;, он исчезает при каждой итерации цикла, поэтому основной поток не сможет выполнить pthread_join для него.

Таким образом, когда ваша функция потока завершается, она будет в не присоединенном (то есть зомби) состоянии.

Кроме того, на каждой итерации каждый вызов pthread_create может указывать на my_thread по тому же адресу . Я не уверен, как это повлияет на другие потоковые примитивы, но я буду осторожен.

Вы можете добавить pthread_t mythread; к выделенной структуре. Таким образом, он гарантированно имеет уникальный адрес. И основной поток может отслеживать выделенные им структуры.

Другой способ справиться с этим - заставить вашу функцию потока сделать pthread_detach вверху [или добавить соответствующие атрибуты к вызову pthread_create]

Это может быть проще, чище.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...