2

I need to write a simple file transfer protocol in C++ to upload or download a file from a client to a server. Right now both my client and server applications are do-it-all scripts. I want to modularize everything in classes/methods so I can more easily modify things later.

To upload a file, I need the name of user sending file (no authentication though), file name, file size, and file contents. This is what I have on client side:

int sendFile(string fileName, SOCKET s) {
    ifstream file;
    int ibytesent;
    int size;
    char fileSize[packetSize];

    // Open file, set get pointer at end of file
    file.open (fileName.c_str(), ios::binary | ios::ate);

    // Get file size and pass it to buffer
    size = (int) file.tellg();

    //itoa(size, fileSize, packetSize);
    sprintf(fileSize, "%d", size);

    // Set pointer to beginning
    file.seekg(0, ios::beg);

    // Send file size
    if (send(s, fileSize, packetSize, 0) == SOCKET_ERROR) {
        throw "Send failed\n";
    }

    cout << "Size sent: " << size;

    // Buffer the file Name
    char fileNameBuffer[packetSize];

    sprintf(fileNameBuffer, fileName.c_str());

    // Send the file name
    if (send(s, fileNameBuffer, packetSize, 0) == SOCKET_ERROR) {
        throw "Send failed\n";
    }

    cout << "File name sent: " << fileName << endl;

    char buf[packetSize];
    ibytesent = 0;
    // Loop until the whole file is sent
    while(file.good()) {
        // Read next packetSize bytes into buf
        file.read(buf, packetSize);

        // Send the read data
        ibytesent += send(s, buf, packetSize, 0);

        if (ibytessent == SOCKET_ERROR) {
            throw "Send failed\n";
        }
    }

    cout << "File sent.\n";
    //wait for reception of server response.
    ibytesrecv=0;
    /*
    if((ibytesrecv = recv(s,szbuffer,128,0)) == SOCKET_ERROR)
        throw "Receive failed\n";
    else
        cout << "File received.";
     */
    file.close();

    return 1;
}

And then there is a main() method that opens up a socket and connects to the server. My question really is about how to design/implement a proper file transfer protocol. What is a good structure for it? I would also like to know your opinions about how to split (and send) a file into a packet structure rather than the way I do it now. I don't necessarily need code, but separation of concerns into this and that class, etc.

Please don't recommend an already existing protocol since that isn't the point of this exercise.

Thanks

EDIT I kind of found something similar to what I'm looking for here: http://www.eventhelix.com/realtimemantra/PatternCatalog/protocol_layer.htm

Sotirios Delimanolis
  • 263,859
  • 56
  • 671
  • 702
  • Doing a billion `send` per second is not the correct way to proceed. You could easily overwhelm the socket. I think you should have a look at `select()` and non-blocking sockets. – Pietro Lorefice Jan 24 '12 at 20:11

2 Answers2

3

(I think this what you were asking for)

A simple protocol could be to precede the file content with a list of name=value pairs that define attributes of the file being sent, with an empty name=value pair terminating the list. A name=value pair is terminated by a newline character.

For example:

sending-user-name=userA\n
file-name=filea.txt\n
is-binary=false\n
byte-count=442\n\n
<file-content-here>

sending-user-name=userZ\n
file-name=filez.zip\n
is-binary=true\n
byte-count=1087\n\n
<file-content-here>

The server code would process the attributes and would be able to open the file in correct mode (binary or not) and would know exactly how many bytes it has to read.

As commented by Narrakan look into non-blocking sockets and select(), which you can use to determine when a socket is readeable or writeable.

Community
  • 1
  • 1
hmjd
  • 117,013
  • 19
  • 199
  • 247
1

Here's a take on your possible classes:

  • ISender and IReceiver interfaces for senders and receivers

  • StreamSender and StreamReceiver concrete implementations of ISender and IReceiver respectively (using sockets I guess)

  • IDataSource interface to get data from source

  • FileReader a concrete implementation of IDataSource to read file off disk to be fed into StreamSender

Example:

ISender sender=new StreamSender(new FileReader("filename.txt"));
sender.setDest(new StreamReceiver(host));
sender.setName(senderName);
sender.send();

Obviously this is just an example off the top of my head. There will be missing pieces.

Sid
  • 7,315
  • 2
  • 26
  • 41