一、PingUDP端口探究
UDP(User Datagram Protocol 用戶數據報協議)是不可靠的傳輸層協議,它不保證數據傳輸的可靠性和有序性,但具有較小的數據包頭開銷、高效率、快速傳輸等特點。而PingUDP利用UDP傳輸ICMP報文實現了類似Ping的功能,但是在使用中也會出現交互性問題,其中一個常見問題就是端口不正確。
在PingUDP發送ICMP報文時,需要指定目的端口,否則將無法接收到響應,因此我們需要明確PingUDP所使用的默認端口號,或者自行指定端口號來保證正確的通信。
using System.Net.Sockets;
public class PingUDPClient
{
private readonly UdpClient _udpClient;
public PingUDPClient(int port = 0)
{
_udpClient = new UdpClient(port);
}
// ...
}
二、PingUDP端口號的選擇
在實際使用PingUDP時,我們可能需要根據具體的場景自行選擇合適的端口號,以避免端口衝突或其他問題。但並不是所有的端口號都適用於PingUDP。
首先,需要考慮到端口號的非系統預留性,例如1024以下的端口號通常都被系統佔用,而且應用程序必須擁有管理員權限才能夠使用。其次,還應該注意到安全性問題,使用常見的端口號(如80, 443, 21等)可能會被惡意軟件攻擊,因此需要使用不易被掃描的、較為隨機的端口號。
using System.Net.Sockets;
public class PingUDPClient
{
private readonly UdpClient _udpClient;
public PingUDPClient(int port = 0)
{
// 可以使用一個隨機的端口號
if (port == 0)
{
var random = new Random();
byte[] buffer = new byte[2];
random.NextBytes(buffer);
port = (buffer[0] << 8) + buffer[1];
}
_udpClient = new UdpClient(port);
}
// ...
}
三、PingUDP的包頭和數據
UDP報文不僅僅包含了數據的內容,還包含了報文的首部。在PingUDP中,報文的首部包含了類型、代碼、校驗和等信息,且與Ping不同的是,PingUDP將這些信息放在了數據部分。
具體而言,PingUDP的報文結構如下:
0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Type | Code | Checksum | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Identifier | Sequence Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data (200 Bytes Max) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
其中,Type佔一個位元組,表示消息類型。Code也佔一個位元組,表示消息的子類型。Checksum佔用兩個位元組,表示整個報文的校驗和。Identifier佔用兩個位元組,Sequence Number佔用兩個位元組,都是為了確定每個PingUDP包的唯一性。而Data最多只有200位元組,用於傳遞不同的測試數據。
using System.Net.Sockets;
public class PingUDPClient
{
private readonly UdpClient _udpClient;
public PingUDPClient(int port = 0)
{
// ...
}
public byte[] SendPing(string hostNameOrAddress, int timeout = 5000)
{
IPAddress ipAddress;
try
{
ipAddress = Dns.GetHostAddresses(hostNameOrAddress)[0];
}
catch (Exception ex)
{
throw new Exception("解析主機名或IP地址失敗:", ex);
}
var pingData = new byte[200];
// 填充數據部分
new Random().NextBytes(pingData);
// 填充頭部
pingData[0] = 8; // Echo Request
pingData[1] = 0; // Subcode
var checksum = Checksum(pingData);
pingData[2] = (byte)(checksum >> 8); // 高位位元組
pingData[3] = (byte)checksum; // 低位位元組
// ...
return null;
}
private ushort Checksum(byte[] buffer)
{
// ...
}
}
四、PingUDP的超時處理方式
PingUDP的超時處理方式與Ping類似,都需要設置一個等待時間。但是PingUDP要比Ping更為靈活,因為它可以通過網絡中的路由器設備分散流量,從而避免網絡擁塞和丟包問題。在PingUDP中,我們可以通過設置TTL(Time To Live)和Don’t Fragment(DF)來控制流量。
具體而言,TTL表示生存時間,每經過一個路由器,TTL值就會減1,當TTL值為0時,路由器會把報文丟棄。Don’t Fragment表示不要分片,如果報文長度超出了所經過的某個路由器的MTU(Maximum Transmission Unit),該路由器會將報文拆分成幾個小片進行傳輸,但是設置了該標誌後,路由器就會丟棄該報文。
using System.Net.Sockets;
public class PingUDPClient
{
// ...
public byte[] SendPing(string hostNameOrAddress, int timeout = 5000)
{
// ...
_udpClient.Send(pingData, pingData.Length, new IPEndPoint(ipAddress, 0));
var startTime = Environment.TickCount;
var receivedData = _udpClient.Receive(ref ipAddress, ref port);
var roundTripTime = Environment.TickCount - startTime;
// ...
return null;
}
}
原創文章,作者:YATP,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/138381.html
微信掃一掃
支付寶掃一掃