Плохой файловый дескриптор с сокетом BSD

Я продолжаю получать сообщение об ошибке «Неверный дескриптор файла», когда пытаюсь отправить данные с моего TCP-сервера на мой TCP-клиент. Что это означает с точки зрения сокетов? Я занимаюсь этим некоторое время и не вижу, что может быть не так с моим кодом. В основном это тот же код, который я использовал два дня назад, и этот код работал нормально. Я надеялся, что кто-нибудь может сказать мне, каковы общие причины плохих файловых дескрипторов при попытке отправить через сокет и как я могу их проверить/исправить. Любая помощь приветствуется. Я опубликую код ниже, если это поможет.

/*Waits to connect a client. Returns true if successful*/
bool TcpServer::launchServer() {
int status;

struct addrinfo hints;
struct addrinfo *servinfo;  //will point to the results

//store the connecting address and size
struct sockaddr_storage their_addr;
socklen_t their_addr_size;


memset(&hints, 0, sizeof hints); //make sure the struct is empty
hints.ai_family = AF_INET;  //ipv4
hints.ai_socktype = SOCK_STREAM; //tcp

//get server info, put into servinfo
if ((status = getaddrinfo("192.168.2.3", port, &hints, &servinfo)) != 0) {
    printf("\ngetaddrinfo error: %m", errno);
    return false;
}

//make socket
fd = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
if (fd < 0) {
    printf("\nserver socket failure %m", errno);
    return false;
}

//allow reuse of port
int yes=1;
if (setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char*) &yes,sizeof(int)) == -1) {
    perror("setsockopt");
    return false;
}

//bind
if(bind (fd, servinfo->ai_addr, servinfo->ai_addrlen) < 0) {
    printf("\nBind error %m", errno);
    return false;
}

//free up space
freeaddrinfo(servinfo);

//listen
if(listen(fd, 5) < 0) {
    printf("\nListen error %m", errno);
    return false;
}
their_addr_size = sizeof(their_addr);


//accept
comm_fd = accept(fd, (struct sockaddr*)&their_addr, &their_addr_size);
if( comm_fd < 0) {
    printf("\nAccept error %m", errno);
    return false;
}

return true;
}   //END LAUNCHSERVER






void TcpServer::communicate() {


fd_set read_flags,write_flags; // the flag sets to be used
struct timeval waitd = {10, 0};          // the max wait time for an event
int sel;        // holds return value for select();
int numRead;    //holds return value for read()
int numSent;    //holds return value for send()
char in[255];   //in buffer
char out[255];  //out buffer

//clear buffersz
memset(&in, 0, 255);
memset(&out, 0, 255);


while(!done) {
    FD_ZERO(&read_flags);
    FD_ZERO(&write_flags);
    FD_SET(comm_fd, &read_flags);
    FD_SET(comm_fd, &write_flags);
    FD_SET(STDIN_FILENO, &read_flags);
    FD_SET(STDIN_FILENO, &write_flags);

    //call select
    sel = select(comm_fd+1, &read_flags, &write_flags, (fd_set*)0, &waitd);

    //if an error with select
    if(sel < 0)
        continue;

    //if socket ready for reading
    if(FD_ISSET(comm_fd, &read_flags)) {

        //clear set
        FD_CLR(comm_fd, &read_flags);

        memset(&in, 0, 255);

        numRead = recv(comm_fd, in, 255, 0);
        //if an error, exit
        if(numRead < 0) {
            printf("\nError reading %m", errno);
            myAgent->getRobot()->pauseSensorStream();
            done = true;
        }   //end if error
        //if connection closed, exit
        else if(numRead == 0) {
            printf("\nClosing socket");
            close(comm_fd);
            done = true;
        }   //end if connection closed
        //if message, call getsendback
        else if(in[0] != '\0') {
            //std::cout<<"\nClient: "<<in;
            getSendBack(in);
        }   //end if message
    }   //end if ready for read


    //if stdin is ready for reading
    if(FD_ISSET(STDIN_FILENO, &read_flags))
        fgets(out, 255, stdin);


    //if socket ready for writing
    if(FD_ISSET(comm_fd, &write_flags)) {

        //printf("\nSocket ready for write");
        FD_CLR(comm_fd, &write_flags);

        //check validity by checking for a digit
        if(isdigit(out[0])) {

            //create message to send
            std::stringstream tosend;
            tosend<<"@ "<<out;
            //std::cout<<"\ntosend: "<<tosend.str();

            //send
            //********ERROR HAPPENS HERE PRINTS OUT MESSAGE BELOW******
            numSent = send(comm_fd, tosend.str().c_str(), tosend.str().length(), 0);
        }   //end if valid message
        //if error, exit
        if(numSent < 0) {
            printf("\nError sending %m", errno);
            done = true;
        }   //end if error
        //wait for message to get there, then clear
        usleep(5000);
        memset(&out, 0, 255);
    }   //end if
}   //end while
}   //END COMMUNICATE

Клиентский код в основном такой же.


person Sterling    schedule 19.08.2011    source источник
comment
Можете ли вы напечатать fd прямо перед send ?   -  person cnicutar    schedule 19.08.2011
comment
Я сделал, и значение comm_fd равно 4.   -  person Sterling    schedule 19.08.2011
comment
Ваш код печатает закрытие сокета перед сообщением об ошибке?   -  person SKi    schedule 19.08.2011
comment
Ошибка означает, что файловый дескриптор уже был закрыт. Как только вы его закроете, использовать дескриптор файла больше нельзя.   -  person mark4o    schedule 19.08.2011


Ответы (2)


Вы ответили на свой вопрос. Без явной инициализации numSent и numRead вы получите мусор, который может оказаться отрицательным числом для numSent, что приведет к ошибке, если в массиве out[] не будет цифры.

person chmeee    schedule 25.08.2011

Ваша программа печатает «Bad file descriptor», когда errno равно EBADF. С справочной страницы отправки:

EBADF = указан недопустимый дескриптор.

Я совершенно уверен, что сокет закрывается перед вызовом send(). Это может произойти, потому что программа может перейти в ветку «готово к записи» после ветки «соединение закрыто».

Попробуйте следующее:

else if(numRead == 0) {
    printf("\nClosing socket");
    close(comm_fd);
    break;
} 

Вместо:

    else if(numRead == 0) {
        printf("\nClosing socket");
        close(comm_fd);
        done = true;
    }
person SKi    schedule 19.08.2011
comment
Да, но ваш код нуждается в перерыве. Я обновил свой ответ, чтобы легче было увидеть предлагаемое изменение. - person SKi; 19.08.2011
comment
Это ничего не изменило. С чего бы это? Чтобы быть более ясным, ошибка возникает в начале моей программы. У меня есть сообщения, которые отправляются каждые 0,2 секунды, и после отправки первого сообщения выдает ошибку. - person Sterling; 19.08.2011
comment
Как видите, код может вызывать send() после close(). Если это не решило проблему, значит, есть и другая проблема. - person SKi; 19.08.2011
comment
Я не уверен, почему это сработало, но если я инициализирую numSent и numRead равными 0, я больше не получаю ошибку. Я предполагаю, что это просто переход к оператору if до того, как они были инициализированы чем-либо? - person Sterling; 19.08.2011