WPF客户端连接服务器并调用主窗口方法时崩溃求助
WPF客户端TCP事件触发UI操作崩溃问题
我基于TCP搭建了简易服务器和ClientHandle脚本,服务器启动10秒后会调用AddDevice方法,此时触发OnAddedDevice事件调用WPF主窗口的GoToPage方法时,客户端直接崩溃。我尝试过直接调用方法、重新实例化对象、修改数据包信息等操作,问题仍未解决。
相关代码
ClientHandle类
public class ClientHandle { public static event Action OnAddedDevice; public static void Welcome(Packet packet) { string msg = packet.ReadString(); int myId = packet.ReadInt(); Client.instance.myId = myId; ClientSend.WeclomeReceived(); } public static void AddDevice(Packet packet) { int myId = packet.ReadInt(); OnAddedDevice?.Invoke(); } }
事件订阅代码(NetLogic类)
public NetLogic(MainWindow mainWindow) { _mainWindow = mainWindow; ClientHandle.OnAddedDevice += TestA; StartServer(); Client client = new Client(); client.ConnectToServer(); }
Client类完整实现
public class Client { public static Client instance; public static int dataBufferSize = 4096; public string ip = "127.0.0.1"; public int port = 26950; public int myId = 0; public TCP tcp; private delegate void PacketHandler(Packet _packet); private static Dictionary<int, PacketHandler> packetHandlers; public Client() { instance = this; tcp = new TCP(); } public void ConnectToServer() { InitializeClientData(); tcp.Connect(); } public class TCP { public TcpClient socket; private NetworkStream stream; private Packet receivedData; private byte[] receiveBuffer; public void Connect() { socket = new TcpClient { ReceiveBufferSize = dataBufferSize, SendBufferSize = dataBufferSize }; receiveBuffer = new byte[dataBufferSize]; socket.BeginConnect(instance.ip, instance.port, ConnectCallback, socket); } private void ConnectCallback(IAsyncResult _result) { socket.EndConnect(_result); if (!socket.Connected) { return; } stream = socket.GetStream(); receivedData = new Packet(); stream.BeginRead(receiveBuffer, 0, dataBufferSize, ReceiveCallback, null); } public void SendData(Packet _packet) { try { if (socket != null) { stream.BeginWrite(_packet.ToArray(), 0, _packet.Length(), null, null); } } catch (Exception exception) { Console.WriteLine($"Error sending data to server via TCP: {exception}"); } } private void ReceiveCallback(IAsyncResult _result) { try { int _byteLength = stream.EndRead(_result); if (_byteLength <= 0) { // TODO: disconnect return; } byte[] _data = new byte[_byteLength]; Array.Copy(receiveBuffer, _data, _byteLength); receivedData.Reset(HandleData(_data)); stream.BeginRead(receiveBuffer, 0, dataBufferSize, ReceiveCallback, null); } catch { // TODO: disconnect } } private bool HandleData(byte[] _data) { int _packetLength = 0; receivedData.SetBytes(_data); if (receivedData.UnreadLength() >= 4) { _packetLength = receivedData.ReadInt(); if (_packetLength <= 0) { return true; } } while (_packetLength > 0 && _packetLength <= receivedData.UnreadLength()) { byte[] _packetBytes = receivedData.ReadBytes(_packetLength); ThreadManager.ExecuteOnMainThread(() => { using (Packet _packet = new Packet(_packetBytes)) { int _packetId = _packet.ReadInt(); packetHandlers[_packetId](_packet); } }); _packetLength = 0; if (receivedData.UnreadLength() >= 4) { _packetLength = receivedData.ReadInt(); if (_packetLength <= 0) { return true; } } } if (_packetLength <= 1) { return true; } return false; } } private void InitializeClientData() { packetHandlers = new Dictionary<int, PacketHandler>() { { (int)ServerPackets.welcome, ClientHandle.Welcome }, { (int)ServerPackets.AddDevice, ClientHandle.AddDevice }, }; } }
TestA方法
public void TestA() { var mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault(); mainWindow.GoToPage(); }
问题分析与解决方案
崩溃的核心原因是WPF UI元素只能由创建它们的主线程访问,即使你通过ThreadManager.ExecuteOnMainThread处理数据包,仍可能存在以下问题:
TestA中通过Application.Current.Windows查找主窗口的方式不可靠,可能返回null导致空引用异常- 若
ThreadManager.ExecuteOnMainThread的实现未正确调度到WPF主线程,会导致事件触发时在非UI线程执行UI操作
修复步骤:
直接使用已持有的主窗口实例,避免动态查找的不确定性:
// 在NetLogic类中保留已注入的_mainWindow引用 private readonly MainWindow _mainWindow; public void TestA() { // 明确调度UI操作到主线程 _mainWindow.Dispatcher.Invoke(() => { _mainWindow.GoToPage(); }); }验证ThreadManager的实现,确保它正确使用WPF的Dispatcher调度到主线程:
public static class ThreadManager { public static void ExecuteOnMainThread(Action action) { if (Application.Current == null) { action.Invoke(); return; } // 使用Dispatcher确保在主线程执行 Application.Current.Dispatcher.Invoke(action); } }检查GoToPage方法,确保内部所有UI操作都没有跨线程调用,若有子控件操作,同样需要通过Dispatcher调度。
内容的提问来源于stack exchange,提问作者Vlad Horbatiuk
相关产品推荐
相关产品推荐

