提示:本博客是在前面几篇博客上进行的最终优化,后续将不再更新这类问题。
文章目录
前言
提示:本博客解决了我的前几篇博客中提出的所有问题,针对代码程序进行了优化,下面链接是我的前几篇博客:
【NVIDIA JETSON AGX XAVIER】与个人笔记本(win11)建立TCP-IP连接传输数据(含源码)
【NVIDIA JETSON AGX XAVIER】与个人笔记本(win11)建立TCP-IP连接相互传输数据(含源码)
上面这几篇博客中出现的问题:
1.无法实现连续传输数据
2.无法实现从客户端(Xavier)传输数据到服务器端(个人笔记本电脑)
3.Xavier板子无法作为服务器进行TCP-IP连接
本博客将一一进行解决,并记录解决过程中出现的问题。
不想看改进过程的可以直接点击目录跳转到“五、2.3”程序看最终的代码
一、实现持续传输数据
1.服务器代码改进
在连接客户端程序中的def server_program()函数中,使用while循环实时接收到数据,并继续等待客户端新的文件请求。
while True:
2.服务器代码改进
在连接服务器程序中的client_program()函数中添加外部循环以持续请求文件
while True:
并增加代码,输入文件名可以一直传输数据,直到输入“exit”结束程序
file_name = input("输入要传输的文件名或输入'exit'退出: ")
具体实现循环的代码见以下链接中【一、个人笔记本(win11)传输数据到XAVIER(多次传输)】
【NVIDIA JETSON AGX XAVIER】与个人笔记本(win11)建立TCP-IP连接相互传输数据(含源码)
二、实现相互传输数据,但存在问题
具体实现相互传输的代码见以下链接中【二、两端相互传输(以另一种形式解决上一篇博客的问题)】
【NVIDIA JETSON AGX XAVIER】与个人笔记本(win11)建立TCP-IP连接相互传输数据(含源码)
3.程序出现问题
以上代码实现了相互传输数据,但是出现问题如下:
添加好程序后能够传输文件,传输后文件内容为空,且客户端send发送一次文件后,再次send无法发送文件,但是再强制结束进程后,第一次传输的文件大小从空恢复正常。
强制结束进程客户端(Xavier)报错如下:
查找资料寻找解决方法:客户端在传输一次数据后需要断开(执行close)
客户端第一次 send 后,如果没有执行 shutdown 或 close,可能会发生阻塞或无法发送。
需要确保服务器端已经准备好接收新的数据。如果服务器端处理第一次接收的文件内容后没有正确地再次准备接收新数据,客户端可能会无法进行二次发送。
解决:
在数据发送和接收的代码周围添加适当的错误判断和处理机制,比如尝试捕获和处理可能发生的异常。
具体代码:
服务器代码:
在receive_file(filename, conn)函数中
增加
conn.send(b'READY')
和
conn.send(b'DONE') # 发送文件接收完成的确认消息
发送给客户端“服务器以准备好接收数据”
对应客户端代码会有如下代码
if response == 'READY':
……
……
if response == 'DONE':
print("文件成功发送并由服务器接收。")
else:
print("服务器未确认文件接收完成。")
else:
print("服务器未准备好接收文件。")
同样在服务器的send_file()函数中有
if user_response[:2] == 'OK':
对应客户端中的
client_socket.send(b'OK')
修改后代码如下
三、解决传输中的缓存问题
1.服务器
代码如下(示例):
import socket
import os
import chardet
def send_file(filename, conn):
if os.path.isfile(filename):
# 发送文件存在确认消息
conn.send(b'EXISTS ' + str(os.path.getsize(filename)).encode())
user_response = conn.recv(1024).decode()
if user_response[:2] == 'OK':
with open(filename, 'rb') as f:
bytes_to_send = f.read(1024)
conn.send(bytes_to_send)
# while bytes_to_send != "":
while bytes_to_send:
bytes_to_send = f.read(1024)
conn.send(bytes_to_send)
else:
conn.send(b'ERR ')
def server_program():
# 获取主机名
host = socket.gethostname()
port = 5011 # 初始化端口号
server_socket = socket.socket() # 获取socket对象
server_socket.bind((host, port)) # 绑定地址到socket
server_socket.listen(5) # 监听连接,最多可接受5个连接
print("服务器启动,等待连接...")
conn, address = server_socket.accept() # 接受新连接
print("连接来自: " + str(address))
while True:
# raw_data = conn.recv(1024)
# if not raw_data:
# break
# encoding = chardet.detect(raw_data)['encoding']
# data = raw_data.decode(encoding)
data = conn.recv(1024).decode()
if data.startswith('send'):
filename = data[5:]
receive_file(filename, conn)
elif data.startswith('get'):
send_file(data[4:], conn)
if not data:
break
conn.close()
def receive_file(filename, conn):
conn.send(b'READY')
with open(filename, 'wb') as f:
while True:
bytes_read = conn.recv(1024)
if not bytes_read:
break
f.write(bytes_read)
break
# conn.send(b'DONE') # 发送文件接收完成的确认消息
conn.send(b'DONE') # 发送文件接收完成的确认消息
if __name__ == '__main__':
server_program()
2.客户端
代码如下(示例):
import socket
import os
def client_program():
host = socket.gethostname()
port = 5011
client_socket = socket.socket()
# 172.17.0.1
client_socket.connect(("192.168.137.1", port))
print("连接成功")
while True: # 添加外部循环以持续请求文件
action = input("输入 'get' 请求文件,或者 'send' 发送文件,或输入'exit'退出:")
if action.lower() == 'exit':
break
elif action.lower() == 'get':
file_name = input("输入要传输的文件名: ")
client_socket.send(file_name.encode())
data = client_socket.recv(1024).decode()
if data[:6] == 'EXISTS':
file_size = int(data[6:])
client_socket.send(b'OK')
# f = open(r'' + file_name, 'wb')
f = open(r'new_' + file_name, 'wb')
data = client_socket.recv(1024)
total_recv = len(data)
f.write(data)
while total_recv < file_size:
data = client_socket.recv(1024)
total_recv += len(data)
f.write(data)
print("{0:.2f}".format((total_recv/float(file_size))*100)+ "% Done")
print("下载完成!")
f.close()
else:
print("文件不存在!")
client_socket.send(file_name.encode())
elif action.lower() == 'send':
file_name = input("输入要发送的文件名: ")
if os.path.isfile(file_name): # 在这里添加检查
send_file_to_server(file_name, client_socket)
print("成功发送:" + file_name)
else:
print("文件不存在,请检查文件名是否正确。")
client_socket.close()
def send_file_to_server(filename, client_socket):
if os.path.isfile(filename):
client_socket.send(('send ' + filename).encode())
response = client_socket.recv(1024).decode()
if response == 'READY':
with open(filename, 'rb') as f:
bytes_to_send = f.read(1024)
while bytes_to_send:
client_socket.send(bytes_to_send)
bytes_to_send = f.read(1024)
response = client_socket.recv(1024).decode() # 等待服务器的确认消息
# print("wd")
if response == 'DONE':
print("文件成功发送并由服务器接收。")
else:
print("服务器未确认文件接收完成。")
else:
print("服务器未准备好接收文件。")
else:
print("文件不存在,请检查文件名是否正确。")
if __name__ == '__main__':
client_program()
3.出现新问题
在客户端尝试接收文件(get)时报错,解决了相互传输和传输时延问题,一开始已经实现的从服务器传输数据到客户端居然失败了。
调试代码,寻找问题所在:
在程序中一步一步添加print()输出,判断程序卡在哪一步不能运行,最后找到
在客户端中
client_socket.send(file_name.encode())
原本写的代码如上,修改为下述代码后,客户端成功get接收到程序。
解决:
client_socket.send(('get ' +file_name).encode())
是因为服务器中存在以下代码,在get时,只接收data第四个字节后的数据
if data.startswith('send'):
filename = data[5:]
receive_file(filename, conn)
elif data.startswith('get'):
send_file(data[4:], conn)
if not data:
break
解决后完整的代码:
4.服务器
代码如下(示例):
import socket
import os
import chardet
def send_file(filename, conn):
if os.path.isfile(filename):
# 发送文件存在确认消息
conn.send(b'EXISTS ' + str(os.path.getsize(filename)).encode())
user_response = conn.recv(1024).decode()
if user_response[:2] == 'OK':
with open(filename, 'rb') as f:
bytes_to_send = f.read(1024)
conn.send(bytes_to_send)
# while bytes_to_send != "":
while bytes_to_send:
bytes_to_send = f.read(1024)
conn.send(bytes_to_send)
else:
conn.send(b'ERR ')
def server_program():
# 获取主机名
host = socket.gethostname()
port = 5011 # 初始化端口号
server_socket = socket.socket() # 获取socket对象
server_socket.bind((host, port)) # 绑定地址到socket
server_socket.listen(5) # 监听连接,最多可接受5个连接
print("服务器启动,等待连接...")
conn, address = server_socket.accept() # 接受新连接
print("连接来自: " + str(address))
while True:
# raw_data = conn.recv(1024)
# if not raw_data:
# break
# encoding = chardet.detect(raw_data)['encoding']
# data = raw_data.decode(encoding)
data = conn.recv(1024).decode()
if data.startswith('send'):
filename = data[5:]
receive_file(filename, conn)
elif data.startswith('get'):
send_file(data[4:], conn)
if not data:
break
conn.close()
def receive_file(filename, conn):
conn.send(b'READY')
with open(filename, 'wb') as f:
while True:
bytes_read = conn.recv(1024)
if not bytes_read:
break
f.write(bytes_read)
break
# conn.send(b'DONE') # 发送文件接收完成的确认消息
conn.send(b'DONE') # 发送文件接收完成的确认消息
if __name__ == '__main__':
server_program()
5.客户端
代码如下(示例):
import socket
import os
def client_program():
host = socket.gethostname()
port = 5011
client_socket = socket.socket()
# 172.17.0.1
client_socket.connect(("192.168.137.1", port))
print("连接成功")
while True: # 添加外部循环以持续请求文件
action = input("输入 'get' 请求文件,或者 'send' 发送文件,或输入'exit'退出:")
if action.lower() == 'exit':
break
elif action.lower() == 'get':
file_name = input("输入要传输的文件名: ")
client_socket.send(('get ' +file_name).encode())
print("wd")
data = client_socket.recv(1024).decode()
if data[:6] == 'EXISTS':
file_size = int(data[6:])
client_socket.send(b'OK')
# f = open(r'' + file_name, 'wb')
# f = open(r'new_' + file_name, 'wb')
f = open('new_' + file_name, 'wb')
data = client_socket.recv(1024)
total_recv = len(data)
f.write(data)
while total_recv < file_size:
data = client_socket.recv(1024)
total_recv += len(data)
f.write(data)
print("{0:.2f}".format((total_recv/float(file_size))*100)+ "% Done")
print("下载完成!")
f.close()
else:
print("文件不存在!")
client_socket.send(file_name.encode())
elif action.lower() == 'send':
file_name = input("输入要发送的文件名: ")
if os.path.isfile(file_name): # 在这里添加检查
send_file_to_server(file_name, client_socket)
print("成功发送:" + file_name)
else:
print("文件不存在,请检查文件名是否正确。")
client_socket.close()
def send_file_to_server(filename, client_socket):
if os.path.isfile(filename):
client_socket.send(('send ' + filename).encode())
response = client_socket.recv(1024).decode()
if response == 'READY':
with open(filename, 'rb') as f:
bytes_to_send = f.read(1024)
while bytes_to_send:
client_socket.send(bytes_to_send)
bytes_to_send = f.read(1024)
response = client_socket.recv(1024).decode() # 等待服务器的确认消息
# print("wd")
if response == 'DONE':
print("文件成功发送并由服务器接收。")
else:
print("服务器未确认文件接收完成。")
else:
print("服务器未准备好接收文件。")
else:
print("文件不存在,请检查文件名是否正确。")
if __name__ == '__main__':
client_program()
6.运行结果
Xavier(客户端·)从个人笔记本电脑(服务器端)接收的文件将在前面增加new_ 进行命名。文件将保存在代码当前的目录。
个人笔记本电脑(服务器端)从Xavier(客户端·)接收的文件将保存在代码当前的目录。
输入exit结束进程
7.意料之外
就当我以为一切都解决时,新的问题出现了,在客户端(Xavier)尝试传输docx文件给服务器(个人笔记本)时,客户端(Xavier)没有保存,但服务器(个人笔记本)报错:
四、目前进展
上述代码能够实现:
无延迟实现,Xavier(客户端)可接收任何服务器文件,可发送文件,但是只发送txt格式文件成功 ,发送docx、jpg、pkl文件失败
后面的任务:改进算法,使Xavier能传输任何数据给个人笔记本电脑
改进好后我将编辑此博客,并附上源码!
五、最终优化
1.解决问题
反思:一开始的算法Xavier(客户端)能够传输任何格式文件到个人笔记本**,只是存在传输延时,当程序结束时才能发送**
而目前的算法只能实现无延迟传输txt格式
从源头开始分析发现在服务器(个人笔记本)代码中
if not bytes_read:
break
f.write(bytes_read)
从来都没有运行过
也就是说在客户端发送完数据后,服务器端仍然在接收数据,没有断开进入下一次。(就是说客户端没有发送空数据,使bytes_read为空)
只要这里能够进入if,从而break,就能够解决问题。
即需要让客户端发送空数据使服务器端break
尝试以下两种代码都报错:
client_socket.send("")
client_socket.send(b'')
在询问GPT后,GPT给出解答:
在客户端代码中,你需要设计一个协议来标识数据发送的结束。这通常有两种做法:关闭连接或发送特定的结束标志。下面将这两种方法分别描述,并展示如何实现它们。
由于我需要在这一进程中持续传输数据,所以不能关闭连接,只能使用特定的结束标志
客户端代码示例:
#特定数据包作为结束表示
END_OF_FILE_MARKER = b"EOF"
client_socket.send(END_OF_FILE_MARKER)
在服务端,需要修改代码来检测这个特定的结束标志。因为要能够分辨出文件数据和结束标志:
服务端代码示例:
END_OF_FILE_MARKER = b"EOF"
if END_OF_FILE_MARKER in bytes_read: # 检查是否收到结束标志
break
f.write(bytes_read)
这样就解决了不能进入if的问题
完整代码如下:
2.服务器端最终代码
注意在测试代码时将ip地址改为自己的
import socket
import os
import chardet
def send_file(filename, conn):
if os.path.isfile(filename):
# 发送文件存在确认消息
conn.send(b'EXISTS ' + str(os.path.getsize(filename)).encode())
user_response = conn.recv(1024).decode()
if user_response[:2] == 'OK':
with open(filename, 'rb') as f:
bytes_to_send = f.read(1024)
conn.send(bytes_to_send)
# while bytes_to_send != "":
while bytes_to_send:
bytes_to_send = f.read(1024)
conn.send(bytes_to_send)
else:
conn.send(b'ERR ')
def server_program():
# 获取主机名
host = socket.gethostname()
port = 5011 # 初始化端口号
server_socket = socket.socket() # 获取socket对象
server_socket.bind((host, port)) # 绑定地址到socket
server_socket.listen(5) # 监听连接,最多可接受5个连接
print("服务器启动,等待连接...")
conn, address = server_socket.accept() # 接受新连接
print("连接来自: " + str(address))
while True:
# raw_data = conn.recv(1024)
# if not raw_data:
# break
# encoding = chardet.detect(raw_data)['encoding']
# data = raw_data.decode(encoding)
data = conn.recv(1024).decode()
if data.startswith('send'):
filename = data[5:]
receive_file(filename, conn)
elif data.startswith('get'):
send_file(data[4:], conn)
if not data:
# continue
break
conn.close()
def receive_file(filename, conn):
conn.send(b'READY')
END_OF_FILE_MARKER = b"EOF"
with open(filename, 'wb') as f:
while True:
bytes_read = conn.recv(1024)
# if not bytes_read:
if END_OF_FILE_MARKER in bytes_read: # 检查是否收到结束标志
# print("1")
break
f.write(bytes_read)
# print("12")
# conn.send(b'DONE')
# print("123")
# break
# conn.send(b'DONE') # 发送文件接收完成的确认消息
conn.send(b'DONE') # 发送文件接收完成的确认消息
if __name__ == '__main__':
server_program()
3.客户端最终代码
import socket
import os
def client_program():
host = socket.gethostname()
port = 5011
client_socket = socket.socket()
# 172.17.0.1
client_socket.connect(("192.168.137.1", port))
print("连接成功")
while True: # 添加外部循环以持续请求文件
action = input("输入 'get' 请求文件,或者 'send' 发送文件,或输入'exit'退出:")
if action.lower() == 'exit':
break
elif action.lower() == 'get':
file_name = input("输入要传输的文件名: ")
client_socket.send(('get ' +file_name).encode())
# print("wd")
data = client_socket.recv(1024).decode()
if data[:6] == 'EXISTS':
file_size = int(data[6:])
client_socket.send(b'OK')
# f = open(r'' + file_name, 'wb')
# f = open(r'new_' + file_name, 'wb')
f = open('new_' + file_name, 'wb')
data = client_socket.recv(1024)
total_recv = len(data)
f.write(data)
while total_recv < file_size:
data = client_socket.recv(1024)
total_recv += len(data)
f.write(data)
print("{0:.2f}".format((total_recv/float(file_size))*100)+ "% Done")
print("下载完成!")
f.close()
else:
print("文件不存在!")
client_socket.send(file_name.encode())
elif action.lower() == 'send':
file_name = input("输入要发送的文件名: ")
if os.path.isfile(file_name): # 在这里添加检查
send_file_to_server(file_name, client_socket)
print("成功发送:" + file_name)
else:
print("文件不存在,请检查文件名是否正确。")
client_socket.close()
def send_file_to_server(filename, client_socket):
# 特定数据包作为结束表示
END_OF_FILE_MARKER = b"EOF"
if os.path.isfile(filename):
client_socket.send(('send ' + filename).encode())
response = client_socket.recv(1024).decode()
if response == 'READY':
with open(filename, 'rb') as f:
bytes_to_send = f.read(1024)
while bytes_to_send:
client_socket.send(bytes_to_send)
bytes_to_send = f.read(1024)
client_socket.send(END_OF_FILE_MARKER)
# client_socket.send(b'')
response = client_socket.recv(1024).decode() # 等待服务器的确认消息
# print("wd")
if response == 'DONE':
print("文件成功发送并由服务器接收。")
else:
print("服务器未确认文件接收完成。")
else:
print("服务器未准备好接收文件。")
else:
print("文件不存在,请检查文件名是否正确。")
if __name__ == '__main__':
client_program()
4.运行结果
服务器:
客户端:
总结
【NVIDIA JETSON AGX XAVIER】与个人笔记本电脑建立TCP-IP连接实现相互传输数据
实现这个功能虽然遇到了许多问题,但是经过自己的努力最后都一一解决了,过程是艰辛的,但结果是好的。