一、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