There are two much simpler ways I can think of in which you can solve this. Both involve some changes in the behaviors of both the client and the server.
The first is to use padding. Let's say you're sending a file. What you would do is read the file, encode this into a simpler format like Base64, then send enough space characters to fill up the rest of the 4096-byte 'chunk'. What you would do is something like this:
from cStringIO import StringIO
import base64
import socket
import sys
CHUNK_SIZE = 4096 # bytes
# Extract the socket data from the file arguments
filename = sys.argv[1]
host = sys.argv[2]
port = int(sys.argv[3])
# Make the socket
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,port))
# Prepare the message to send
send_str = "send %s" % (filename,)
end_str = "end %s" % (filename,)
data = open(filename).read()
encoded_data = base64.b64encode(data)
encoded_fp = StringIO(encoded_data)
sock.send(send_str + '\n')
chunk = encoded_fp.read(CHUNK_SIZE)
while chunk:
sock.send(chunk)
if len(chunk) < CHUNK_SIZE:
sock.send(' ' * (CHUNK_SIZE - len(chunk)))
chunk = encoded_fp.read(CHUNK_SIZE)
sock.send('\n' + end_str + '\n')
This example seems a little more involved, but it will ensure that the server can keep reading data in 4096-byte chunks, and all it has to do is Base64-decode the data on the other end (a C library for which is available here. The Base64 decoder ignores the extra spaces, and the format can handle both binary and text files (what would happen, for example, if a file contained the "end filename" line? It would confuse the server).
The other approach is to prefix the sending of the file with the file's length. So for example, instead of sending send filename
you might say send 4192 filename
to specify that the length of the file is 4192 bytes. The client would have to build the send_str
based on the length of the file (as read into the data
variable in the code above), and would not need to use Base64 encoding as the server would not try to interpret any end filename
syntax appearing in the body of the sent file. This is what happens in HTTP; the Content-length
HTTP header is used to specify how long the sent data is. An example client might look like this:
import socket
import sys
# Extract the socket data from the file arguments
filename = sys.argv[1]
host = sys.argv[2]
port = int(sys.argv[3])
# Make the socket
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,port))
# Prepare the message to send
data = open(filename).read()
send_str = "send %d %s" % (len(data), filename)
end_str = "end %s" % (filename,)
sock.send(send_str + '\n')
sock.send(data)
sock.send('\n' + end_str + '\n')
Either way, you're going to have to make changes to both the server and the client. In the end it would probably be easier to implement a rudimentary HTTP server (or to get one which has already been implemented) in C, as it seems that's what you're doing here. The encoding/padding solution is quick but creates a lot of redundantly-sent data (as Base64 typically causes a 33% increase in the quantity of data sent), the length prefix solution is also easy from the client side but may be more difficult on the server.