问题描述
我正在做的是首先发送文件名和文件大小(一个编码的(utf-8)json字符串),因此服务器可以提前知道文件的大小,以便他知道何时所有数据到达。
当只有一个客户端发送文件时,它工作得很好,但是当两个或多个客户端同时发送文件时,服务器经常崩溃,而“ utf-8”编解码器无法解码 (有时它可以正常工作,这使它更加令人困惑) 位置36的字节0xff: run()
函数的第一行中的无效起始字节 。
我不知道为什么会这样,因为每个客户都有自己的流程,因此彼此之间不应有任何冲突。
客户:
import socket, json
f = 'img.jpg'
f_bin = open(f, 'rb').read()
info = {'name': f, 'size': len(f_bin)}
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(('89.1.59.435', 9005))
s.send(json.dumps(info).encode())
total_sent = 0
while total_sent < info['size']:
try:
sent = s.send(f_bin[total_sent:])
except Exception as err:
break
total_sent += sent
服务器:
import socket, threading, json
def get_file(conn, info):
remaining = info['size'] # file size, our trigger to know that all packages arrived
file_bin = b''
progress = None
while remaining > 0:
try:
package = conn.recv(1024)
except Exception as err:
return None
file_bin += package
remaining -= len(package)
return file_bin
def run(conn):
info = json.loads(conn.recv(1024).decode()) # 'utf-8' codec can't decode byte 0xff in position 36: invalid start byte
file_bin = get_file(conn, info)
if file_bin is not None:
dest = 'files/{}'.format(info['name'])
with open(dest, 'wb') as f:
f.write(file_bin)
print('success on receiving and saving {} for {}'.format(info['name'], conn.getpeername()))
conn.close()
host, port = ('', 9005)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((host, port))
sock.listen(5)
while True:
conn, addr = sock.accept()
print('conn', addr)
threading.Thread(target=run, args=(conn,)).start()
我删除了印刷品只是为了张贴相关部分并说明问题
1楼
TCP是一种流协议,因此不能保证recv
准确接收到JSON标头。
它可能只包含JSON的一部分,或者可能包含一些更高版本的二进制数据。
在您的情况下,在激活多个连接的情况下,recv可能会延迟足够长的时间来获取额外的数据。
您需要某种方式来了解标题的大小。 一种常见的方法是选择一个标记结束的字符。 JSON字符串不包含NUL('\\ x00`),因此效果很好。 在标头后立即写一个NUL,服务器可以扫描该null以再次将其拆分。 我稍微修改了代码,使其可以在我的机器上运行,并在此示例中添加了一些基本的错误处理。
client.py
import socket, json, sys
f = 'img.jpg'
f_bin = open(f, 'rb').read()
info = {'name': f, 'size': len(f_bin)}
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(('localhost', 9005))
s.send(json.dumps(info).encode())
s.send(b'\x00')
total_sent = 0
while total_sent < info['size']:
try:
sent = s.send(f_bin[total_sent:])
except Exception as err:
break
total_sent += sent
server.py
import socket, threading, json, io, struct
def reset_conn(conn):
"""Reset tcp connection"""
# linger zero
conn.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
struct.pack('ii', 1, 0)) # on/off, linger-timeout
conn.shutdown(socket.SHUT_RDWR)
conn.close()
def get_file(conn, info):
remaining = info['size'] # file size, our trigger to know that all packages arrived
file_bin = b''
progress = None
while remaining > 0:
try:
package = conn.recv(1024)
if not package:
print("Unexpected end of file")
reset_conn(conn)
return None
except Exception as err:
return None
file_bin += package
remaining -= len(package)
return file_bin
def run(conn):
# get header
buf = io.BytesIO()
while True:
c = conn.recv(1)
if not len(c):
print("Error, no header received")
reset_conn(conn)
return
if c == b'\x00':
break
buf.write(c)
info = json.loads(buf.getvalue().decode())
del buf
file_bin = get_file(conn, info)
if file_bin is not None:
dest = 'files/{}'.format(info['name'])
with open(dest, 'wb') as f:
f.write(file_bin)
print('success on receiving and saving {} for {}'.format(info['name'], conn.getpeername()))
conn.close()
host, port = ('localhost', 9005)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((host, port))
sock.listen(5)
while True:
conn, addr = sock.accept()
print('conn', addr)
threading.Thread(target=run, args=(conn,)).start()