Не получает передачу файлов из TCP-соединения

#c #sockets #tcp #network-programming

#c #сокеты #tcp #сетевое программирование

Вопрос:

Я пишу протокол TCP-клиента и сервера для школьного проекта. Клиент отправляет сообщение «GET r n», и сервер должен передать » OKrn», размер файла и сам файл, если он существует в каталоге сервера. Я заблокирован при передаче файлов

Я пытался решить это небольшими шагами за раз. Я настроил соединение, отправил запрос от клиента и получил сообщение «OK» от сервера.

Теперь я открыл файл на сервере и попытался отправить его клиенту по 128 байт за раз. Чтение файла работает и, по-видимому, также отправка буферов, но клиент ничего не получает…

Вот мой сервер.c

 
#include    <stdlib.h>
#include    <string.h>
#include    <inttypes.h>

#include    "../errlib.h"
#include    "../sockwrap.h"

#define BUFLEN      128 /* Buffer length */
#define TIMEOUT     15  /* TIMEOUT */

/* FUNCTION PROTOTYPES */
void service(int s);

/* GLOBAL VARIABLES */
char *prog_name;

int main(int argc, char *argv[])
{
    int         conn_request_skt;   /* passive socket */
    uint16_t    lport_n, lport_h;   /* port used by server (net/host ord.) */
    int         bklog = 2;          /* listen backlog */
    int         s;                  /* connected socket */

    fd_set      cset;               // waiting for connection
    struct timeval  tval;           // timeout
    size_t      n;

    socklen_t   addrlen;
    struct sockaddr_in  saddr, caddr;   /* server and client addresses */

    prog_name = argv[0];

    if (argc != 2) {
        printf("Usage: %s <port number>n", prog_name);
        exit(1);
    }

    /* get server port number */
    if (sscanf(argv[1], "%" SCNu16, amp;lport_h)!=1)
    err_sys("Invalid port number");
    lport_n = htons(lport_h);

    /* create the socket */
    printf("creating socket...n");
    s = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    printf("done, socket number %un",s);

    /* bind the socket to any local IP address */
    bzero(amp;saddr, sizeof(saddr));
    saddr.sin_family      = AF_INET;
    saddr.sin_port        = lport_n;
    saddr.sin_addr.s_addr = INADDR_ANY;
    showAddr("Binding to address", amp;saddr);
    Bind(s, (struct sockaddr *) amp;saddr, sizeof(saddr));
    printf("done.n");

    /* listen */
    printf ("Listening at socket %d with backlog = %d n",s,bklog);
    Listen(s, bklog);
    printf("done.n");

    conn_request_skt = s;

    /* main server loop */
    for ( ; ; )
    {
        printf("waiting for connection...n");
        /* accept next connection */
        FD_ZERO(amp;cset);
        FD_SET(conn_request_skt, amp;cset);
        tval.tv_sec = TIMEOUT;
        tval.tv_usec = 0;
        n = Select(FD_SETSIZE, amp;cset, NULL, NULL, amp;tval);

        if ( n > 0 ){
            addrlen = sizeof(struct sockaddr_in);
            s = Accept(conn_request_skt, (struct sockaddr *) amp;caddr, amp;addrlen);
            showAddr("Accepted connection from", amp;caddr);
            printf("new socket: %un",s);

            /* serve the client on socket s */
            service(s);
        } else {
            printf("No connection request after %d secondsn",TIMEOUT);

        }
    }
}

void service(int s) {
    char    buf[BUFLEN];        /* reception buffer */
    char    filename[BUFLEN];

    int     n;
    long    filesize;
    uint32_t fsize;

    FILE *fp;

    for ( ; ; )
    {
        n = recv(s, buf, BUFLEN, 0);
        if (n < 0) {
            printf("Read errorn");
            close(s);
            printf("Socket %d closedn", s);
            break;
        } else if (n == 0) {
            printf("Connection closed by party on socket %dn",s);
            close(s);
            break;
        } else {
            printf("Received request from socket d :n", s);
            sscanf(buf, "GET %srn", filename);
            strcpy(buf, " OKrn");
            printf("%s",buf);
            if(writen(s, buf, strlen(buf)) != strlen(buf))
                printf("Write error while sending  OKn");

            // open file
            fp = fopen(filename, "r");
            if( fp == NULL){
                //TODO close connection
            }
            // calculating dim of file
            fseek(fp, 0L, SEEK_END);
            filesize = ftell(fp);
            rewind(fp); // go back at beginning of file

            fsize = htonl(filesize); // size file in network byte order

            // sending file size
            if(writen(s, amp;fsize, 4) != 4)
                printf("Write error while sending file sizen");

            while(fread(buf, 1, BUFLEN - 1, fp) == BUFLEN - 1){
                printf("%s", buf);
                if(writen(s, buf, strlen(buf)) != strlen(buf))
                    printf("Write error while bufn");
            }
            printf("%s", buf);
            printf("I am heren");
        }
    }
}


  

Пока здесь мой клиент.c

 
#include     <stdlib.h>
#include     <string.h>
#include     <inttypes.h>

#include     "../errlib.h"
#include     "../sockwrap.h"

#define BUFLEN  128 /* BUFFER LENGTH */
#define TIMEOUT 15  /* TIMEOUT*/

/* GLOBAL VARIABLES */
char *prog_name;

int main(int argc, char *argv[])
{
    char        request[BUFLEN];        /* request buffer */
    char        rbuf[BUFLEN];       /* reception buffer */

    uint32_t        taddr_n;            /* server IP addr. (net/host ord) */
    uint16_t        tport_n, tport_h;   /* server port number (net/host ord) */

    int        s, len;
    int        resu<
    struct sockaddr_in  saddr;      /* server address structure */
    struct in_addr  sIPaddr;        /* server IP addr. structure */

    fd_set      cset;               // variables for timeout
    struct timeval  tval;
    size_t n;

    prog_name = argv[0];

    if(argc < 4)
        err_sys("Wrong number of parameters!n");

    // read address from first argument
    taddr_n = inet_addr(argv[1]);
    if (taddr_n == INADDR_NONE)
        err_sys("Invalid address");

    // read port number from second argument
    if (sscanf(argv[2], "%" SCNu16, amp;tport_h)!=1)
    err_sys("Invalid port number");
    tport_n = htons(tport_h);

    /* create the socket */
    printf("Creating socketn");
    s = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    printf("done. Socket fd number: %dn",s);

    /* prepare address structure */
    bzero(amp;saddr, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port   = tport_n;
    saddr.sin_addr   = sIPaddr;


    printf("trying to connect to the server...n");

    /* connect */
    showAddr("Connecting to target address", amp;saddr);
    Connect(s, (struct sockaddr *) amp;saddr, sizeof(saddr));
    printf("done.n");

    // loop to request files
    for (int i = 3 ; i < argc ; i   ){ // i = 4 because the first file is the fourth argument
        // check if file name is too big
        if(strlen(argv[i]) >= BUFLEN - 6)
            err_sys("The file name is too big for the buffer request!n");

        // create the string of bytes for the request
        strcpy(request, "GET ");
        strcat(request, argv[i]);
        strcat(request, "rn");

        len = strlen(request);
        if(writen(s, request, len) != len){
            printf("Write errorn");
            break;
        }

        printf("waiting for response...n");
        // receive file from server
        n = recv(s, rbuf, BUFLEN, 0);
        if (n < 0) {
            printf("Read errorn");
            close(s);
            printf("Socket %d closedn", s);
            break;
        } else if (n == 0) {
            printf("Connection closed by party on socket %dn",s);
            close(s);
            break;
        } else {
            printf("Received reply from servern");

            uint32_t fsize;

            printf("%s",rbuf);
            if(strcmp(rbuf, " OKrn") == 0){
                n = recv(s, amp;fsize, 4, 0);
                if (n < 0) {
                    printf("Read errorn");
                    close(s);
                    printf("Socket %d closedn", s);
                    break;
                } else if (n == 0) {
                    printf("Connection closed by party on socket %dn",s);
                    close(s);
                    break;
                } else {
                    // received file dimension
                    fsize = ntohl(fsize);
                }

                while(fsize > 0){
                    printf("I am here1nn");
                    // receive file
                    n = recv(s, rbuf, BUFLEN-1, 0);
                    if (n < 0) {
                        printf("Read errorn");
                        close(s);
                        printf("Socket %d closedn", s);
                        break;
                    } else if (n == 0) {
                        printf("Connection closed by party on socket %dn",s);
                        close(s);
                        break;
                    } else {
                        printf("I am here");
                        fsize -= n;
                    }
                }

            }
        }
    }

    printf("===========================================================n");
    close(s);
    exit(0);
}

  

Recv в клиенте, где я должен получать файл, просто блокируется, ничего не получая. Я не понимаю, чего мне не хватает…

Комментарии:

1. Насколько велик файл, который вы пытаетесь отправить?

2. Также можете ли вы опубликовать вывод после запуска кода?

3. Файл имеет размер 79 байт, и вывод с сервера: « создание сокета … выполнено, привязка сокета номер 3 к адресу 0.0.0.0!2048 выполнено. Прослушивание в сокете 3 с невыполненной работой = 2 выполнено. ожидание соединения… Принято соединение от 127.0.0.1!50646 новый сокет: 4 Получен запрос от сокета 004: OK привет всем, это пример текстового файла, чтобы посмотреть, работают ли клиент и сервер??? Я здесь «

4. Клиент: « Создание сокета завершено. Номер fd сокета: 3 пытается подключиться к серверу… Подключение к целевому адресу 0.0.0.0!2048 выполнено. ожидание ответа… Получен ответ от сервера ОК, я здесь 1. Соединение закрыто стороной на сокете 3 «

5. while (fread(buf, 1, BUFLEN - 1, fp) == BUFLEN - 1) недопустимо. Файл короче, чем ваш буфер, и даже если это не последнее чтение, почти наверняка будет. Итак, ничего не было написано. Вы должны использовать read() для файла; вы должны использовать положительное число, возвращаемое read() в качестве длины для записи, как на сервере, так и на клиенте; вы не должны предполагать, что конечные нули с strlen() ; и вы не должны предполагать, что read() заполняет буфер.

Ответ №1:

Проблема здесь общая: вы не соблюдаете границы сообщений.

В вашем клиенте вы выполняете recv и проверяете, больше ли количество байтов, чем 0. Но тогда вы не выполняете дополнительную проверку длины. Затем вы выполняете a strcmp для конкретной строки, которую вы ожидаете получить ( OKrn ). Но вы могли получить 3 байта ( OK ) или вы могли получить 10: ( OKrnXXXXX ) или более [в сторону: также, recv не гарантирует, что ваша строка байтов заканчивается нулем]. Ничто не мешает ядру на обратной стороне от пакетной обработки преамбулы плюс последующие байты в один TCP-пакет. Аналогично, ничто не мешает локальной стороне объединять несколько пакетов TCP в один буфер.

Вы должны указать границы сообщений. Если вы ожидаете, что ваше следующее сообщение будет размером 5 байт, то вы должны получить ровно 5 байт (и повторить попытку, если вы получите меньше — будьте осторожны, чтобы проверить EOF также на случай, если другая сторона прервала работу раньше). Или, в качестве альтернативы, вставьте слой буферизации перед вашей логикой приема, чтобы он получал до некоторого большого количества, возвращал вам нужное количество байтов, а затем сохранял все, что есть в избытке, для последующего вызова «receive».

Чтобы повторить это по-другому: ваш сервер отправляет OKrn , затем он отправляет длину в четыре байта, затем он начинает отправлять файл. Но это означает, что ваш первый recv на стороне клиента мог получать преамбулу, плюс длину, плюс первые N байтов файла за один системный вызов.

TCP не соблюдает, не предоставляет и не применяет границы сообщений.

Комментарии:

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

2. Большое спасибо, проблема была там