问题
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
回答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.
回答2:
(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.
来源:https://stackoverflow.com/questions/8993306/designing-a-file-transfer-protocol-in-c