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

WPF异步UDP聊天程序双向通信异常问题求助

问题原因分析

你的UDP聊天程序出现"后发送消息的程序仅能发送、接收失效"的核心问题在于接收端复用了被修改的IPEndPoint对象:

  • 类成员endPoint初始化为IPAddress.Any,用于监听所有来源的消息
  • 第一次调用EndReceive时,该对象会被自动填充为发送方的具体端点(IP+随机端口)
  • 后续调用StartListening时,依然复用这个已被修改的endPoint,导致接收端只能监听来自该特定端点的消息,无法接收其他来源的消息

此外,发送逻辑中多余的ReuseAddress设置也可能引发端口冲突风险。

修复方案

1. 重构接收逻辑,每次监听使用新的IPEndPoint

删除类成员endPoint,在每次启动监听时创建新的IPEndPoint(IPAddress.Any, SERVERPORT),并通过BeginReceive的AsyncState参数传递给回调函数。

2. 移除发送逻辑中多余的端口复用设置

发送端无需设置ReuseAddress,系统会自动分配随机端口发送消息。

3. 优化窗口关闭逻辑,避免资源泄漏

关闭程序时先释放UDP客户端和线程资源,再关闭窗口。

修改后的完整代码
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Dispatcher;

namespace NetworkFinal
{
    public enum Type { Client, Server }

    public partial class MainWindow : Window
    {
        private const int SERVERPORT = 9000;
        private Type type = Type.Server;
        private Thread hThread;

        private UdpClient client;
        private IAsyncResult asyncResult;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnSend_Click(object sender, RoutedEventArgs e)
        {
            string ip = IPTextBox.Text;
            string message = SendTextBox.Text;
            byte[] bytesHeader = Encoding.UTF8.GetBytes(type == Type.Server ? "S" : "C");
            byte[] bytesMessage = Encoding.UTF8.GetBytes(message);
            byte[] bytes = new byte[bytesHeader.Length + bytesMessage.Length];
            bytesHeader.CopyTo(bytes, 0);
            bytesMessage.CopyTo(bytes, bytesHeader.Length);

            try
            {
                using (UdpClient sendClient = new UdpClient())
                {
                    IPEndPoint targetEndPoint = new IPEndPoint(IPAddress.Parse(ip), SERVERPORT);
                    sendClient.Send(bytes, bytes.Length, targetEndPoint);

                    PrintChat("나: " + message);
                    SendTextBox.Text = string.Empty;
                }
            }
            catch (Exception ex)
            {
                PrintConsole(ex.Message);
            }
        }

        private void btnConnect_Click(object sender, RoutedEventArgs e)
        {
            hThread = new Thread(ReceiveThreadFunc);
            hThread.Start();
        }

        private void btnChange_Click(object sender, RoutedEventArgs e)
        {
            if (type == Type.Server)
            {
                type = Type.Client;
                TypeLabel.Content = "클라이언트";
            }
            else
            {
                type = Type.Server;
                TypeLabel.Content = "서버";
            }
        }

        private void btnClose_Click(object sender, RoutedEventArgs e)
        {
            // 释放UDP资源
            if (client != null)
            {
                client.Close();
                client = null;
            }
            // 终止线程
            if (hThread != null && hThread.IsAlive)
            {
                hThread.Abort();
            }
            Close();
        }
        
        private void ReceiveThreadFunc()
        {
            try
            {
                if (client != null) throw new Exception("UDP 소켓이 이미 생성되어 있습니다.");

                client = new UdpClient();
                client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                client.ExclusiveAddressUse = false;
                client.Client.Bind(new IPEndPoint(IPAddress.Any, SERVERPORT));

                PrintConsole(string.Format("Binding Port {0}. Start Listening...", SERVERPORT));
                StartListening();
            }
            catch (Exception ex)
            {
                PrintConsole(ex.Message);
            }
        }

        private void StartListening()
        {
            // 每次创建新的端点,监听所有来源
            IPEndPoint listenEndPoint = new IPEndPoint(IPAddress.Any, SERVERPORT);
            asyncResult = client.BeginReceive(Receive, listenEndPoint);
        }

        private void Receive(IAsyncResult ar)
        {
            if (client == null) return;

            try
            {
                // 从AsyncState获取监听端点
                IPEndPoint remoteEndPoint = (IPEndPoint)ar.AsyncState;
                byte[] bytes = client.EndReceive(ar, ref remoteEndPoint);
                string message = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
                if (isValidMessage(message))
                {
                    message = message.Substring(1);
                    PrintChat("상대: " + message);
                }
                // 继续监听下一条消息
                StartListening();
            }
            catch (Exception ex)
            {
                PrintConsole(ex.Message);
            }
        }

        private bool isValidMessage(string message)
        {
            if (type == Type.Server)
            {
                return message[0].Equals('C');
            }
            else
            {
                return message[0].Equals('S');
            }
        }

        private void PrintChat(string message)
        {
            Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate {
                ChatTextBlock.Text += message;
                ChatTextBlock.Text += "\n";
            }));
        }

        private void PrintConsole(string message)
        {
            Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate {
                ConsoleTextBlock.Text += message;
                ConsoleTextBlock.Text += "\n";
            }));
        }
    }
}
验证步骤
  1. 运行两个程序实例,分别设置为服务器和客户端
  2. 点击两个程序的「连接」按钮绑定端口
  3. 先发送消息的程序和后发送的程序均能正常收发消息

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 08:30:57