1

I am trying to send files over TCP client/server in C++ app. The scenario is pretty simple; Client side send files and server side receive files. I can send text based(such as .cpp or .txt) files successfully, but when I try to send files such as .mp4 or .zip, the files received on the server are corrupted.

client.cpp

FILE *fptr = fopen(m_fileName.c_str(), "rb");

off_t offset = 0;
int bytes = 1;
if(fptr){
    while (bytes > 0){
        bytes = sendfile(m_sock, fptr->_fileno, &offset, BUFFER_SIZE);
        std::cout<< "sended bytes : "<< offset << '\n';
    }
}

fclose(fptr);
std::cout<< "File transfer completed!\n";

Server.cpp

//some stuff...
for (;;) {
    if ((m_result = recv(epes[i].data.fd, buf, BUFFER_SIZE, 0)) == -1) {
        if (errno == EAGAIN)
            break;
        exit_sys("recv");
    }
    
    if (m_result > 0) {
        buf[m_result] = '\0';
        
        std::ofstream outfile;
        if(!outfile.is_open())
            outfile.open(m_filename.c_str(), std::ios_base::app | std::ios_base::binary);
        
        outfile << std::unitbuf << buf;
        m_filesize -= m_result;

        std::cout << "count       : " << m_result << '\n';
        std::cout << "remain data : " << m_filesize << '\n';

        if(!m_filesize){
            outfile.close();
            m_fileTransferReady = 0;
            std::cout<<"File transfer stop\n";
            if (send(epes[i].data.fd, "transferok", 10, 0) == -1)
                exit_sys("send");
        }
    }
    //some stuff for else
}

I used binary mode when sending and receiving files, but I guess that's not enough. Is there a special sending method for formatted files?

Purgoufr
  • 761
  • 14
  • 22
  • 2
    You're server code is treating each received data chunk as a null-terminated char array. That's not valid with binary data which can legitimately contain nulls. Try using [unformatted output](https://en.cppreference.com/w/cpp/io/basic_ostream/write) instead. – G.M. Nov 21 '21 at 10:07

1 Answers1

0

I found the solution by going the way G.M. mentioned in the comment. Adding a null character to the end of a file opened in binary mode causes problems for non-text-based files. Another issue is with the use of the << operator. Instead ofstream's write member function needs to be used. As a result;

server.cpp

//...
//...
    
    if (m_result > 0) {
        //buf[m_result] = '\0';       //Deleted!
        std::ofstream outfile;
        if(!outfile.is_open())
            outfile.open(m_filename.c_str(), std::ios_base::app | std::ios_base::binary);
        
        outfile << std::unitbuf;      //Modified
        outfile.write(buf, m_result); //Added
        m_filesize -= m_result;
        
        //...
        //...
Purgoufr
  • 761
  • 14
  • 22