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

Python通过Socket向Android传输摄像头Base64视频流仅一帧后冻结问题

问题原因
  • Android端在循环内每次接收一帧就重新创建Socket连接,未复用已建立的TCP连接。旧连接断开后Python端仍持续向失效连接发送数据,TCP发送缓冲区被占满后send操作进入永久阻塞,直接导致Python程序冻结。同时Android端每次新建连接只会拉取一帧数据,因此仅能显示首帧画面。
  • Python端错误将UDP专用的sendto方法用于TCP套接字,且未处理客户端断开连接的异常,连接失效后无法退出内层循环回到监听状态。
  • 未校验摄像头读取结果:vid.read()可能读取失败返回空帧,后续resize操作会直接触发崩溃。
修复方案

Python端修改

替换原有main函数逻辑,核心修改点:异常捕获处理断开的连接、替换sendto为send、校验帧有效性、连接断开后主动关闭客户端套接字。

def main():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 开启端口复用,避免重启程序时端口占用
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    vid = cv2.VideoCapture(0)
    host_ip = "192.168.100.20"
    print('IP DEL HOST:', host_ip)
    port = 9999
    socket_address = (host_ip, port)
    server_socket.bind(socket_address)

    server_socket.listen(5)
    print("LUGAR DE ESCUCHA: ", socket_address)

    try:
        while True:    
            client_socket, addr = server_socket.accept()
            print('CONEXIÓN ENTRANTE:', addr)
            WIDTH = 400
            try:
                while True:
                    ret, img = vid.read()
                    # 校验帧读取是否成功
                    if not ret:
                        break
                    frame = imutils.resize(img,width=WIDTH)
                    cv2.imshow("Android_cam", frame)
                    encoded,buffer = cv2.imencode('.jpeg',frame,[cv2.IMWRITE_JPEG_QUALITY,50])
                    message = base64.b64encode(buffer)
                    size = len(message)
                    # TCP直接用send发送,不需要sendto
                    client_socket.send((str(size) + "\n").encode('utf-8'))
                    client_socket.send(message)
                    client_socket.send(("\nhappy face\n").encode('utf-8'))
                    if cv2.waitKey(1) == 27:
                        break
            except (BrokenPipeError, ConnectionResetError):
                # 客户端断开连接,正常退出内层循环
                print(f"客户端{addr}断开连接")
            finally:
                # 主动关闭客户端套接字
                client_socket.close()
            cv2.destroyAllWindows()
    except KeyboardInterrupt:
        print('Finalizado')
    finally:
        server_socket.close()
        vid.release()
        cv2.destroyAllWindows()

Android端修改

核心修改点:将Socket创建移到循环外复用连接、异常时主动关闭资源。

public class actualizar extends AsyncTask<String, Bitmap, String> {

    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    protected String doInBackground(String... params) {
        Log.i("hola","Running");
        Socket socket = null;
        BufferedReader mBufferIn = null;
        try {
            // 仅创建一次Socket,复用连接
            socket = new Socket(IP_HOST,PORT);
            mBufferIn = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
            boolean XD = true;
            while(XD){
                String sizeStr = mBufferIn.readLine();
                if (sizeStr == null) break;
                int size = Integer.parseInt(sizeStr);
                String base64Str = mBufferIn.readLine();
                if (base64Str == null) break;
                // 按实际长度截取有效base64内容
                String validBase64 = base64Str.substring(0,size);
                byte[] decodedString = Base64.decode(validBase64, Base64.DEFAULT);
                Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
                this.publishProgress(decodedByte);
                // 读取分隔符
                String happyFace = mBufferIn.readLine();
                if (happyFace == null || !happyFace.equals("happy face")) {
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 主动关闭资源
            try {
                if (mBufferIn != null) mBufferIn.close();
                if (socket != null) socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    protected void onProgressUpdate(Bitmap... values) {
        super.onProgressUpdate(values);
        imgShow.setImageBitmap(values[0]);
    }
}

内容的提问来源于stack exchange,提问作者Javier Muñoz Martín del Campo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 10:06:04