简体   繁体   English

错误 - 接受()失败:错误的文件描述符

[英]error - accept() failed: Bad file descriptor

I have to implement two processes that have to interact through TCP, basically a file transfer between a server and a client (school homework).我必须实现两个必须通过 TCP 交互的进程,基本上是服务器和客户端之间的文件传输(学校作业)。 Everything work for the first file transmission but when the client ends its job the server crashes because of error - accept() failed: Bad file descriptor .一切都适用于第一次文件传输,但是当客户端结束其作业时,服务器因error - accept() failed: Bad file descriptor崩溃error - accept() failed: Bad file descriptor

server_main.c server_main.c

int main (int argc, char *argv[]) {

    char cwd[100];

    if (getcwd(cwd, sizeof(cwd)) != NULL) {
       printf("App[starting]: " ANSI_COLOR_CYAN "%s" ANSI_COLOR_RESET "\n", cwd);
    } else {
       printf("App[quitting]: " ANSI_COLOR_RED "UNABLE TO LOCATE WDIR" ANSI_COLOR_RESET "\n");
       return 1;
    }

    // procedo solo se vengono passati esattamente due parametri allo script
    // 1. il nome dello script (default)
    // 2. la porta
    if (argc == 2) {
        int passiveSocket = startTcpServer(argv[1]);
        runIterativeTcpInstance(passiveSocket);
        close(passiveSocket);
        return 0;
    }

    // se arrivo qui ho app crash
    printf("App[quitting]: " ANSI_COLOR_RED "USAGE: <PORT>" ANSI_COLOR_RESET "\n");

    return 1;
}

gj_server.c gj_server.c

int startTcpServer(const char* port) {

    uint16_t i_port;
    int sockfd;

    sockfd = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    parsePort(port, &i_port);
    bindToAny(sockfd, i_port);
    Listen(sockfd, 516);
    printf("Server[listening]: " ANSI_COLOR_GREEN "PORT = %d, BACKLOG = 516" ANSI_COLOR_RESET "\n", (int) i_port);

    return sockfd;
}

void runIterativeTcpInstance(int passiveSock) {

    struct sockaddr_in cli_addr;
    socklen_t addr_len = sizeof(struct sockaddr_in);

    while(1) {
        printf("Server[accepting]: " ANSI_COLOR_YELLOW "WAITING FOR A CONNECTION..." ANSI_COLOR_RESET "\n");
        int connSock = Accept(passiveSock , (struct sockaddr *) &cli_addr, &addr_len);
        doTcpJob(connSock);
        close(connSock);
    }

}

void doTcpJob(int connSock) {
    //TODO: uscire in caso di errore di una delle due fasi
    printf("Server[connection]:" ANSI_COLOR_GREEN "STARTED A NEW ON CONNECTION (PID=%d)" ANSI_COLOR_RESET "\n", getpid());
    char request[256];
    initStr(request, 256);
    if(doTcpReceive(connSock, request) == 0)
        doTcpSend(connSock, request);
    else {
        printf("Server[error]: " ANSI_COLOR_RED "INVALID REQUEST FROM CLIENT: %s" ANSI_COLOR_RESET "\n", request);
        char err_buff[7] = "-ERR\r\n";
        send(connSock, err_buff, 6, 0);
        //close(connSock);
    }

//close(connSock); //关闭(connSock); } }

int doTcpReceive(int connSock, char *request) {

    struct timeval tval;
    fd_set cset;
    FD_ZERO(&cset);
    FD_SET(connSock, &cset);
    tval.tv_sec = 15;
    tval.tv_usec = 0;
    if(Select(FD_SETSIZE, &cset, NULL, NULL, &tval) == 1) {
        // TODO: INSERIRE LOGICA RECEIVE QUI
        ssize_t read = 0;
        while (reqCompleted(request) == -1 ) {
         ssize_t received = Recv(connSock, request, 256, 0); 
         read += received;
        }
        printf("Server[receive]: " ANSI_COLOR_CYAN "RECEIVED %s" ANSI_COLOR_RESET "\n", request);
        return checkRequest(request);
    }
    // esco per timeout
    //close(connSock);
    return -1;
}

void doTcpSend(int connSock, char *request) {
    // TODO: INSERIRE LOGICA SEND QUI

    // 1. send ok message
    char ok_msg[6] = "+OK\r\n";
    send(connSock, ok_msg, 5, 0);

    // 2. send file size
    FILE *fp = fopen(request, "rb+");

    // qui il file dovrebbe esistere, ma potrebbe essere inacessibile o
    // l'apertura potrebbe fallire
    if (fp == NULL) {
        printf("SERVER[READING]" ANSI_COLOR_RED "CANNOT OPEN FILE %s " ANSI_COLOR_RESET "\n", request);
        char err_buff[7] = "-ERR\r\n";
        send(connSock, err_buff, 6, 0);
        //close(connSock);
        return;
    }

    struct stat stat_buf;

    if (fstat(fileno(fp), &stat_buf) == 0) {

        long f_size = stat_buf.st_size;
        long f_time = stat_buf.st_mtime;
        uint32_t net_f_size = htonl(f_size);
        send(connSock, &net_f_size, 4, 0);

        // 3. send file content
        ssize_t sent = 0;
        printf("fsize: %d", (int)f_size);
        while (sent < f_size) {
            sent += sendfile(connSock, fileno(fp), NULL, f_size);
            showProgress((int)sent, (int)f_size, "Server[sending]: ");
        }

        fclose(fp);

        // 4. send file timestamp
        uint32_t net_f_time = htonl(f_time);
        send(connSock, &net_f_time, 4, 0); 
        //close(connSock);

    } else {
        char err_buff[7] = "-ERR\r\n";
        send(connSock, err_buff, 6, 0);
        //close(connSock);
        return;
    }


}

int reqCompleted(char *request) {
    int len = strlen(request);
    return len > 6 && request[len - 2] == '\r' && request[len - 1] == '\n' ? 0 : -1;
}

int checkRequest(char *request) {
    int len = strlen(request);
    if (len > 6 && request[0] == 'G' && request[1] == 'E' && request[2] == 'T'
        && request[3] == ' ' && request[len - 2] == '\r' && request[len - 1] == '\n') {
            memcpy(request, request + 4, (len - 6)); // estraggo il nome del file dalla richiesta
            request[len - 6] = '\0';
            return access(request, F_OK); // verifico che il file esista nella cartella di lavoro
        }
    return -1;
}

the parameter called passiveSock causes the problem, at the second iteration of the while loop, I'm quite new in C and I think it's relative to life cycle of a variable.名为passiveSock的参数导致了这个问题,在while循环的第二次迭代中,我在C中很新,我认为它与变量的生命周期有关。 I've already tried to print passiveSock before the accept call and gives me an error.我已经尝试在接受调用之前打印passiveSock并给我一个错误。 This is the valgrind output:这是 valgrind 的输出:

==1== Syscall param accept(s) contains uninitialised byte(s)
==1==    at 0x4F21990: __accept_nocancel (syscall-template.S:84)
==1==    by 0x10A7D4: Accept (sockwrap.c:97)
==1==    by 0x109AA5: runIterativeTcpInstance (gj_server.c:28)
==1== 
((null)) error - accept() failed: Bad file descriptor
==1== 
==1== HEAP SUMMARY:
==1==     in use at exit: 0 bytes in 0 blocks
==1==   total heap usage: 2 allocs, 2 frees, 1,576 bytes allocated
==1== 
==1== All heap blocks were freed -- no leaks are possible
==1== 
==1== For counts of detected and suppressed errors, rerun with: -v
==1== Use --track-origins=yes to see where uninitialised values come from
==1== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

EDIT sockwrap.c Accept definition编辑 sockwrap.c 接受定义

int Accept (int listen_sockfd, SA *cliaddr, socklen_t *addrlenp)
{
    int n;
again:
    if ( (n = accept(listen_sockfd, cliaddr, addrlenp)) < 0)
    {
        if (INTERRUPTED_BY_SIGNAL ||
            errno == EPROTO || errno == ECONNABORTED ||
            errno == EMFILE || errno == ENFILE ||
            errno == ENOBUFS || errno == ENOMEM         
            )
            goto again;
        else
            err_sys ("(%s) error - accept() failed", prog_name);
    }
    return n;
}

initStr and showProgress initStr 和 showProgress

void initStr(char* string, int length) {
    memset(string, '\0', length);
    string[length] = '\0';
}

void showProgress(int done, int tot, char * progMsg) {
    int progress = ((double) done / (double) tot) * 100;
    printf("\r%s " ANSI_COLOR_CYAN "%d bytes (%d%%)" ANSI_COLOR_RESET, progMsg, done, progress);
    fflush(stdout);
}

Of course the bug was in the first function I wanted to check.当然,错误在我想检查的第一个函数中。 Look closely:仔细看:

    void initStr(char* string, int length) {
        memset(string, '\0', length);
        string[length] = '\0'; // <-- BOOM!
    }

But:但:

    char request[256];
    initStr(request, 256);

So request is an array with 256 entries.所以request是一个有 256 个条目的数组。 But initStr writes into the 257th entry, which does not exist.但是initStr写入第 257 个条目,该条目不存在。

You should get rid of this function and never use anything even remotely like it.你应该摆脱这个功能,永远不要使用任何像它这样的东西。 Filling an entire buffer with zeroes serves no purpose.用零填充整个缓冲区没有任何意义。 Instead, properly track the length of the data in the buffer and stop calling strlen all the time.相反,正确跟踪缓冲区中数据的长度并停止一直调用strlen There are other bugs in your code because of this, much more subtle ones.因此,您的代码中还有其他错误,更微妙的错误。

For example, you try to read 256 bytes from the connection.例如,您尝试从连接中读取 256 个字节。 This could fill your entire buffer, replacing all the zero bytes.这可能会填满整个缓冲区,替换所有零字节。 You then pass the buffer to strlen -- but there is no guarantee there is a zero byte anywhere in the buffer.然后将缓冲区传递给strlen —— 但不能保证缓冲区中的任何地方都有零字节。

Data received over a network connection is not a string.通过网络连接接收的数据不是字符串。 It's raw data with a known length that is returned from the read or recv function.它是从readrecv函数返回的已知长度的原始数据。 Don't pretend it is a string.不要假装它是一个字符串。 It's a bad habit that will bite you again and again and again.这是一个坏习惯,会一次又一次地咬你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM