Сокет Python получает большое количество данных
Когда я пытаюсь получить большее количество данных, он отключается, и я должен нажать enter, чтобы получить остальную информацию. Сначала мне удалось немного увеличить его, но он все равно не получит его. Как вы видите, я увеличил буфер на conn.recv(), но он все равно не получает все данные. В какой-то момент это сокращает его. Я должен нажать enter на моем raw_input, чтобы получить остальную часть данных. В любом случае, я могу получить все данные сразу? Вот код.
port = 7777
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('0.0.0.0', port))
sock.listen(1)
print ("Listening on port: "+str(port))
while 1:
conn, sock_addr = sock.accept()
print "accepted connection from", sock_addr
while 1:
command = raw_input('shell> ')
conn.send(command)
data = conn.recv(8000)
if not data: break
print data,
conn.close()
Ответы
Ответ 1
TCP/IP - это потоковый протокол, а не протокол на основе сообщений. Нет никакой гарантии, что каждый вызов send()
одним узлом приведет к одному вызову recv()
другим узлом, получающим точные отправленные данные - он может получить пакет данных, разделенный на несколько вызовов recv()
из-за пакета фрагментация.
Вам нужно определить свой собственный протокол на основе сообщений поверх TCP, чтобы разграничить границы сообщений. Затем, чтобы прочитать сообщение, вы продолжаете вызывать recv()
, пока не прочитаете сообщение целиком или не произойдет ошибка.
Одним из простых способов отправки сообщения является добавление префикса к каждому сообщению по его длине. Затем, чтобы прочитать сообщение, вы сначала читаете длину, а затем читаете столько байтов. Вот как вы можете это сделать:
def send_msg(sock, msg):
# Prefix each message with a 4-byte length (network byte order)
msg = struct.pack('>I', len(msg)) + msg
sock.sendall(msg)
def recv_msg(sock):
# Read message length and unpack it into an integer
raw_msglen = recvall(sock, 4)
if not raw_msglen:
return None
msglen = struct.unpack('>I', raw_msglen)[0]
# Read the message data
return recvall(sock, msglen)
def recvall(sock, n):
# Helper function to recv n bytes or return None if EOF is hit
data = bytearray()
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data.extend(packet)
return data
Затем вы можете использовать функции send_msg
и recv_msg
для отправки и получения целых сообщений, и у них не возникнет проблем с разделением или объединением пакетов на уровне сети.
Ответ 2
Вы можете использовать его как: data = recvall(sock)
def recvall(sock):
BUFF_SIZE = 4096 # 4 KiB
data = b''
while True:
part = sock.recv(BUFF_SIZE)
data += part
if len(part) < BUFF_SIZE:
# either 0 or end of data
break
return data
Ответ 3
Принятый ответ хорош, но он будет очень медленным с большими файлами -string - неизменный класс, это означает, что каждый раз, когда вы используете знак +
, создается больше объектов, использование list
в качестве структуры стека будет более эффективным.
Это должно работать лучше
while True:
chunk = s.recv(10000)
if not chunk:
break
fragments.append(chunk)
print "".join(fragments)
Ответ 4
Вам может потребоваться несколько раз вызвать conn.recv() для получения всех данных. При вызове его один раз не гарантируется получение всех отправленных данных из-за того, что потоки TCP не поддерживают границы кадров (т.е. Они работают только как поток необработанных байтов, а не структурированный поток сообщений).
См. этот ответ для другого описания проблемы.
Обратите внимание, что это означает, что вам нужен какой-то способ узнать, когда вы получили все данные. Если отправитель всегда будет отправлять ровно 8000 байт, вы можете подсчитать количество полученных байтов до сих пор и вычесть из 8000, чтобы узнать, сколько осталось получить; если данные имеют переменный размер, существуют различные другие методы, которые могут использоваться, например, когда отправитель отправляет заголовок с номерами байтов перед отправкой сообщения или если он отправляет текст ASCII, вы можете искать символ новой строки или NUL.
Ответ 5
Вариант с использованием функции-генератора (который я считаю более питоническим):
def recvall(sock, buffer_size=4096):
buf = sock.recv(buffer_size)
while buf:
yield buf
if len(buf) < buffer_size: break
buf = sock.recv(buffer_size)
# ...
with socket.create_connection((host, port)) as sock:
sock.sendall(command)
response = b''.join(recvall(sock))
Ответ 6
Большинство ответов описывают какой-то метод recvall()
. Если узким местом при получении данных является создание байтового массива в цикле for
, я протестировал три подхода к распределению полученных данных в recvall()
:
Метод байтовой строки:
arr = b''
while len(arr) < msg_len:
arr += sock.recv(max_msg_size)
Метод списка:
fragments = []
while True:
chunk = sock.recv(max_msg_size)
if not chunk:
break
fragments.append(chunk)
arr = b''.join(fragments)
Предварительно выделенный метод bytearray
:
arr = bytearray(msg_len)
pos = 0
while pos < msg_len:
arr[pos:pos+max_msg_size] = sock.recv(max_msg_size)
pos += max_msg_size
Результаты:
![enter image description here]()
Ответ 7
Изменение кода Адама Розенфилда:
import sys
def send_msg(sock, msg):
size_of_package = sys.getsizeof(msg)
package = str(size_of_package)+":"+ msg #Create our package size,":",message
sock.sendall(package)
def recv_msg(sock):
try:
header = sock.recv(2)#Magic, small number to begin with.
while ":" not in header:
header += sock.recv(2) #Keep looping, picking up two bytes each time
size_of_package, separator, message_fragment = header.partition(":")
message = sock.recv(int(size_of_package))
full_message = message_fragment + message
return full_message
except OverflowError:
return "OverflowError."
except:
print "Unexpected error:", sys.exc_info()[0]
raise
Однако я бы очень хотел использовать оригинальный подход.
Ответ 8
Вы можете сделать это с помощью сериализации
from socket import *
from json import dumps, loads
def recvall(conn):
data = ""
while True:
try:
data = conn.recv(1024)
return json.loads(data)
except ValueError:
continue
def sendall(conn):
conn.sendall(json.dumps(data))
ПРИМЕЧАНИЕ. Если вы хотите создать файл, используя приведенный выше код, вам необходимо кодировать/декодировать его в base64.
Ответ 9
Для тех, кто ищет ответ в тех случаях, когда вы не знаете длину пакета ранее. Здесь простое решение, которое считывает 4096 байт за раз и останавливается, когда получено менее 4096 байт. Однако он не будет работать в тех случаях, когда общая длина полученного пакета составляет ровно 4096 байт - тогда он снова вызовет recv()
и повиснет.
def recvall(sock):
data = b''
bufsize = 4096
while True:
packet = sock.recv(bufsize)
data += packet
if len(packet) < bufsize:
break
return data