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

Xamarin中TcpListener启动报NullReferenceException异常如何解决

Xamarin.Forms 中 TcpListener.Start() 抛出 NullReferenceException 排查修复

核心报错原因

  • 绑定IP无效导致内部套接字未初始化:你硬编码的192.168.1.2并非当前安卓设备实际持有的局域网IP,TcpListener在绑定不存在的IP时,内部核心Socket对象初始化失败为null,调用Start()方法时直接触发空引用异常。
  • 缺少安卓网络权限:Xamarin.Android项目默认未开启网络访问权限,无权限时所有Socket类API调用都会被系统拦截,对象无法正常初始化。
  • 主线程执行阻塞网络操作:你把TCP服务启动、同步等待客户端连接的逻辑全部写在了MainPage构造函数(UI主线程)中,安卓系统从3.0版本开始严格禁止主线程执行网络请求,会直接抛出异常;同时AcceptTcpClient()是同步阻塞方法,写在构造函数会直接卡死UI线程触发ANR。
  • 无空值防护:定时器中直接调用stream.Read(),如果客户端未连接、流对象为null时会额外触发空引用。

修复步骤

1. 配置安卓必要权限

打开Xamarin.Android项目路径下的Properties/AndroidManifest.xml,在manifest根节点下添加以下权限配置:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

2. 修正TCP服务启动逻辑

  • 不要硬编码监听IP,使用IPAddress.Any监听设备所有网卡的指定端口,避免IP不匹配导致绑定失败。
  • 把网络相关逻辑从构造函数移到页面OnAppearing生命周期方法中,放入后台线程执行,避免阻塞UI线程。
  • 所有跨线程UI更新必须切回主线程执行,避免线程访问异常。
  • 页面销毁时主动释放TCP相关资源,避免内存泄漏。

3. 修复后可运行代码参考

using System;
using Xamarin.Forms;
using System.Net.Sockets;
using System.Net;
using Android.Widget;
using System.Text;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Teplica
{
    public partial class MainPage : ContentPage
    {
        private int port = 8545;
        private bool door = true;
        TcpListener server;
        TcpClient client = null;
        NetworkStream stream = null;
        private bool TimerEnable = true;
        private List<string> list = new List<string>();

        public MainPage()
        {
            InitializeComponent();
        }

        protected override async void OnAppearing()
        {
            base.OnAppearing();
            // 后台线程启动TCP服务,不阻塞UI
            await Task.Run(() =>
            {
                try
                {
                    // 监听所有网卡对应端口,无需硬编码IP
                    server = new TcpListener(IPAddress.Any, port);
                    server.Start();
                    
                    // 阻塞等待ESP8266连接
                    client = server.AcceptTcpClient();
                    stream = client.GetStream();

                    // 连接成功后启动数据读取定时器
                    Device.StartTimer(TimeSpan.FromSeconds(1), TeplicaTimer);
                    
                    Device.BeginInvokeOnMainThread(() =>
                    {
                        Toast.MakeText(Android.App.Application.Context, "TCP服务启动成功,等待传感器数据", ToastLength.Short).Show();
                    });
                }
                catch (Exception ex)
                {
                    Device.BeginInvokeOnMainThread(() =>
                    {
                        Toast.MakeText(Android.App.Application.Context, $"服务启动失败:{ex.Message}", ToastLength.Long).Show();
                    });
                }
            });
        }

        private bool TeplicaTimer()
        {
            // 空值判断避免异常
            if (stream == null || !stream.CanRead)
            {
                return TimerEnable;
            }
            try
            {
                byte[] data = new byte[256];
                StringBuilder builder = new StringBuilder();
                int bytes = stream.Read(data, 0, data.Length);
                if (bytes > 0)
                {
                    builder.Append(Encoding.UTF8.GetString(data, 0, bytes));
                    list.Add(builder.ToString());
                    // 主线程更新UI
                    Device.BeginInvokeOnMainThread(() =>
                    {
                        DataVlazhnost.ItemsSource = null;
                        DataVlazhnost.ItemsSource = list;
                    });
                }
            }
            catch (Exception ex)
            {
                Device.BeginInvokeOnMainThread(() =>
                {
                    Toast.MakeText(Android.App.Application.Context, $"数据读取失败:{ex.Message}", ToastLength.Short).Show();
                });
            }
            return TimerEnable;
        }

        private void OpenDoor_Clicked(object sender, EventArgs e)
        {
            if (door)
            {
                Toast.MakeText(Android.App.Application.Context, "Открытие двери", ToastLength.Short).Show();
                OpenDoor.Text = "Закрыть";
                door = false;
            }
            else
            {
                Toast.MakeText(Android.App.Application.Context, "Закрытие двери", ToastLength.Short).Show();
                OpenDoor.Text = "Открыть";
                door = true;
            }
        }

        private void Water_Clicked(object sender, EventArgs e)
        {
            Toast.MakeText(Android.App.Application.Context, "Подача воды", ToastLength.Short).Show();
        }

        protected override void OnDisappearing()
        {
            base.OnDisappearing();
            // 释放资源
            TimerEnable = false;
            stream?.Close();
            client?.Close();
            server?.Stop();
        }
    }
}

额外注意事项

  • ESP8266连接时需填写安卓设备当前实际的局域网IP,可在手机WiFi设置的已连接网络详情中查看,确保ESP8266和手机连接同一个WiFi局域网。
  • 列表数据建议改用ObservableCollection<string>代替List<string>,新增数据时会自动触发UI更新,无需每次重置ItemsSource。
  • 后续如果需要接入多个传感器设备,需为每个连接的客户端开启独立的后台读写任务,不要使用单客户端逻辑。

内容的提问来源于stack exchange,提问作者user18289280

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 22:15:48