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

树莓派4 TCP客户端停止接收.NET MAUI服务器数据问题排查

问题分析与解决方案

核心问题定位

你的通信故障和后续无法重连,主要由Python客户端的连接逻辑错误和C#服务器的事件处理与数据发送逻辑缺失共同导致,具体如下:

一、Python客户端的错误

  1. 频繁创建/销毁TCP连接
    代码在while True循环内每次都通过with socket.socket(...)新建连接,接收1字节数据后立即关闭连接。TCP连接的建立/关闭需要三次握手/四次挥手,频繁操作会:

    • 消耗树莓派和手机的端口资源,短时间内会出现端口耗尽,无法创建新连接;
    • 服务器端堆积大量处于TIME_WAIT状态的连接,最终无法接受新的连接请求。
  2. 无超时与异常处理
    s.recv(1)是阻塞式调用,当服务器没有数据发送时,客户端会一直卡在这个调用上;如果连接中途断开(比如手机网络波动),connect或recv会抛出异常,直接中断程序循环,无法尝试重新连接。

  3. GPIO初始化冗余
    部分GPIO引脚重复设置(比如leftForwardPin=21,后面又GPIO.setup(21, GPIO.OUT)),虽然不影响功能,但属于不必要的冗余代码。

二、C# MAUI服务器的错误

  1. 仅在客户端连接时发送一次数据
    只有ClientConnected事件触发时才发送当前command值,后续按钮修改command后,没有主动将新指令发送给已连接的客户端。这会导致客户端每次连接后只能收到一次初始指令,后续服务器没有数据发送,客户端recv阻塞,表现为"停止接收数据包"。

  2. 未实现关键事件处理方法
    ClientDisconnected和DataReceived方法直接抛出NotImplementedException,一旦客户端断开连接或有数据发送到服务器,服务器会因未捕获的异常崩溃,停止监听端口,客户端自然无法重新连接。

  3. 服务器IP绑定局限性
    硬编码绑定192.168.99.146,如果手机的IP地址发生变化(比如重启路由器后),服务器将无法被客户端连接;另外,启动时仅检查网络访问权限,未处理后续网络变化的情况。

连接停止且无法重置的根本原因

  1. 服务器因未捕获的异常崩溃,停止监听11000端口,客户端后续的connect请求会直接失败;
  2. 客户端频繁创建连接导致两端端口资源耗尽,无法建立新的TCP连接;
  3. 客户端无异常处理,一旦网络故障导致recv阻塞或连接失败,程序无法进入下一次循环尝试重连。

修复建议

Python客户端修复示例

import socket
from time import sleep
import RPi.GPIO as GPIO

host = "192.168.99.146"
port = 11000

# GPIO配置
leftBackwardPin = 26
leftForwardPin = 21
rightBackwardPin = 13
rightForwardPin = 19
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(20, GPIO.IN)
# 统一初始化GPIO输出引脚
GPIO.setup([leftBackwardPin, leftForwardPin, rightBackwardPin, rightForwardPin], GPIO.OUT)

def reset_motors():
    # 封装电机停止逻辑,避免重复代码
    GPIO.output(leftForwardPin, GPIO.LOW)
    GPIO.output(rightForwardPin, GPIO.LOW)
    GPIO.output(leftBackwardPin, GPIO.LOW)
    GPIO.output(rightBackwardPin, GPIO.LOW)

while True:
    try:
        # 建立长连接,而不是每次循环新建
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.settimeout(5)  # 设置超时,避免无限阻塞
            s.connect((host, port))
            print("已连接到服务器")
            
            while True:
                try:
                    data = s.recv(1)
                    if not data:
                        print("服务器断开连接")
                        break
                    
                    print(f"收到指令: {data}")
                    reset_motors()
                    if data == b'1':
                        print("前进")
                        GPIO.output(leftForwardPin, GPIO.HIGH)
                        GPIO.output(rightForwardPin, GPIO.HIGH)
                    elif data == b'2':
                        print("后退")
                        GPIO.output(leftBackwardPin, GPIO.HIGH)
                        GPIO.output(rightBackwardPin, GPIO.HIGH)
                    elif data == b'3':
                        print("左转")
                        GPIO.output(leftForwardPin, GPIO.HIGH)
                        GPIO.output(rightBackwardPin, GPIO.HIGH)
                    elif data == b'4':
                        print("右转")
                        GPIO.output(leftBackwardPin, GPIO.HIGH)
                        GPIO.output(rightForwardPin, GPIO.HIGH)
                    # 其他情况执行reset_motors停止电机
                    sleep(0.1)
                except socket.timeout:
                    print("接收超时,保持连接")
                    continue
                except Exception as e:
                    print(f"通信错误: {e}")
                    break
    except Exception as e:
        print(f"连接失败: {e}")
        print("5秒后尝试重新连接...")
        reset_motors()
        sleep(5)

C# MAUI服务器修复示例

using Microsoft.Maui.Controls;
using SuperSimpleTcp;
using System.Diagnostics;
using System.Text;

namespace MauiApp1;

public partial class MainPage : ContentPage
{
    private int _command = 0;
    private SimpleTcpServer? _server;
    private string _clientIpPort = string.Empty; // 存储已连接客户端的标识

    public MainPage()
    {
        InitializeComponent();
        StartTcpServer();
    }

    private void StartTcpServer()
    {
        // 绑定到所有网卡的11000端口,避免IP硬编码问题
        _server = new SimpleTcpServer("0.0.0.0:11000");
        
        _server.Events.ClientConnected += OnClientConnected;
        _server.Events.ClientDisconnected += OnClientDisconnected;
        _server.Events.DataReceived += OnDataReceived;

        try
        {
            _server.Start();
            Debug.WriteLine("TCP服务器已启动");
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"启动服务器失败: {ex.Message}");
        }
    }

    private void OnDataReceived(object sender, DataReceivedEventArgs e)
    {
        // 实现数据接收逻辑,避免抛异常
        Debug.WriteLine($"收到客户端数据: {Encoding.UTF8.GetString(e.Data)}");
    }

    private void OnClientDisconnected(object sender, ConnectionEventArgs e)
    {
        Debug.WriteLine($"客户端断开连接: {e.IpPort}");
        _clientIpPort = string.Empty;
    }

    private void OnClientConnected(object sender, ConnectionEventArgs e)
    {
        Debug.WriteLine($"客户端已连接: {e.IpPort}");
        _clientIpPort = e.IpPort;
        // 发送初始指令
        SendCommandToClient();
    }

    private void SendCommandToClient()
    {
        if (_server != null && !string.IsNullOrEmpty(_clientIpPort) && _server.IsListening)
        {
            try
            {
                _server.Send(_clientIpPort, _command.ToString());
                Debug.WriteLine($"发送指令: {_command}");
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"发送指令失败: {ex.Message}");
            }
        }
    }

    private void Forward(object sender, EventArgs e)
    {
        _command = 1;
        SendCommandToClient();
    }

    private void Backward(object sender, EventArgs e)
    {
        _command = 2;
        SendCommandToClient();
    }

    private void Left(object sender, EventArgs e)
    {
        _command = 3;
        SendCommandToClient();
    }

    private void Right(object sender, EventArgs e)
    {
        _command = 4;
        SendCommandToClient();
    }

    private void Released(object sender, EventArgs e)
    {
        _command = 0;
        SendCommandToClient();
    }
}

内容的提问来源于stack exchange,提问作者Михаил Димитров

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 19:57:25