How to send and receive a file via Socket?

6

I need to send files via Socket in Linux C ++, as the file may have extensive content I will need to send it to pieces. In this case, I need to create a kind of protocol to send a file (in pieces) through Sockets and be able to join again on the server side and / or the client side. Can anyone help with how to perform this task, below are the Server and Client methods, respectively, where I can send and receive text messages between the client and the server and vice versa.

// Método no Servidor, recebe e envia mensagem ao cliente.
void SocketServer::receiver()
{
   int read_size = -1;
   char msg_buf_recv[MAX_MSG];
   char msg_buf_send[MAX_MSG];
   std::string client_message;

   while( (read_size = ::recv(sockClient, msg_buf_recv, sizeof(msg_buf_recv), 0)) > 0)
   {
       std::cout << msg_buf_recv << std::endl;
       std::cout << "Servidor: ";
       std::cin.getline(msg_buf_send, sizeof(msg_buf_send));

       write(sockClient, msg_buf_send, sizeof(msg_buf_send));
   }

   if(read_size == 0)
   {
       std::cout << "\nClient disconnected" << std::endl;
   }
   else if(read_size == -1)
   {
       std::cerr << "Recv failed" << std::endl;
   }
}


// Método cliente enviar e recebe mensagens ao servidor.
bool SocketClient::conectar() 
{
    char server_message[MAX_MSG];
    char client_message[MAX_MSG];

    if ( connect(sockClient, (struct sockaddr *)&client , sizeof(client)) < 0)
    {
        std::cerr << "Connect failed. Error" << std::endl;
        return false;
    }

    std::cout << "Connectando..." << std::endl;
    sleep( 1 );
    system("clear");
    std::cout << "Conectado ao Servidor IP: " << ipClient << std::endl;

    while(1)
    {
        std::cout << "Marcos: ";
        std::cin.getline (client_message, sizeof(client_message));

        //Send some data
        if( send(sockClient, client_message, sizeof(client_message), 0) < 0)
        {
            std::cerr << "Send failed" << std::endl;
            return false;
        }
        std::cout << "Client message: " << client_message << std::endl;

        //Receive a reply from the server
        if( recv(sockClient, server_message, sizeof(server_message), 0) < 0)
        {
           std::cerr << "recv failed" << std::endl;
           return false;
        }
        std::cout << "Server message: " << server_message << std::endl;
    }
    return true;
}
    
asked by anonymous 05.01.2015 / 13:45

2 answers

5

In this explanation I will not delve into examples of "manual mapping" or how to handle different files, as there is no need since we are only transferring hexadecimal data.

Header formatting

Take the information you need: size, name, format (it may already be included in the name) ... In this case I will show you how to get the file size.

ifstream file( "example.txt", ios::binary | ios::ate);
return file.tellg();

Remembering that in the above operation the file has not yet been allocated in your program, we are simply reading the metadata of it, you can get information from the file directly through GetFileAttributes or GetFileAttributesEx.

You can send a header in the format file name + file size + md5 hash that is enough to ensure that the document is submitted and validated.

Sending the file

I'll give you an example of sending a small document, which does not have to manipulate the memory of your program intelligently to avoid excessive RAM consumption.

Allocating the file in your program

First you should open it and turn it into a byte array

static std::vector<char> ReadAllBytes(char const* filename)
{
    ifstream ifs(filename, ios::binary|ios::ate);
    ifstream::pos_type pos = ifs.tellg();

    std::vector<char>  result(pos);

    ifs.seekg(0, ios::beg);
    ifs.read(&result[0], pos);

    return result;
}

If you are concerned about the efficiency of the code pass the result as a parameter:

static void ReadAllBytes(char const* filename, std::vector<char>& result)

And if the document is too large (larger than 1mb) use malloc () before you start allocating the bytes (this is for receiving too)

Then break it into "chunks" (or packets) of 32 bytes each. But this we will do in the next step.

Shipping Loop

Taking advantage of your code ...

std::vector<char> rts;
int pos = 0;
ReadAllBytes("arquivo.txt", rts);

while(filesize > pos*32)
{
    if( send(sockClient, rts[pos * 32], 32, 0) < 0)
    {
        std::cerr << "Send failed" << std::endl;
        return false;
    }
}

Receive Loop

char (*rec);
rec = malloc(filesize);
int bytesRecv = 0;

while(bytesRecv*32 < filesize)
{
    if(recv(sockClient,rec[bytesRecv*32],32,0))
    {
        bytesRecv++;
    }
}

Then save to a file

FILE* file = fopen( filename, "wb" );
fwrite( rec , 1, filesize, file );

Source

    
05.01.2015 / 20:33
2

Some methods I use for file upload using fstream and FILE. I commented a few things, I hope it helps.

Reading files, and sending them to the client.

void Socket_Manip::FILE_SEND(char directory[]) {

    std::ifstream::pos_type size;
    char* memblock;

    std::ifstream file(directory, std::ios::in|std::ios::binary|std::ios::ate);

    if (file.is_open()) {
    size = file.tellg(); // recebendo o tamanho do arquivo atraves da função tellg

        //envio do tamanho do arquivo para o endereço remoto.
        char GotFileSize[1024];
        snprintf(GotFileSize, 1024, "%d", size); //concatenando em GotFileSIze o inteiro de Size
        send(new_sockfd, GotFileSize, 1024, 0); // enviando para o socket o tamanho do arquivo
        if(size > 0) {
            memblock = new char[size]; // alocação com o tamanho do arquivo.
            file.seekg(0, std::ios::beg); // posicionando o ponteiro no inicio do arquivo.
            file.read(memblock, size); // leitura do arquivo
            file.close(); // fechando o arquivo.
            send(new_sockfd, memblock, size, 0); // enviando para o socket o conteudo.

            delete[] memblock;
            }
    }
    Close_Socket(); // função para encerrar a conexão
}

Receiving Files from the Server:

void Socket_Manip::FILE_READ(char directory[]) {
    //File Size
    recv(sockfd, GotFileSize, MY_BUFFER_SIZE, 0); // recebe o tamanho do arquivo

    long FileSize = atoi(GotFileSize); // converte o tamanho do arquivo para inteiro
    long SizeCheck = 0; //variavel auxiliar para o loop
    FILE *fp = fopen("/home/rh4yd3n/Downloads/file.zip", "w"); //abrindo o arquivo para escrita
    char* mfcc;  // variavel onde você irá alocar o tamanho dinamicamente.
    if(FileSize > 1499) {
        mfcc = (char*)malloc(1500); // alocação na variavel 
        while(SizeCheck < FileSize){
            int Received = recv(sockfd, mfcc, 1500, 0);
            SizeCheck += Received;
            fwrite(mfcc, 1, Received, fp);
            fflush(fp);
            printf("Filesize: %d\nSizecheck: %d\nReceived: %d\n\n", FileSize, SizeCheck, Received);
        }
    } else { 
        mfcc = (char*)malloc(FileSize + 1);
        int Received = recv(sockfd, mfcc, FileSize, 0);
        fwrite(mfcc, 1, Received, fp);
        fflush(fp);
    }

    fclose(fp);
    close(sockfd);
    free(mfcc);
    return 0;
}
    
05.01.2015 / 23:58