概述:
UDP(User Datagram Protocol)是一种无连接的、轻量级的传输协议。与TCP不同,UDP无需建立连接,就可以收发数据,因此UDP不保证数据的可靠传输,会出现丢包和乱序风险,但是其传输效率比较高,因此根据项目需要进行合理选择。UDP通讯的核心是UdpClient类,它封装了UDP协议的底层操作,提供了简单易用的接口。
使用
- 发送端例子
public class MainViewModel : Stylet.Screen
{
private UdpClient _udpClient;
public MainViewModel()
{
// 注册5888端口,监听本地ip上的5888端口消息。
// 如果不指定端口,系统会随机分配端口,每次运行程序都是不一样的端口,会很麻烦。
// 发送方指定地址,一是为了让远端可以获取到固定的地址,二是为了接收远端发送的消息
string localIp = "127.0.0.1";
int localPort = 5888;
IPAddress localAddress = IPAddress.Parse(localIp);
IPEndPoint localPoint = new IPEndPoint(localAddress, localPort);
_udpClient = new UdpClient(localPoint);
}
public void BtnSendClick(string str)
{
// 向指定的远端发送消息
string remoteIPStr = "127.0.0.1";
int remotePort = 5999;
IPAddress ipAddress = IPAddress.Parse(remoteIPStr);
IPEndPoint remotePoint = new IPEndPoint(ipAddress, remotePort);
byte[] buffer = Encoding.UTF8.GetBytes("hello");
_udpClient.Send(buffer, buffer.Length, remotePoint);
Debug.WriteLine("已发送消息");
}
}
- 接收端例子
public partial class MainWindow : Window
{
private UdpClient _udpClient;
public MainWindow()
{
InitializeComponent();
// 注册5999端口,监听本地ip上的5999端口消息
string localIp = "127.0.0.1";
int localPort = 5999;
IPAddress localAddress = IPAddress.Parse(localIp);
IPEndPoint localPoint = new IPEndPoint(localAddress, localPort);
_udpClient = new UdpClient(localPoint);
// 准备接收远端的point对象,先随便填入值,因为Receive需要传入一个ref修饰的IPEndPoint来接受远端point
IPEndPoint remotePoint = new IPEndPoint(IPAddress.Parse("1.1.1.1"), 1);
Task.Run(() =>
{
while (true)
{
if (_udpClient != null)
{
var received = _udpClient.Receive(ref remotePoint);
string info = Encoding.UTF8.GetString(received);
string from = $"{remotePoint.Address}:{remotePoint.Port}";
Debug.WriteLine(info);//hello
Debug.WriteLine(from);// 127.0.0.1:5888
}
}
});
}
}
注意点:
-
端口和ip地址:
由于之前先接触的是TCP,所以刚开始对这个产生了一时疑惑。在tcp中,是存在服务端(TcpListener)和客户端(TcpClient )概念的,服务端通过tcpListener = new TcpListener(IPAddress.Parse(ipAddress), port)开启设置要监听的网口地址和端口,而客户端通过tcpClient.Connect(ipAddress, port)开启对远程地址和端口的连接,一旦建立连接后,服务端会通过TcpClient client = tcpListener.AcceptTcpClient()监听到这个客户端对象,因此双方就都可以通过client的网络流NetworkStream对象进行收发数据,也都无需知道客户端的ip地址和端口。但是UDP是无连接的,因此不管接收方还是发送方都需要_udpClient = new UdpClient(localPoint)来指定自己要监听的地址和端口,而接收方则可以通过var received = _udpClient.Receive(ref remotePoint)拿到发送方的地址和端口。
image.png Receive:
Receive方法是阻塞方法,所以需要在新的线程内运行,程序会一直等待接收数据,当接收到一包数据时程序就返回,要持续接收数据需要重复调用Receive方法。丢包和乱序问题:
简单的处理办法就是把包的头部固定长度的空间拿出来存放核对信息,比如包编号,如果有缺失,可以要求发送方重发,也可以进行排序。
