⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pmtudiscovery.cs

📁 rudp可靠保障得udp传输
💻 CS
字号:
//#define CONSOLE_TRACE

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.NetworkInformation;
using System.Diagnostics;
using System.Net;

namespace Helper.Net.RUDP
{
	/// <summary>
	/// An protocol for optimal MTU discovery and monitoring.
	/// </summary>
	internal sealed class PMTUDiscovery
	{
		#region Variables

		private int InterfaceMTU = -1;

		// Test the MTU each 3 minutes
		private const long DefaultProbeInterval = 3 * 60 * 1000 * 1000;

		// Next delayed probe
		private long _probeTS = -1;
		private int _probeMTU = 0;

		// Time out checking
		private long _lastProbeTS = -1;
		private int _mtuProbeResendCount = 0;

		// Min & Max
		static internal int MinMTU = 576;
		static internal int MaxMTU = 16 * 1024;

		// RUDP socket
		private RUDPSocket _rudp;

		#endregion

		#region _pmtuDiscovery

		internal PMTUDiscovery(RUDPSocket rudp)
		{
			_rudp = rudp;

			// Start with minimum MTU to insure correct communication
			_rudp._mtu = MinMTU;
		}

		#endregion

		#region StartTuning

		internal void StartTuning()
		{
			// Start tuning with default MTU
			SendProbe(DiscoverInterfaceMTU());
		}

		#endregion

		#region DiscoverInterfaceMTU

		public int DiscoverInterfaceMTU()
		{
			if (InterfaceMTU > -1)
				return InterfaceMTU;

			int MTU = Int32.MaxValue;

			// Get MTU
			NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
			foreach (NetworkInterface adapter in nics)
			{
				if (!adapter.Supports(NetworkInterfaceComponent.IPv4))
					continue;

				IPInterfaceProperties adapterProperties = adapter.GetIPProperties();
				foreach (UnicastIPAddressInformation addrInfo in adapterProperties.UnicastAddresses)
				{
					if (addrInfo.Address.Equals(((IPEndPoint)_rudp._physical._socket.LocalEndPoint).Address))
					{
						IPv4InterfaceProperties p = adapterProperties.GetIPv4Properties();
						if (p != null)
							MTU = Math.Min(MTU, p.Mtu);
					}
				}
			}

			//MTU -= 8; // UDP Header size

			// IP header is already removed (20 bytes)

			InterfaceMTU = MTU;

			return InterfaceMTU;
		}

		#endregion

		#region SendProbe

		internal void SendProbe(int mtu)
		{
			//---- Avoid congestion
			_rudp._controlWindow.WaitOne();

			Trace("MTU:SendProbe:" + mtu);

			//---- Calculate full packet length
			byte[] buffer = new byte[mtu - RUDPStack.UDPHeaderLength - RUDPStack.RUDPHeaderLength];

			//---- Set the TS
			_lastProbeTS = HiResTimer.MicroSeconds;

			//---- Send a packet
			RUDPStack.PushPacketToSend(_rudp, false, RUDPPacketChannel.MTUTuning, buffer, 0, buffer.Length);
		}

		#endregion

		#region SendDelayedProbe

		internal void SendDelayedProbe(int mtu, long delayMicroSeconds)
		{
			_probeMTU = mtu;
			_probeTS = HiResTimer.MicroSeconds + delayMicroSeconds;
		}

		#endregion

		#region OnReceiveProbe

		internal void OnReceiveProbe(int payloadLength)
		{
			Trace("MTU:OnReceiveProbe:" + payloadLength + RUDPStack.UDPHeaderLength + RUDPStack.RUDPHeaderLength);

			if (_rudp._status != RUDPSocketStatus.Connected)
				return;

			int mtu = payloadLength + RUDPStack.UDPHeaderLength + RUDPStack.RUDPHeaderLength;

			//---- New possible MTU ?
			_rudp._mtu = Math.Max(_rudp._mtu, mtu);

			//---- Avoid congestion
			_rudp._controlWindow.WaitOne();

			//---- Send the ACK
			byte[] buffer = new byte[4];
			BinaryHelper.WriteInt(mtu, buffer, 0);

			// Send the ACK
			RUDPStack.PushPacketToSend(_rudp, false, RUDPPacketChannel.MTUTuningACK, buffer, 0, 4);
		}

		#endregion

		#region OnReceiveProbeACK

		internal void OnReceiveProbeACK(byte[] payload)
		{
			//---- We have a result for the packet
			_lastProbeTS = -1;
			_mtuProbeResendCount = 0;

			//---- Get the MTU of this probe
			int mtu = BinaryHelper.ReadInt(payload, 0);

			Trace("MTU:OnReceiveProbeACK:" + mtu);

			//---- Update MTU

			// Reduce MTU to allow several packets on the window
			if (_rudp._controlWindow._cwnd < mtu)
				mtu = (int)(_rudp._controlWindow._cwnd / 4);

			_rudp._mtu = mtu;

			//---- Continue to probe
			int probeMTU = (int)(_rudp._mtu * 1.25);
			if (probeMTU < MaxMTU)
				SendDelayedProbe(probeMTU, 5000000);
		}

		#endregion

		#region OnICMPError

		internal void OnICMPError(RUDPOutgoingPacket packet)
		{
			int errorMTU = packet.Payload.Length + RUDPStack.UDPHeaderLength + RUDPStack.RUDPHeaderLength;
			int nextMTU = (int)(errorMTU * 0.75);

			Trace("MTU:OnICMPError:" + errorMTU + " -> " + nextMTU);

			//---- We have a result for the packet
			_lastProbeTS = -1;
			_mtuProbeResendCount = 0;

			//---- User packet : URGENT
			if (packet.Channel == RUDPPacketChannel.UserPacket)
			{
				_rudp._mtu = nextMTU;
				SendProbe(_rudp._mtu);
				return;
			}

			//---- MTU probe
			if (packet.Channel == RUDPPacketChannel.MTUTuning)
			{
				if (errorMTU > _rudp._mtu && nextMTU < _rudp._mtu)
					nextMTU = _rudp._mtu;
				SendDelayedProbe(nextMTU, DefaultProbeInterval);
				return;
			}
		}

		#endregion

		#region OnHeartBeat

		internal void OnHeartBeat(long now)
		{
			if (_rudp._status != RUDPSocketStatus.Connected)
				return;

			//---- Delayed send
			if (_probeTS > -1 && now >= _probeTS)
			{
				_probeTS = -1;
				SendProbe(_probeMTU);
			}

			//---- Check for time out
			if (_lastProbeTS > -1 && (now - _lastProbeTS) > _rudp._rto)
			{
				// Too much probe, current MTU is not correct
				if (_mtuProbeResendCount == 3)
				{
					_mtuProbeResendCount = 0;
					SendProbe((int)(_rudp._mtu * 0.75));
					return;
				}

				// Check if the current MTU is still correct
				_mtuProbeResendCount++;
				SendProbe(_rudp._mtu);
			}
		}

		#endregion

		#region Trace

		[Conditional("CONSOLE_TRACE")]
		internal static void Trace(string text)
		{
			Console.WriteLine(text);
		}

		#endregion
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -