Python Socket报错Address already in use:树莓派与PC双向通信问题
解决端口占用及双向通信逻辑错误问题
一、临时解决端口占用问题
错误OSError: [Errno 98] Address already in use说明8000端口被其他进程占用,执行以下命令清理:
- 查找占用8000端口的进程ID:
或者用netstat:sudo lsof -i :8000sudo netstat -tulpn | grep :8000 - 杀掉该进程:
把sudo kill -9 <PID><PID>替换成上面查到的进程号。
二、根本问题:代码端口逻辑混乱
你当前的代码存在端口对应错误、重复绑定socket的问题,导致连接失败和端口占用。以下是修正后的完整代码:
树莓派端代码(负责采集图像并接收随机数)
import io import cv2 import socket import struct import time import numpy as np from PIL import Image # 输入PC的局域网IP地址 pc_ip = str(input('Enter PC IP address: ')) # 初始化摄像头 camera = cv2.VideoCapture(0) # 初始化接收随机数的服务端socket(仅创建一次,避免重复绑定) server_socket = socket.socket() server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind(('0.0.0.0', 8001)) server_socket.listen(1) print("树莓派监听8001端口,等待PC连接...") try: while True: # 1. 向PC发送摄像头图像(PC监听8000端口) send_socket = socket.socket() send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: send_socket.connect((pc_ip, 8000)) send_conn = send_socket.makefile('wb') ret, frame = camera.read() if not ret: print("读取摄像头失败,重试中...") time.sleep(1) continue # 图像预处理 frame = cv2.resize(frame, (640, 480)) frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) img = Image.fromarray(frame) # 转为字节流发送 stream = io.BytesIO() img.save(stream, format="JPEG") stream.seek(0) image_bytes = stream.read() # 先发送图像长度,再发送图像数据 send_conn.write(struct.pack('<L', len(image_bytes))) send_conn.flush() send_conn.write(image_bytes) send_conn.flush() stream.truncate() finally: send_conn.close() send_socket.close() # 2. 接收PC发送的随机数 client_conn, addr = server_socket.accept() print(f"PC [{addr[0]}] 已连接") try: rand_num = client_conn.recv(1024).decode().strip() if rand_num: print(f"收到PC的随机数: {rand_num}") finally: client_conn.close() finally: camera.release() server_socket.close()
PC端代码(负责接收图像并发送随机数)
import cv2 import io import socket import struct from PIL import Image import numpy as np import random # 填写树莓派的局域网IP地址 pi_ip = '192.168.1.109' # 初始化接收图像的服务端socket(仅创建一次) server_socket = socket.socket() server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind(('0.0.0.0', 8000)) server_socket.listen(1) print("PC监听8000端口,等待树莓派连接...") try: while True: # 1. 接收树莓派发送的图像 client_conn, addr = server_socket.accept() print(f"树莓派 [{addr[0]}] 已连接") try: # 读取图像长度 image_len = struct.unpack('<L', client_conn.read(struct.calcsize('<L')))[0] if not image_len: continue # 读取图像数据 image_stream = io.BytesIO() image_stream.write(client_conn.read(image_len)) image_stream.seek(0) # 转换为OpenCV格式并显示,可在此处添加图像修改逻辑 img = Image.open(image_stream).convert('RGB') img_np = np.array(img) # 示例:在图像上画一个绿色矩形 cv2.rectangle(img_np, (50, 50), (150, 150), (0, 255, 0), 2) cv2.imshow('Raspberry Pi Camera (Modified)', cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR)) cv2.waitKey(1) finally: client_conn.close() # 2. 向树莓派发送随机数(树莓派监听8001端口) send_socket = socket.socket() send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: send_socket.connect((pi_ip, 8001)) rand_num = random.randint(0, 100) send_socket.send(str(rand_num).encode()) print(f"发送随机数 {rand_num} 到树莓派") finally: send_socket.close() finally: cv2.destroyAllWindows() server_socket.close()
三、代码修正说明
- 端口逻辑对齐:
- PC监听8000端口接收图像,树莓派作为客户端连接该端口发送图像
- 树莓派监听8001端口接收随机数,PC作为客户端连接该端口发送随机数
- 避免重复绑定:将服务端socket的创建和绑定移到循环外,避免每次循环重复绑定端口导致的TIME_WAIT占用
- 端口复用设置:所有socket都添加
SO_REUSEADDR选项,允许端口快速复用 - 资源释放:确保每个socket和连接都在使用后正确关闭,避免资源泄漏
内容的提问来源于stack exchange,提问作者Emir Ünalan
相关产品推荐
相关产品推荐

