Python Socket连接成功但无数据传输问题求助
问题描述
我刚开始学习Python Socket编程,尝试通过Python Socket与Raspberry Pi 3进行数据收发。服务端程序运行在Raspberry Pi上,客户端程序运行在我的Ubuntu系统笔记本上,二者处于同一WiFi网络。程序启动后客户端能够成功连接服务端,但没有数据传输,客户端和服务端均无任何响应;不过在同一设备上运行两端程序时一切正常。
补充说明:当我在客户端按下Ctrl + C后,服务端似乎会在断开连接前发送部分数据。
客户端代码
import socket HOST = '172.16.31.51' PORT = 5000 BUFSIZ = 1024 if __name__ == '__main__': client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) host = input("Enter hostname [%s]: " %HOST) or HOST port = input("Enter port [%s]: " %PORT) or PORT sock_addr = (host, int(port)) client_sock.connect(sock_addr) payload = 'GET TIME' try: while True: client_sock.send(payload.encode('utf-8')) data = client_sock.recv(BUFSIZ) print(repr(data)) more = input("Want to send more data to server[y/n]:") if more.lower() == 'y': payload = input("Enter payload: ") else: break except KeyboardInterrupt: print("Exited by user") client_sock.close()
服务端代码
import socket from time import ctime PORT = 5000 BUFSIZ = 1024 ADDR = ('', PORT) if __name__ == '__main__': server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server_socket.bind(ADDR) server_socket.listen(5) server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1) while True: print('Server waiting for connection...') client_sock, addr = server_socket.accept() print('Client connected from: ', addr) while True: data = client_sock.recv(BUFSIZ) if not data or data.decode('utf-8') == 'END': break print("Received from client: %s" % data.decode('utf-8')) print("Sending the server time to client: %s" %ctime()) try: client_sock.send(bytes(ctime(), 'utf-8')) except KeyboardInterrupt: print("Exited by user") client_sock.close() server_socket.close()
问题分析与解决方案
你的代码在本地设备运行正常,但跨设备通信时卡住,核心原因是TCP的Nagle算法导致小数据包被缓冲延迟发送,加上跨网络的传输特性,使得客户端发送的数据无法及时到达服务端,两边都卡在recv()调用上。另外服务端的SO_REUSEADDR设置位置也有问题,下面是具体的修复和调试建议:
1. 禁用Nagle算法,确保数据即时发送
在客户端连接成功后,添加一行代码禁用TCP_NODELAY选项,这样客户端发送的小数据包会立即发送到服务端,不会被系统缓冲:
client_sock.connect(sock_addr) # 添加这行代码,禁用Nagle算法 client_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
2. 修正服务端SO_REUSEADDR的设置位置
SO_REUSEADDR选项用于允许端口复用,应该在bind()之前设置,否则可能无法生效:
server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 将setsockopt移到bind之前 server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1) server_socket.bind(ADDR) server_socket.listen(5)
3. 检查Raspberry Pi的防火墙配置
虽然你能成功建立连接,但仍需确认Pi上的5000端口是否允许双向通信:
- 临时关闭防火墙测试:
sudo ufw disable - 测试正常后,重新开启防火墙并开放端口:
sudo ufw enable sudo ufw allow 5000/tcp
4. 调试辅助建议
可以在代码中添加打印语句,帮助你确认数据的收发状态:
- 在客户端
send()后添加打印:client_sock.send(payload.encode('utf-8')) print(f"已发送数据: {payload}") - 在服务端
recv()后添加打印:data = client_sock.recv(BUFSIZ) if not data or data.decode('utf-8') == 'END': break print(f"收到原始数据: {data}") print("Received from client: %s" % data.decode('utf-8'))
完成以上修改后,跨设备的Socket数据收发应该就能正常工作了。
内容的提问来源于stack exchange,提问作者Sparsh Kumar




