C# 網(wǎng)絡(luò)編程之UDP
一、概述
UDP和TCP是網(wǎng)絡(luò)通訊常用的兩個傳輸協(xié)議,C#一般可以通過Socket來實現(xiàn)UDP和TCP通訊,由于.NET框架通過UdpClient、TcpListener 、TcpClient這幾個類對Socket進行了封裝,使其使用更加方便, 本文就通過這幾個封裝過的類講解一下相關(guān)應用。
二、UDP基本應用
與TCP通信不同,UDP通信是不分服務端和客戶端的,通信雙方是對等的。為了描述方便,我們把通信雙方稱為發(fā)送方和接收方。
發(fā)送方:
首先創(chuàng)建一個UDP對象:
string locateIP = "127.0.0.1"; //本機IP int locatePort = 9001; //發(fā)送端口 IPAddress locateIpAddr = IPAddress.Parse(locateIP); IPEndPoint locatePoint = new IPEndPoint(locateIpAddr, locatePort); UdpClient udpClient = new UdpClient(locatePoint);
發(fā)送數(shù)據(jù):
string remoteIP = "127.0.0.1"; //目標機器IP int remotePort = 9002; //接收端口 IPAddress remoteIpAddr = IPAddress.Parse(remoteIP); IPEndPoint remotePoint = new IPEndPoint(remoteIpAddr, remotePort); byte[] buffer = Encoding.UTF8.GetBytes(“hello”); udpClient.Send(buffer, buffer.Length, remotePoint);
以上就完成了一個發(fā)送任務,一個較完整的發(fā)送代碼如下:
public partial class FormServer : Form { private UdpClient udpClient = null; private void btnConnect_Click(object sender, EventArgs e) { string locateIP = "127.0.0.1"; int locatePort = 9001; IPAddress locateIpAddr = IPAddress.Parse(locateIP); IPEndPoint locatePoint = new IPEndPoint(locateIpAddr, locatePort); udpClient = new UdpClient(locatePoint); this.groupWork.Enabled = true; } private void Send_Click(object sender, EventArgs e) { string text = this.txtSend.Text.Trim(); string remoteIP = "127.0.0.1"; int remotePort = 9002; byte[] buffer = Encoding.UTF8.GetBytes(text); if (udpClient != null) { IPAddress remoteIp = IPAddress.Parse(remoteIP); IPEndPoint remotePoint = new IPEndPoint(remoteIp, remotePort); udpClient.Send(buffer, buffer.Length, remotePoint); } Debug.WriteLine("Send OK"); } }
接收端:
首先創(chuàng)建一個UDP對象:
string locateIP = "127.0.0.1"; int locatePort = 9002; IPAddress locateIpAddr = IPAddress.Parse(locateIP); IPEndPoint locatePoint = new IPEndPoint(locateIpAddr, locatePort); UdpClient udpClient = new UdpClient(locatePoint);
接收數(shù)據(jù):
IPEndPoint remotePoint = new IPEndPoint(IPAddress.Parse("1.1.1.1"), 1); var received = udpClient.Receive(ref remotePoint); string info = Encoding.UTF8.GetString(received); string from=$” {remotePoint.Address}:{remotePoint.Port}”;
注意兩點:
1、remotePoint是獲得發(fā)送方的IP信息,定義時可以輸入任何合法的IP和端口信息;
2、Receive方法是阻塞方法,所以需要在新的線程內(nèi)運行,程序會一直等待接收數(shù)據(jù),當接收到一包數(shù)據(jù)時程序就返回,要持續(xù)接收數(shù)據(jù)需要重復調(diào)用Receive方法。
一個較完整的接收端代碼如下:
public partial class FormClent : Form { private UdpClient udpClient = null; private void btnConnect_Click(object sender, EventArgs e) { string locateIP = "127.0.0.1"; int locatePort = 9002; IPAddress locateIpAddr = IPAddress.Parse(locateIP); IPEndPoint locatePoint = new IPEndPoint(locateIpAddr, locatePort); udpClient = new UdpClient(locatePoint); 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}”; } } }); } }
三、丟包和亂序問題
當發(fā)送端發(fā)送一包數(shù)據(jù)時,不管對方是否接收都是發(fā)送成功的,UDP協(xié)議本身并不會對發(fā)送的可靠性進行驗證。(這里的可靠性是指是否接收到,如果對方接收到數(shù)據(jù)包,其內(nèi)容還是可靠的,這個在鏈路層進行了保證。)同時,由于網(wǎng)絡(luò)延時等因素,先發(fā)送的包并不能確定先被接收到,所以由于這兩個原因,UDP通信存在丟包和亂序的情況。
某些業(yè)務場景下,比如實時狀態(tài)監(jiān)控,可能對丟包和亂序情況并不敏感, 可以不用處理,但大部分情況下還是介意丟包的,簡單的處理辦法就是把包的頭部固定長度的空間拿出來存放核對信息,比如包編號,如果有缺失,可以要求發(fā)送方重發(fā),也可以進行排序。
四、將數(shù)據(jù)接收包裝為事件
我們對UdpClent又進行一次封裝,啟用一個線程進行接收數(shù)據(jù),將接收到的數(shù)據(jù)包通過事件發(fā)布出來,這樣使用起來就更方便了。
namespace Communication.UDPClient { public class UdpStateEventArgs : EventArgs { public IPEndPoint remoteEndPoint; public byte[] buffer = null; } public delegate void UDPReceivedEventHandler(UdpStateEventArgs args); public class UDPClient { private UdpClient udpClient; public event UDPReceivedEventHandler UDPMessageReceived; public UDPClient(string locateIP, int locatePort) { IPAddress locateIp = IPAddress.Parse(locateIP); IPEndPoint locatePoint = new IPEndPoint(locateIp, locatePort); udpClient = new UdpClient(locatePoint); //監(jiān)聽創(chuàng)建好后,創(chuàng)建一個線程,開始接收信息 Task.Run(() => { while (true) { UdpStateEventArgs udpReceiveState = new UdpStateEventArgs(); if (udpClient != null) { IPEndPoint remotePoint = new IPEndPoint(IPAddress.Parse("1.1.1.1"), 1); var received = udpClient.Receive(ref remotePoint); udpReceiveState.remoteEndPoint = remotePoint; udpReceiveState.buffer = received; UDPMessageReceived?.Invoke(udpReceiveState); } else { break; } } }); } } }
具體使用辦法:
private void btnConnect_Click(object sender, EventArgs e) { string locateIP = "127.0.0.1"; int locatePort = 9002; UDPClient udpClient = new UDPClient(locateIP, locatePort); udpClient.UDPMessageReceived += UdpClient_UDPMessageReceived; } private void UdpClient_UDPMessageReceived(UdpStateEventArgs args) { var remotePoint = args.remoteEndPoint; string info = Encoding.UTF8.GetString(args.buffer); }
傳送門:
C#網(wǎng)絡(luò)編程入門系列包括三篇文章:
以上就是C# 網(wǎng)絡(luò)編程之UDP的詳細內(nèi)容,更多關(guān)于C# 網(wǎng)絡(luò)編程UDP的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#開發(fā)微信門戶及應用(3) 文本消息和圖文消息應答
這篇文章主要為大家詳細介紹了C#開發(fā)微信門戶及應用第二篇,微信文本消息和圖文消息的應答,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06Unity3D實現(xiàn)物體旋轉(zhuǎn)縮放移動效果
這篇文章主要為大家詳細介紹了Unity3D實現(xiàn)物體旋轉(zhuǎn)縮放移動效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-02-02