Неправильное декодирование информации в python socket
files
server.py:
import socket
ip_port = ('127.0.0.1', 25565)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(ip_port)
sock.listen(100)
clients = {}
def sendm(message: str, sock=sock):
len_message = len(message)
sock.send(len_message)
sock.send(message)
if sock.recv(1).decode('utf-8') == '1':
return True
else:
return False
def getm(sock=sock):
try:
len_message = int(sock.recv(3).decode('utf-8'))
message = sock.recv(len_message).decode('utf-8')
sock.send('1'.encode('utf-8'))
return message
except:
sock.send('0'.encode('utf-8'))
return None
def auth(sock=sock, clients=clients):
len_password = int(sock.recv(2).decode('utf-8'))
password = sock.recv(len_password).decode('utf-8')
len_name = int(sock.recv(2).decode('utf-8'))
name = sock.recv(len_name).decode('utf-8')
if name not in clients:
clients[name] = {}
clients[name]["password"] = password
return True
else:
for client in clients:
if clients[client] == password:
return True
return False
while True:
try:
client, addres = sock.accept()
except socket.error:
pass
else:
print(clients)
if auth(client):
print(f'Connect with {client}. Addres is {addres}...')
client.send('1'.encode('utf-8'))
msg = client.recv(1024)
client.py:
import socket
ip_port = ('127.0.0.1', 25565)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(ip_port)
alias = 'Sock1'
def auth(password, name=alias, sock=sock):
len_password = str(len(password))
print(len_password.encode('utf-8').decode('utf-8'))
sock.send(len_password.encode('utf-8'))
sock.send(password.encode('utf-8'))
len_name = str(len(alias))
sock.send(len_name.encode('utf-8'))
sock.send(name.encode('utf-8'))
if sock.recv(1).decode('utf-8') == '1':
return True
else:
return False
print(8)
def sendm(message: str, sock=sock):
len_message = str(len(message))
sock.send(len_message.encode('utf-8'))
sock.send(message.encode('utf-8'))
if sock.recv(1).decode('utf-8') == '1':
return True
else:
return False
def getm(sock=sock):
try:
len_message = int(sock.recv(3).decode('utf-8'))
message = sock.recv(len_message).decode('utf-8')
sock.send('1'.encode('utf-8'))
return message
except:
sock.send('0'.encode('utf-8'))
return None
cycle = True
while cycle:
print('Auth')
if auth(password='qwerty123'):
print('Ok')
sendm('Hello, world!')
msg = getm()
print(msg)
else:
print('Fail on autho')
sock.close()
При приёме информации (len_password) от куда-то берётся 9q. Хотя я выводил информацию и не как не могло это получиться.
Вот трэйс бэк:
Traceback (most recent call last):
File "C:\Users\Admin\Desktop\k\server.py", line 59, in <module>
if auth(client):
File "C:\Users\Admin\Desktop\k\server.py", line 33, in auth
len_password = int(sock.recv(2).decode('utf-8'))
ValueError: invalid literal for int() with base 10: '9q'
Ответы (1 шт):
Проблема в протоколе, а именно в том, как вы передаете длину:
len_message = str(len(message))
sock.send(len_message.encode('utf-8'))
Тут количество байт для кодирования длины зависит от длины сообщения. И не ясно как отделить байты длины от самого сообщения. Кодировка получается неоднозначная, т.е. разные сообщения могут приводить к одинаковой последовательности байт пересылаемой по сети.
Лучше зафиксировать количество байт используемых для передачи длины. Например, если ограничить длину строки 2**32 т.е. ~4 гигабайта, то достаточно 4 байта для длины.
Нужно превратить int в массив байт. Это можно сделать используя модуль struct:
import struct
len_as_bytes = struct.pack('I>', len(msg))
sock.send(len_as_bytes)
На приемной стороне, нужно взять первых четыре байта принятого сообщения и превратить в int:
received = ...
(message_len,) = struct.unpack('>I', received[:4])
Далее обратите внимание, что нет гарантии, что вы получите данные такими же порциями, как и посылали. socket.recv может вернуть меньше данных, чем вы попросили. Например, вы посылаете длину 4 байта, а потом сообщение, например 10 байт используя два вызова к send. На принимающей стороне, может так случится, что вызов recv(4) вернет сначала 2 байта, и нужно будет дочитывать недостающие байты пока не накопится все, что было послано.
Так что нужно recv вызывать в цикле, накапливая пришедшие данные, кодом типа:
def myrecv(sock, to_receive):
received = []
while len(received) < to_receive:
chunk = sock.recv(min(to_receive - len(received), 2048))
if chunk == b'':
raise RuntimeError("socket connection broken")
received += chunk
return received