我在这段代码中看到两个问题
第一的:
你有错误的缩进,有些线在外面while True,但它们应该在里面。
客户端.py
with open(filename, "wb") as f:
while True:
bytes_read = s.recv(BUFFER_SIZE)
# the lines below should to be inside `while True`
if not bytes_read:
break
f.write(bytes_read)
progress.update(len(bytes_read))
服务器.py
with open(filename, "rb") as f:
while True:
bytes_read = f.read(BUFFER_SIZE)
if not bytes_read:
break
# the lines below should to be inside `while True`
client_socket.sendall(bytes_read)
progress.update(len(bytes_read))
错误的缩进client.py使它永远不会结束while循环。
第二:
Socket是原始对象,当数据结束时可能不会通知客户端 - 并且代码if not bytes_read: break可能无法正常工作。或者它可能会出现暂时的连接问题,并bytes_read = 0在服务器发送所有数据之前关闭连接。
您应该使用filesize来检查您是否获得所有数据。你可以倒计时 - 减去接收字节的大小,filesize直到你得到0
客户端.py
with open(filename, "wb") as f:
while filesize > 0: # <--- use `filesize`
bytes_read = s.recv(BUFFER_SIZE)
# this may not work
if not bytes_read:
break
filesize -= len(bytes_read) # <--- use `filesize`
f.write(bytes_read)
progress.update(len(bytes_read))
# --- after loop ---
if filesize == 0:
print('download completed')
else:
print('download uncomplete, missing', filesize, 'bytes')
我看到其他可能的问题。
客户端发送gamename到服务器,服务器发送f"{filename}{SEPARATOR}{filesize}"到客户端。套接字可能会发送分割成较小部分的数据并延迟发送它们,因此在这两种情况下它可能会部分发送它,然后服务器可能会变得不完整gamename并尝试使用错误的文件名。同样的情况也可能发生f"{filename}{SEPARATOR}{filesize}"- 客户端可能只得到其中的一部分,然后它可以在获取文件数据并将其保存在文件中时得到休息 - 所以你创建了不正确的文件。
您应该发送一个具有已发送数据大小的字节,而另一端应首先读取一个具有此大小的字节并使用它从服务器读取正确的数据。
示例f"{filename}{SEPARATOR}{filesize}"
server.py - 发送信息
# - send header -
# convert info to header
header = f"{filename}{SEPARATOR}{filesize}"
header_bytes = header.encode("utf-8")
header_size = len(header_bytes)
print('header:', header)
# first send 1-byte with header size
size = struct.pack("B", header_size)
client_socket.sendall(size)
# next send header
client_socket.sendall(header_bytes)
client.py - 接收信息
# - recv header -
# first recv 1-byte with header size
size = s.recv(1)
header_size = struct.unpack("B", size)[0]
# next recv header
header_bytes = s.recv(header_size)
header = header_bytes.decode('utf-8')
print('header:', header)
# convert header to info
filename, filesize = header.split(SEPARATOR)
filesize = int(filesize)
包含其他更改的完整代码
服务器.py
import socket
import tqdm
import os
import time
import struct
# --- constants ---
SEPARATOR = "<SEPARATOR>"
BUFFER_SIZE = 4096
# --- functions ---
def send_file(host, port, buffer_size):
print(f"Starting {host}:{port}")
s = None # default value at start
try:
s = socket.socket() # standard options TCP/IP
# solution for "[Error 89] Address already in use". Use before bind()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))
# kolejka klientów oczekujących na połączenie (max 5 klientów)
s.listen(5)
# --- loop ---
while True:
print("\nWaiting for client")
client_socket, client_address = s.accept()
print("Client:", client_address)
try:
time.sleep(0.5)
# --- recv request ---
received = client_socket.recv(buffer_size).decode("utf-8")
filename = received + ".zip"
print("received info about installation")
filesize = os.path.getsize(filename)
# --- send response ---
# - send header -
# convert info to header
header = f"{filename}{SEPARATOR}{filesize}"
header_bytes = header.encode("utf-8")
header_size = len(header_bytes)
print('header:', header)
# first send 1-byte with header size
size = struct.pack("B", header_size)
client_socket.sendall(size)
# next send header
client_socket.sendall(header_bytes)
# - send data -
progress = tqdm.tqdm(range(filesize), f"Sending {filename}", unit="B", unit_scale=True, unit_divisor=1024)
# send file
with open(filename, "rb") as f:
while True:
bytes_read = f.read(BUFFER_SIZE)
if not bytes_read:
break
client_socket.sendall(bytes_read)
progress.update(len(bytes_read))
# simulate slow connection (and see progress bar)
time.sleep(0.5)
progress.close() # stop displaying forgotten updates of progress bar
print('[DEBUG] end of file')
except BrokenPipeError as ex:
print('[Exception]', ex)
# --- after loop ---
except KeyboardInterrupt:
print('Stopped by Ctrl+C')
finally:
print('Closing')
if s is not None:
s.close()
# --- main ---
if __name__ == '__main__':
host = socket.gethostname()
send_file(host, 5000, BUFFER_SIZE)
客户端.py
import socket
import tqdm
import os
import time
import struct
# --- constants ---
SEPARATOR = "<SEPARATOR>"
BUFFER_SIZE = 4096
# --- other ---
SERVER_HOST = socket.gethostname()
SERVER_PORT = 5000
gamename = "gra"
# ---------------------------------------
s = socket.socket()
# --- connect ---
s.connect((SERVER_HOST, SERVER_PORT))
print("Succesfully connected to the server", (SERVER_HOST, SERVER_PORT))
# --- send request ---
request = gamename
request_bytes = request.encode("utf-8")
s.sendall(request_bytes)
print("request about downloaded game sended")
# --- recv response ---
# - recv header -
# first recv 1-byte with header size
size = s.recv(1)
header_size = struct.unpack("B", size)[0]
# next recv header
header_bytes = s.recv(header_size)
header = header_bytes.decode('utf-8')
print('header:', header)
# convert header to info
filename, filesize = header.split(SEPARATOR)
filesize = int(filesize)
# - recv data -
progress = tqdm.tqdm(range(filesize), f"Receiving {filename}", unit="B", unit_scale=True, unit_divisor=1024)
# change name only for tests
filename = 'output.zip'
with open(filename, "wb") as f:
while filesize > 0:
bytes_read = s.recv(BUFFER_SIZE)
if not bytes_read:
break
filesize -= len(bytes_read)
f.write(bytes_read)
progress.update(len(bytes_read))
# simulate slow connection (and see progress bar)
time.sleep(0.5)
progress.close() # stop displaying forgotten updates of progress bar
if filesize == 0:
print('download completed')
else:
print('download uncomplete, missing', filesize, 'bytes')
# --- close ---
f.close()