You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Socket报错Address already in use:树莓派与PC双向通信问题

解决端口占用及双向通信逻辑错误问题

一、临时解决端口占用问题

错误OSError: [Errno 98] Address already in use说明8000端口被其他进程占用,执行以下命令清理:

  1. 查找占用8000端口的进程ID:
    sudo lsof -i :8000
    
    或者用netstat:
    sudo netstat -tulpn | grep :8000
    
  2. 杀掉该进程:
    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()

三、代码修正说明

  1. 端口逻辑对齐:
    • PC监听8000端口接收图像,树莓派作为客户端连接该端口发送图像
    • 树莓派监听8001端口接收随机数,PC作为客户端连接该端口发送随机数
  2. 避免重复绑定:将服务端socket的创建和绑定移到循环外,避免每次循环重复绑定端口导致的TIME_WAIT占用
  3. 端口复用设置:所有socket都添加SO_REUSEADDR选项,允许端口快速复用
  4. 资源释放:确保每个socket和连接都在使用后正确关闭,避免资源泄漏

内容的提问来源于stack exchange,提问作者Emir Ünalan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 06:25:55