How to send and receive a file via Socket?

Asked

Viewed 2,775 times

3

I am needing to send files via Socket on C++ Linux, as the file may have an extensive content it will need to be sent in 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 and/or client side. Does anyone can help in how to accomplish this task, follows below the Server and Client methods, respectively, where I can send and receive text messages between client and 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;
}

2 answers

4

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.

Formatting of the header

Get the information you think you need: size, name, format (it may already be included in the name)... In this case I’ll 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 media from it, you can get information from the file directly by Getfileattributes or Getfileattributesex.

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

Sending the file

I’ll give you an example showing you sending a small document that doesn’t need to intelligently manipulate your program’s memory 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 worried about code efficiency pass the result as 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 "pieces" (or packets) of 32 bytes each. But this we will do in the next step.

Sending 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;
    }
}

Receiving 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

  • Very good tip and used in my application, however, I’m still doubtful to receive the file on the Server/ Client side, IE recv, how do I join the parts?

  • @Mark, I’m sorry for the delay, I was away from the computer, I made up my mind because I have no compiler in sight. I will try to update again tomorrow with a full source.

  • Thanks, I’m waiting for the complete source, as I couldn’t understand it properly, I ended up trying to redo everything, but it’s not working. If you have everything complete the understanding will be much better and will be of great help.

  • @Marcos https://www.dropbox.com/s/k08u4ya6k0babj2/Server.rar?dl=0

  • Thanks for the strength Vitor! I will analyze to better understand this matter.

2

Some methods I use for file sending using fstream and FILE. I mentioned some things, I hope it helps.

Read files, and send to customer.

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;
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.