Сокетное общение на языке Си.Отправка содержимого файла - PullRequest
0 голосов
/ 10 декабря 2018

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

Я начал думать об этом немного больше, и я решил отправиться и попытаться отправить содержимое конкретного файла сервер --> клиент (сервер отправляет содержимое клиенту).Пока я создавал код, который я часто тестировал локально, и он работал как задумано, настоящая проблема возникла, когда я загрузил код сервера на свой сервер (ха), на котором работает Ubuntu.Запустил сервер, все ок, запустил клиент, запросил «index.html» и BAM! половина файла не была получена.Сервер распечатывает его (я сделал так, чтобы он печатал содержимое файла в том виде, в котором он был отправлен, чтобы мне было проще устранять неполадки).

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

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

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

Клиентская сторона

printf("Please specify the filename: ");
fgets(msg,1000,stdin); // get message from std input

if(strcmp(msg,"\n")==0) {
    printf("Wrong file name or format\n");
    printf("Please specify the filename: ");
    fgets(msg,1000,stdin); // get message from std input
}


while(strcmp(msg,"!stop\n")) {

    msg[strlen(msg)-1]='\0';
    write(sockfd,msg,strlen(msg));
    FILE *fp = NULL;

    char filecontent[1000];
    bzero(filecontent,sizeof(filecontent));


    while(  (n = read(sockfd,filecontent,1000)) && strcmp(filecontent,"Over and out!")!=0  ) {

        if(strcmp(filecontent,"No such file")!=0 && fp == NULL) {
            fp = fopen(msg,"w");
        }

        printf("%s",filecontent);

        if(fp !=NULL)
            fprintf(fp, "%s",filecontent);

        bzero(filecontent,sizeof(filecontent));
    }

    if(fp != NULL)
        fclose(fp);

    printf("\nPlease specify the filename: ");
    fgets(msg,1000,stdin); // get message from std input


    if(strcmp(msg,"\n")==0) {
        printf("Wrong file name or format\n");
        printf("Please specify the filename: ");
        fgets(msg,1000,stdin); // get message from std input
    }

}

Серверная часть

   char date[50];
time_t ticks;
struct tm *tinfo;
time(&ticks);
tinfo=localtime(&ticks);
strcpy(date,asctime(tinfo));
printf("DATA: %s\n",date);
write(newsocketfd,date,sizeof(date));

while( (n = read(newsocketfd,msg,1000)) && strcmp(msg,"!stop\n")!=0) {
    //printf("MSG: %s\n",msg);

    if(n<0)
        error("ERROR READING");
    /////////READING FILE/////////////


    char *filename = malloc(sizeof(msg)+1);
    strcpy(filename,msg);
    printf("'server filename:%s'\n",filename);
    FILE *fp = fopen( filename,"r");

    if(fp == NULL) {
        printf("No such file found\n");
        write(newsocketfd,"No such file",sizeof("No such file"));
    }

    while( fp!=NULL && fgets(msg,1000,fp)!=NULL){
        write(newsocketfd,msg,sizeof(msg));
        msg[strlen(msg)-1]='\0';
        printf("server: '%s'\n",msg);
        bzero(msg,sizeof(msg));

    }


bzero(msg,sizeof(msg));
bzero(filename,strlen(filename));
n = write(newsocketfd,"Over and out!",sizeof("Over and out!"));
printf("Over\n");

}

извините за любые головные боли. Полный код здесь.

Примеры:

Я думаю, что это в значительной степени показывает проблему

Я думал, что сервер читает файл, строка за строкой, и отправляет его, строка за строкой, клиенту, когда это сделано, сервер отправляет сообщение «over», и клиент перестает читать оттуда, однако кажется, что клиент никогдаполучает всю информацию или сигнал «за».Стоит добавить, что это прекрасно работает, если я запускаю оба кода на своей локальной машине.

1 Ответ

0 голосов
/ 10 декабря 2018

Добро пожаловать в мир сетевого программирования!Сетевые протоколы являются многоуровневыми по причине.Когда вы отправляете что-либо в сокет TCP и сразу же закрываете сокет, доставка ненадежна: она может быть правильно доставлена ​​одноранговому узлу или может исчезнуть из-за условий гонки.

Единственный надежный способ - это толькозакройте сокет, когда узел отправляет подтверждение, что он может получить все, что было отправлено.В стандартном протоколе для этого используются управляющие сообщения, и вам действительно следует обдумать это, но если вам не нужно, чтобы ваш сервер предупреждался о сбоях клиента, вы могли бы просто попросить клиента закрыть соединение, когда оно получило "Over and out!".Кстати, вы должны знать, что, поскольку TCP является потоковым протоколом, ничто не может гарантировать, что сообщение не будет разбито более чем на одно чтение или объединено с другими байтами.Таким образом, вам следует сохранить конец предыдущего чтения (размер строки сигнала минус один байт), объединить следующее чтение с этим и найти строку в любом месте буфера.

Другой распространенный способ - использовать * 1006.* постепенное завершение работы : отправитель использует shutdown(socket.SHUT_WR), чтобы сигнализировать, что связь завершена без закрытия сокета и ждет (с read), чтобы узел закрыл сокет, когда все былоправильно доставлено.

...