Есть два гораздо более простых способа, которыми я могу придумать, в которых вы можете решить эту проблему. Оба включают некоторые изменения в поведении как клиента, так и сервера.
Первый - использовать отступы. Допустим, вы отправляете файл. Что вам нужно сделать, это прочитать файл, закодировать его в более простой формат, такой как Base64, а затем отправить достаточно пробелов, чтобы заполнить оставшуюся часть 4096-байтового «чанка». Что бы вы сделали, это что-то вроде этого:
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')
Этот пример кажется немного более сложным, но он гарантирует, что сервер может продолжать чтение данных в 4096-байтовых блоках, и все, что ему нужно, это Base64-декодировать данные на другом конце (библиотека C, для которой доступно здесь . Декодер Base64 игнорирует лишние пробелы, и формат может обрабатывать как двоичные, так и текстовые файлы (что произойдет, например, если файл содержит строку «end filename»? сервер).
Другой подход заключается в добавлении префикса отправки файла к его длине. Так, например, вместо отправки send filename
вы можете сказать send 4192 filename
, чтобы указать, что длина файла составляет 4192 байта. Клиент должен был бы построить send_str
на основе длины файла (как считывается в переменной data
в приведенном выше коде), и ему не нужно было бы использовать кодировку Base64, поскольку сервер не будет пытаться интерпретировать какие-либо end filename
Синтаксис появляется в теле отправленного файла. Это то, что происходит в HTTP; HTTP-заголовок Content-length
используется для указания длины отправляемых данных. Пример клиента может выглядеть так:
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')
В любом случае вам придется вносить изменения как на сервере, так и на клиенте. В конце концов, возможно, будет проще реализовать элементарный HTTP-сервер (или получить уже внедренный) в C, поскольку, похоже, именно это вы здесь и делаете. Решение для кодирования / заполнения является быстрым, но создает много избыточно отправленных данных (поскольку Base64 обычно вызывает увеличение количества отправляемых данных на 33%), решение с префиксом длины также легко со стороны клиента, но может быть более сложным сервер.