C# 使用 UDP 实现消息接收和发送详解
本文详解如何在 C# 中通过 Socket 和 UdpClient 两种核心类实现 UDP 消息的接收与发送。内容包含服务端持续监听、客户端数据发送的具体代码逻辑,以及基于 WinForm 的完整界面交互案例。重点解析 IP 地址绑定、多线程处理与字节编码等关键环节,附带调试阶段防火墙设置等常见问题的解决方案。
UDP 通信基础准备
在开始编写代码前,请确保已引入必要的命名空间。在使用 Socket 或 UdpClient 进行网络通信时,通常需要添加以下引用:
using System.Net;
using System.Net.Sockets;
using System.Threading;
若开发环境为 Windows Forms (WinForm),还需确保引用了 System.Windows.Forms 以支持界面组件操作。此外,UDP 是一种无连接、不可靠但高效的传输层协议,适合对实时性要求高且允许少量丢包的数据传输场景(如局域网即时消息、状态广播)。在进行本地调试时,Windows 防火墙可能会拦截入站流量,建议暂时关闭防火墙或手动添加入站规则以保障测试顺畅。
服务端实现:消息接收
服务端的任务是开启一个端口并持续监听来自客户端的消息。以下是两种主流的实现在 C# 中的做法。
第1步:使用 Socket 类实现监听
Socket 提供了更底层的控制能力。通过创建 Dgram 类型的 Socket 并绑定到特定 IP 和端口,可以实现稳定的消息接收。
private static Socket udpServer;
static void startUdpReceive()
{
Console.WriteLine("------startUdpReceive--");
// 创建 UDP Socket
udpServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
// 绑定 IP 和端口。此处绑定了具体 IP "192.168.2.106" 和端口 "10023"
// 若希望服务器监听本机所有网卡,可改为 IPAddress.Any
udpServer.Bind(new IPEndPoint(IPAddress.Parse("192.168.2.106"), 10023));
// 将接收逻辑放入后台线程,避免阻塞主线程
new Thread(ReceiveMessage)
{
IsBackground = true
}.Start();
}
private static void ReceiveMessage()
{
while (true)
{
byte[] data = new byte[1024];
// EndPoint 用于存储发送方的地址信息
EndPoint endPoint = new IPEndPoint(IPAddress.Any, 0);
// 阻塞等待接收数据,ref endPoint 会在接收后填充发送方地址
int count = udpServer.ReceiveFrom(data, ref endPoint);
if (count > 0)
{
string message = Encoding.UTF8.GetString(data, 0, count);
Console.WriteLine(
"-----从ip " + (endPoint as IPEndPoint).Address.ToString()
+ ":" + (endPoint as IPEndPoint).Port + "Get " + message);
}
}
}上述代码中,udpServer.Bind 是核心步骤。如果仅需绑定端口而不限制 IP,可以使用 new IPEndPoint(IPAddress.Any, 10023)。接收循环使用 while(true) 保证服务常驻,并通过 ref endPoint 动态获取消息来源。
第2步:使用 UdpClient 类简化监听
如果你不需要精细控制 Socket 参数,UdpClient 是更简洁的选择。它封装了 Socket 的大部分常用功能。
static UdpClient udpcRecv;
public static void UdpServices()
{
try
{
IPAddress ip = IPAddress.Parse("192.168.2.106");
IPEndPoint remoteIpep = new IPEndPoint(ip, 10023);
// 实例化 UdpClient 并绑定地址
udpcRecv = new UdpClient(remoteIpep);
// 启动独立线程接收消息
Thread thrRecv = new Thread(ReceiveMessage22);
thrRecv.IsBackground = true;
thrRecv.Start();
}
catch (Exception ex)
{
// 在实际项目中建议使用日志记录而非直接弹出 MessageBox
Console.WriteLine("错误: " + ex.Message);
}
}
private static void ReceiveMessage22()
{
IPEndPoint remoteIpep = new IPEndPoint(IPAddress.Any, 0);
while (true)
{
try
{
// 阻塞式接收,返回字节数组
byte[] bytRecv = udpcRecv.Receive(ref remoteIpep);
string message = Encoding.UTF8.GetString(bytRecv, 0, bytRecv.Length);
Console.WriteLine("-----received message: " + message);
}
catch (Exception ex)
{
Console.WriteLine("UDP异常: " + ex.Message);
}
}
}UdpClient.Receive 方法与 Socket.ReceiveFrom 类似,都会根据传入的 IPEndPoint 引用更新为实际发送者的地址,从而实现双向通信识别。
客户端实现:消息发送
发送端相对简单,主要涉及建立连接(非 TCP 意义上的握手)、转换数据格式和发送字节流。
第3步:构建发送逻辑
无论是使用 Socket 还是 UdpClient,发送的核心都是确定目标 IP 和 Port。
UdpClient udpClient = new UdpClient();
try
{
// 连接到服务端地址,后续 Send 调用会自动使用该地址
udpClient.Connect("192.168.2.106", 10023);
// 将字符串转换为 ASCII 或 UTF-8 字节数组
Byte[] sendBytes = Encoding.ASCII.GetBytes("Is anybody there??????");
// 执行发送
udpClient.Send(sendBytes, sendBytes.Length);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}注意:Connect 在 UDP 中并不建立连接,而是为默认的发送目标设置地址,这样每次调用 Send 时无需再次指定目标地址。如果使用多播或不定目标发送,可以跳过 Connect 并在 Send 方法中指定 EndPoint。
综合应用:WinForm 客户端与服务端界面
为了更直观地理解 UDP 通信,下面提供一个完整的 WinForm 应用程序示例。该示例包含一个简单的 GUI,支持用户输入目标 IP、端口及发送内容,并能够显示接收到的消息。
第4步:服务端界面设计与代码
服务端程序负责监听端口并向指定目标发送探测消息(例如用于 NAT 穿透或局域网发现)。界面包含目标 IP、端口文本框及发送按钮。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MyTest.UDP
{
public partial class UDP_Sever : Form
{
IPEndPoint remotePoint;
UdpClient sever = null;
public UDP_Sever()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// 解析用户输入的目标 IP 和端口
IPAddress remoteIP = IPAddress.Parse(textBox1.Text.Trim());
int remotePort = int.Parse(textBox2.Text.Trim());
// 实例化远程端点
remotePoint = new IPEndPoint(remoteIP, remotePort);
// 初始化 UdpClient
sever = new UdpClient();
}
private void button2_Click(object sender, EventArgs e)
{
if (!string.IsNullOrWhiteSpace(textBox3.Text.Trim()))
{
string sendString = textBox3.Text.Trim();
// 使用 Default 编码获取字节数组,可根据需求改用 Encoding.UTF8
byte[] sendData = Encoding.Default.GetBytes(sendString);
// 将数据发送到之前设定的远程端点
sever.Send(sendData, sendData.Length, remotePoint);
// 清空发送框
textBox3.Text = "";
}
}
private void UDP_Sever_FormClosing(object sender, FormClosingEventArgs e)
{
// 窗口关闭时释放资源
if(sever != null) {
sever.Close();
}
}
}
}第5步:客户端界面设计与代码
客户端程序启动后自动绑定端口进入监听状态,并将收到的数据显示在列表框中。需注意跨线程 UI 更新的权限问题。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
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 MyTest.UDP
{
public partial class UDP_Client : Form
{
UdpClient client = null;
IPEndPoint remotePoint;
string receiveString = null;
byte[] receiveData = null;
public UDP_Client()
{
InitializeComponent();
// 禁用.NET对跨线程调用的默认安全检查(简易示例常用,生产环境建议用 Invoke)
CheckForIllegalCrossThreadCalls = false;
}
private void button1_Click(object sender, EventArgs e)
{
// 实例化一个远程端点占位符
// 此处的 IP 和端口仅为占位,真正调用 Receive 时会被替换为发送方的真实地址
remotePoint = new IPEndPoint(IPAddress.Any, 0);
// 绑定当前窗口的端口号(由用户输入或默认设定),开始监听
client = new UdpClient(int.Parse(textBox2.Text.Trim()));
// 启动后台线程专门用于接收数据
Thread thread = new Thread(Revice);
thread.IsBackground = true;
thread.Start();
}
private void Revice()
{
while (true)
{
try {
// 阻塞等待数据
receiveData = client.Receive(ref remotePoint);
receiveString = Encoding.Default.GetString(receiveData);
// 更新 UI 显示:IP:内容
listBox1.Items.Add(remotePoint.Address.ToString()+":"+ receiveString);
} catch(Exception ex) {
// 忽略意外中断
}
}
}
}
}总结与注意事项
C# 中 UDP 开发的难点通常不在于 API 的使用,而在于对 IPEndPoint 的管理和多线程并发处理的把握。在上面的 WinForm 示例中,使用了 CheckForIllegalCrossThreadCalls = false; 来简化跨线程更新 UI 的逻辑,这在小型工具或原型开发中非常高效,但在大型商业应用中推荐使用 Control.Invoke 以确保线程安全。
另外,由于 UDP 是无连接的,服务端收到消息时才能确定客户端的地址,因此在设计支持“回复”功能的系统时,务必保留接收消息时的 IPEndPoint 对象,以便后续回传数据。以上就是 C# 使用 UDP 实现消息接收和发送的详细内容,更多关于 C# 网络编程与 Socket 通信的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









