Я впервые занимаюсь программированием сокетов, и у меня есть вопрос о send и recv. Я отправляю файл с сервера клиенту, и он работает нормально. Но когда я хочу продолжить свою программу с большим количеством вызовов send () и recv () (закомментированный код в конце клиента и сервера), клиент получает эти данные и записывает их в newfile.txt.

Итак, мой вопрос: как мне вызвать send () с сервера, чтобы отправлять данные только из test.txt, а затем recv () на клиенте, чтобы получать и записывать данные только из test.txt в newfile.txt? После этого я хотел бы продолжить свою программу с большим количеством вызовов send () и recv (), которые не смешиваются с кодом передачи файлов.

Клиент:

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>

int main(int argc, char *argv[]){
    int port = 8001;
    int client_socket;
    struct sockaddr_in server_addr;
    int connect_status;

    client_socket = socket(AF_INET, SOCK_STREAM, 0);    //SOCK_STREAM for TCP
    if (client_socket < 0)
    {
        printf("Error creating socket\n");
        exit(1);
    }
    printf("Socket created\n");

    //address for socket to connect to
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);             //8001 arbitrary port
    server_addr.sin_addr.s_addr = INADDR_ANY;

    //connect to address of socket
    connect_status = connect(client_socket, (struct sockaddr *) &server_addr, sizeof(server_addr));
    if(connect_status == -1)
    {
        printf("Error connecting to server\n");
        exit(1);
    }
    printf("Connected to Server\n");




    FILE* fp = fopen( "newfile.txt", "w");
    char data[512];
    int b;

    while((b=recv(client_socket, data, 512,0))>0){
        fwrite(data, 1, b, fp);
    }
    fclose(fp);


    //recv(client_socket, data, 512,0);
    //printf("client buffer: %s\n", data);

    close(client_socket);
    return 0;

}

Сервер:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>

int main(int argc, char *argv[]){
    int port = 8001;
    int server_socket;
    struct sockaddr_in server_addr;
    int client_socket, client_addr;
    int bind_status, listen_status;


    server_socket = socket(AF_INET, SOCK_STREAM, 0);    //SOCK_STREAM for TCP
    if (server_socket < 0)
    {
        printf("Error creating socket\n");
        exit(1);
    }
    printf("Socket created\n");

    //address for socket to connect to
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    //bind socket to the IP and port
    bind_status = bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr));
    if (bind_status < 0)
    {
        printf("Error binding to socket\n");
        exit(1);
    }
    printf("Socket binded\n");

    //listen for client connections
    listen_status = listen(server_socket, 10);
    if (listen_status == -1)
    {
        printf("Error listening to socket\n");
        exit(1);
    }
    printf("Listening\n");

    //accept client connection
    socklen_t addr_len;
    addr_len = sizeof(client_socket);
    client_socket = accept(server_socket, (struct sockaddr *) &client_addr, &addr_len);




    FILE *fp = fopen("test.txt", "r");
    char data[512];
    int b;

    while((b = fread(data, 1, 512, fp))>0){
        send(client_socket, data, b, 0);
    }
    fclose(fp);


   // strcpy(data,"test message");
    //printf("server buffer: %s\n", data);
    //send(client_socket, data, 512, 0);

    close(server_socket);
    return 0;
}
1
MoeMan 22 Фев 2021 в 01:21

1 ответ

Лучший ответ

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

Хотя абстракция сокета, кажется, показывает вам, что вызовы recv и send каким-то образом синхронизированы, это означает, что данные, которые вы отправляете от клиента за один вызов для отправки, принимаются на сервере ровно с одним recv, что неверно, в основном из-за того, как реализованы сокеты (клиентский tcp может решить разделить вашу передачу на несколько пакетов, и автоматическое обслуживание сервера может заставить все эти пакеты буферизоваться в приемнике до того, как сервер получит часть из них за один вызов для получения и другие в текстовом звонке.

Единственное, в чем можно быть уверенным, это то, что байт, который был отправлен ранее, получен раньше, и между ними нет повторяющихся или пропущенных байтов. Но количество байтов, полученных при каком-либо вызове recve, определяется только количеством буферизованных данных, имеющихся на одной стороне соединения.

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

Здесь есть несколько подходов ... вы можете отправить внутриполосную последовательность (некоторую управляющую последовательность), которая, прочитанная на другой стороне, будет распознана как конец блока данных и начало следующего. Некоторые системы используют кодирование пакетов, которое просто добавляет к каждому блоку количество последующих байтов, и пустой пакет (например, одно число 0, означающее 0 последующих байтов) может представлять эту последовательность. Другой способ - использовать определенную последовательность, которая, как вы знаете, маловероятна для появления в данных (например, \n.\m, два символа новой строки с перемежением одной точки - это много раз использовалось в редакторе ed unix, например, или протокол почтового отделения использует его.) и если такая последовательность есть в файле, просто удвойте точку, чтобы указать, что это не последовательность разделителей. И оба конца должны согласиться с этим (так называемым) протоколом .

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

Я не включил код, потому что в литературе есть множество примеров. Попробуйте получить доступ к книге Ричарда В. Стивенса "Сетевое программирование Unix". Это очень хороший справочник для ознакомления с протоколами сокетов и с тем, как справляться со всем этим.

1
Luis Colorado 22 Фев 2021 в 17:35