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

C# WinForms UDP聊天程序Timer异常:进度条卡顿问题排查

UDP简易聊天程序Timer进度条异常问题分析与解决

问题背景

开发基于C# WinForms的UDP聊天程序,规则如下:

  • 收到消息后需在10秒内回复
  • 发送回复后,对方需在10秒内回应
  • 发送方通过OtherPeopleTimer计时,每秒将进度条值减10,超时触发惩罚逻辑
  • 接收方通过OursTimer计时,仅展示进度,超时后停止无其他操作

程序以localhost运行,通过独立线程监听UDP数据包并调用ProcessIncomingData处理消息,但Timer工作异常:第一个exe发送消息后,第二个exe的进度条仅递减一次就卡顿,无法走到0。

现象

右侧窗体进度条每秒正常递减,左侧窗体仅递减一次后停滞。

相关代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        IPAddress ServerIP;
        int ServerPort;
        IPEndPoint ServerEndPoint;
        Socket ServerSocket;

        IPAddress ClientIP;
        int ClientPort;
        IPEndPoint ClientEndPoint;

        Thread Listener;

        private void Form1_Load(object sender, EventArgs e)
        {
            OursTimer.Interval= 1000;
            OtherPeopleTimer.Interval= 1000;
            OursTimer.Enabled = false;
            OtherPeopleTimer.Enabled = false;

            ClientIP = IPAddress.Parse("127.0.0.1");
            ClientPort = 20000;
            ClientEndPoint = new IPEndPoint(ClientIP, ClientPort);


            ServerIP = IPAddress.Parse("127.0.0.1");
            ServerPort = 10000;
            ServerEndPoint = new IPEndPoint(ServerIP, ServerPort);

            ServerSocket = new Socket(SocketType.Dgram, ProtocolType.Udp);

            try
            {
                ServerSocket.Bind(ServerEndPoint);
            }
            catch (Exception)
            {
                ServerPort = 20000;
                ServerEndPoint = new IPEndPoint(ServerIP, ServerPort);
                ServerSocket.Bind(ServerEndPoint);
                ClientPort = 10000;
            }

            Listener = new Thread(Listen);
            Listener.Start();

        }
        public void Listen()
        {
            while (true)
            {
                byte[] buffer = new byte[1024];
                ServerSocket.Receive(buffer);

                string incomingMessage = Encoding.UTF8.GetString(buffer);
                incomingMessage = incomingMessage.Substring(0, incomingMessage.IndexOf("\0"));

                ProcessIncomingData(incomingMessage);
            }
            
        }
        public void ProcessIncomingData(string message)
        {
            OtherPeopleTimer.Stop();
            progressBar1.Value = 100;
            OursTimer.Start();
            MessageBox.Show(message);
        }
        private void button1_Click(object sender, EventArgs e)
        {
            OursTimer.Stop();
            byte[] buffer = Encoding.UTF8.GetBytes(textBox1.Text);
            ServerSocket.SendTo(buffer, ClientEndPoint);
            progressBar1.Value = 100;
            OtherPeopleTimer.Start();
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            Environment.Exit(0);
        }
        private void OursTimer_Tick(object sender, EventArgs e)
        {
            if (progressBar1.Value > 0)
                progressBar1.Value -= 10;
            else
                OursTimer.Stop();
        }

        private void OtherPeople_Tick(object sender, EventArgs e)
        {
            if (progressBar1.Value > 0)
                progressBar1.Value -= 10;
            else
                OtherPeopleTimer.Stop();
        }
    }
}

问题原因分析

  1. 跨线程操作UI控件:Listen线程是独立工作线程,直接调用ProcessIncomingData操作UI控件(progressBar1、OursTimer、OtherPeopleTimer),违反WinForms的UI线程独占规则,会导致UI线程消息队列混乱,Timer的Tick事件无法正常触发。
  2. MessageBox阻塞UI线程:ProcessIncomingData中调用MessageBox.Show(message),会强制关联UI线程并阻塞其执行,导致UI线程无法处理Timer的Tick事件,进度条停止更新。
  3. Timer事件绑定可能错误:代码中OtherPeopleTimer的Tick事件处理方法是OtherPeople_Tick,若设计器中未正确绑定该方法,发送方的Timer将无法正常工作。

解决方案

1. 跨线程安全访问UI控件

所有UI操作必须通过UI线程执行,使用Invoke切换到UI线程:

public void ProcessIncomingData(string message)
{
    if (InvokeRequired)
    {
        Invoke(new Action<string>(ProcessIncomingData), message);
        return;
    }

    OtherPeopleTimer.Stop();
    progressBar1.Value = 100;
    OursTimer.Start();
    // 用Label控件替换MessageBox,避免阻塞UI
    labelReceivedMsg.Text = $"收到消息:{message}";
}

2. 移除阻塞式MessageBox

将弹窗提示改为非阻塞的UI控件展示(如Label),或使用异步方式调用MessageBox,避免中断UI线程的事件处理流程。

3. 确认Timer事件绑定

在Form1_Load中手动绑定Timer事件,确保关联正确:

private void Form1_Load(object sender, EventArgs e)
{
    // 原初始化代码...
    OursTimer.Tick += OursTimer_Tick;
    OtherPeopleTimer.Tick += OtherPeople_Tick;
}

4. 优化UDP接收逻辑

将阻塞式的ServerSocket.Receive改为异步接收,避免工作线程长期阻塞,同时增加异常处理防止线程崩溃:

public async void Listen()
{
    byte[] buffer = new byte[1024];
    while (true)
    {
        try
        {
            var receivedBytes = await ServerSocket.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
            string incomingMessage = Encoding.UTF8.GetString(buffer, 0, receivedBytes);
            ProcessIncomingData(incomingMessage);
        }
        catch (Exception ex)
        {
            Invoke(new Action(() => labelStatus.Text = $"接收错误:{ex.Message}"));
        }
    }
}

内容的提问来源于stack exchange,提问作者Bünyamin Bilen KARATAŞ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 12:45:43