ConnectionResetError [Errno 54] TCP连接,试图从服务器请求和接收文件,但客户端被延迟.



总的来说,我运行一个UDP连接,然后通过TCP连接客户端。我希望tcp客户机请求tcp服务器(另一个客户机)一个文件,发送,并下载它。似乎发送方很好,但接收方没有完全接收文件或抛出异常,然后在一段时间后接收总文件,然后异常:我不断返回并考虑接收器是否应该处于while循环中,但我试图解释要发送的任何大小的文件。任何想法吗?TIA。

Exception in thread Thread-5 (file_receiving_thread):
Traceback (most recent call last):
File "/opt/homebrew/Cellar/python@3.11/3.11.2_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/threading.py", line 1038, in _bootstrap_inner
self.run()
File "/opt/homebrew/Cellar/python@3.11/3.11.2_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/threading.py", line 975, in run
self._target(*self._args, **self._kwargs)
File "/App.py", line 205, in file_receiving_thread
data = s.recvfrom(4096)
^^^^^^^^^^^^^^^^
ConnectionResetError: [Errno 54] Connection reset by peer
< Connection with client A closed. >
def file_receiving_thread(s, fileName):
print(f"< Downloading {fileName} ... >")

with open(f'downloaded_{fileName}', 'wb') as f:
while True:
data = s.recvfrom(1024)
if not data:
break
f.write(data[0])
print(f'< {fileName} downloaded successfully! >')


# given a file and a dir, this checks if file is in dir
def is_file_in_dir(file_path, dir_path):
for file_name in os.listdir(dir_path):
if file_name == os.path.basename(file_path):
return True
return False

def file_sender_thread(tcp_conn, tcp_client_address, requested_file_name_decoded):
global my_dir_path
global myTbl
# check if requested file exists
file_name_path_before_abs = os.path.join(my_dir_path, requested_file_name_decoded)
absolute_file_name_path = os.path.abspath(file_name_path_before_abs)
if not is_file_in_dir(absolute_file_name_path, my_dir_path):
print(f"< {requested_file_name_decoded} not found in directory {my_dir_path} >")
tcp_conn.close()
return
## send file
try:
with open(requested_file_name_decoded, 'rb') as f:
print(f'< Transferring {requested_file_name_decoded}... >')
while True:
data = f.read()
if not data:
break
tcp_conn.send(data)
print(f'< {requested_file_name_decoded} transferred successfully! >')

except FileNotFoundError:
tcp_conn.sendall(b'File not found')
print(f'File not found: {requested_file_name_decoded} ({tcp_client_address})')


#thread to listen for connections from other clients and SEND file
def tcp_listen_for_thread(my_tcp_port):

tcp_socket = socket(AF_INET, SOCK_STREAM)
tcp_socket.bind(('localhost', my_tcp_port))
tcp_socket.listen()
while True:
# Wait for a new client connection
tcp_conn, tcp_client_address = tcp_socket.accept()
print(f"< Accepting connection request from {tcp_client_address[0]} >")
requested_file_name = tcp_conn.recvfrom(4096)
requested_file_name_to_decode = requested_file_name[0]
requested_file_name_decoded = requested_file_name_to_decode.decode()
# go to file sender thread 
file_to_send_thread = threading.Thread(target=file_sender_thread, args=(tcp_conn, tcp_client_address, requested_file_name_decoded,))
file_to_send_thread.start()
file_to_send_thread.join()
tcp_conn.close()
print(f'< Connection with client ____ closed. >')

#thread to connect to other clients and REQUEST their file
def tcp_talk_thread(fileName, fileOwner, owners_tcp_IP, owners_tcp_port,):

with socket(AF_INET, SOCK_STREAM) as s:
s.connect((owners_tcp_IP, owners_tcp_port))
print(f"< Connection with client {fileOwner} established. >")
s.sendall(fileName.encode())
# go to file receiver thread
file_to_receive_thread = threading.Thread(target=file_receiving_thread, args=(s, fileName,))
file_to_receive_thread.start()
file_to_receive_thread.join()
s.close()
print(f'< Connection with client {fileOwner} closed. >')

TCP是面向流的,不是面向消息的。它只是一个字节流,它没有消息边界的概念,像UDP一样。

接收方在无限循环中读取文件数据,直到:

  • 发送方关闭连接

  • 连接失败

发送方在发送完文件的数据后关闭连接,但不能保证接收方在发生其他意外导致连接过早失败的情况下会检测到该关闭。

一个更好的设计选择是让发送者先发送文件的大小,然后发送文件的数据。接收方可以先读取大小,然后只读取大小指定的字节数。不要使用连接关闭作为成功指示器。将意外闭包视为传输失败。

在发送文件名时也应该做同样的事情(实际上,对于任何类型的变长数据)。在发送文件名的字符之前先发送文件名的长度,然后先读入长度,然后只读入指定的字符数。


另外,创建一个工作线程只是为了在start之后立即join,这是没有意义的,这是对工作线程的完全浪费。

相关内容

  • 没有找到相关文章

最新更新