1
0
mirror of https://github.com/lidgren/lidgren-network-gen3.git synced 2026-05-15 22:56:30 +09:00

Crude Path MTU detection added

This commit is contained in:
lidgren
2010-12-12 19:07:55 +00:00
parent 55cdf75c5f
commit 772d80835b
12 changed files with 336 additions and 10 deletions

View File

@@ -62,6 +62,7 @@
<Compile Include="NetDeliveryMethod.cs" />
<Compile Include="NetEncryption.cs" />
<Compile Include="NetException.cs" />
<Compile Include="NetConnection.MTU.cs" />
<Compile Include="NetFragmentationHelper.cs" />
<Compile Include="NetIncomingMessage.cs" />
<Compile Include="NetIncomingMessage.Peek.cs" />

View File

@@ -37,6 +37,8 @@ namespace Lidgren.Network
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
bool connectionReset;
m_peer.SendPacket(len, m_remoteEndpoint, 1, out connectionReset);
m_statistics.PacketSent(len, 1);
}
internal void SendPong(int pingNumber)
@@ -50,6 +52,8 @@ namespace Lidgren.Network
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
bool connectionReset;
m_peer.SendPacket(len, m_remoteEndpoint, 1, out connectionReset);
m_statistics.PacketSent(len, 1);
}
internal void ReceivedPong(float now, int pongNumber)

View File

@@ -0,0 +1,176 @@
using System;
namespace Lidgren.Network
{
public partial class NetConnection
{
private enum ExpandMTUStatus
{
None,
InProgress,
Finished
}
private const int c_protocolMaxMTU = (int)((((float)ushort.MaxValue / 8.0f) - 1.0f));
private ExpandMTUStatus m_expandMTUStatus;
private int m_largestSuccessfulMTU;
private int m_smallestFailedMTU;
private int m_lastSentMTUAttemptSize;
private double m_lastSentMTUAttemptTime;
private int m_mtuAttemptFails;
internal int m_currentMTU;
internal void InitExpandMTU(double now)
{
m_lastSentMTUAttemptTime = now + m_peerConfiguration.m_expandMTUFrequency + 1.0f; // wait a tiny bit before starting to expand mtu
m_largestSuccessfulMTU = 512;
m_smallestFailedMTU = -1;
m_currentMTU = m_peerConfiguration.MaximumTransmissionUnit;
}
private void MTUExpansionHeartbeat(double now)
{
if (m_expandMTUStatus == ExpandMTUStatus.Finished)
return;
if (m_expandMTUStatus == ExpandMTUStatus.None)
{
if (m_peerConfiguration.m_autoExpandMTU == false)
{
FinalizeMTU(m_currentMTU);
return;
}
// begin expansion
ExpandMTU(now, true);
return;
}
if (now > m_lastSentMTUAttemptTime + m_peerConfiguration.ExpandMTUFrequency)
{
m_mtuAttemptFails++;
if (m_mtuAttemptFails == 3)
{
FinalizeMTU(m_currentMTU);
return;
}
// timed out; ie. failed
m_smallestFailedMTU = m_lastSentMTUAttemptSize;
ExpandMTU(now, false);
}
}
private void ExpandMTU(double now, bool succeeded)
{
int tryMTU;
// we've nevered encountered failure
if (m_smallestFailedMTU == -1)
{
// we've never encountered failure; expand by 25% each time
tryMTU = (int)((float)m_currentMTU * 1.25f);
//m_peer.LogDebug("Trying MTU " + tryMTU);
}
else
{
// we HAVE encountered failure; so try in between
tryMTU = (int)(((float)m_smallestFailedMTU + (float)m_largestSuccessfulMTU) / 2.0f);
//m_peer.LogDebug("Trying MTU " + m_smallestFailedMTU + " <-> " + m_largestSuccessfulMTU + " = " + tryMTU);
}
if (tryMTU > c_protocolMaxMTU)
tryMTU = c_protocolMaxMTU;
if (tryMTU == m_largestSuccessfulMTU)
{
//m_peer.LogDebug("Found optimal MTU - exiting");
FinalizeMTU(m_largestSuccessfulMTU);
return;
}
SendExpandMTU(now, tryMTU);
}
private void SendExpandMTU(double now, int size)
{
NetOutgoingMessage om = m_peer.CreateMessage(size);
byte[] tmp = new byte[size];
om.Write(tmp);
om.m_messageType = NetMessageType.ExpandMTURequest;
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
bool ok = m_peer.SendMTUPacket(len, m_remoteEndpoint);
if (ok == false)
{
//m_peer.LogDebug("Send MTU failed for size " + size);
// failure
if (m_smallestFailedMTU == -1 || size < m_smallestFailedMTU)
{
m_smallestFailedMTU = size;
m_mtuAttemptFails++;
if (m_mtuAttemptFails >= m_peerConfiguration.ExpandMTUFailAttempts)
{
FinalizeMTU(m_largestSuccessfulMTU);
return;
}
}
ExpandMTU(now, false);
return;
}
m_lastSentMTUAttemptSize = size;
m_lastSentMTUAttemptTime = now;
//m_peer.LogDebug("Requesting MTU expand " + size + " bytes");
m_statistics.PacketSent(len, 1);
}
private void FinalizeMTU(int size)
{
if (m_expandMTUStatus == ExpandMTUStatus.Finished)
return;
m_expandMTUStatus = ExpandMTUStatus.Finished;
m_currentMTU = size;
m_peer.LogVerbose("Maximum Transmission Unit set to: " + m_currentMTU + " bytes");
return;
}
private void SendMTUSuccess(int size)
{
NetOutgoingMessage om = m_peer.CreateMessage(1);
om.Write(size);
om.m_messageType = NetMessageType.ExpandMTUSuccess;
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
bool connectionReset;
m_peer.SendPacket(len, m_remoteEndpoint, 1, out connectionReset);
// m_peer.LogDebug("Received MTU expand request for " + size + " bytes");
m_statistics.PacketSent(len, 1);
}
private void HandleExpandMTUSuccess(double now, int size)
{
if (m_largestSuccessfulMTU < size)
m_largestSuccessfulMTU = size;
if (size < m_currentMTU)
{
//m_peer.LogDebug("Received low MTU expand success (size " + size + "); current mtu is " + m_currentMTU);
return;
}
//m_peer.LogDebug("Expanding MTU to " + size);
m_currentMTU = size;
m_largestSuccessfulMTU = size;
ExpandMTU(now, true);
}
}
}

View File

@@ -81,6 +81,7 @@ namespace Lidgren.Network
m_queuedAcks = new NetQueue<NetTuple<NetMessageType, int>>(4);
m_statistics = new NetConnectionStatistics(this);
m_averageRoundtripTime = -1.0f;
m_currentMTU = m_peerConfiguration.MaximumTransmissionUnit;
}
internal void SetStatus(NetConnectionStatus status, string reason)
@@ -139,6 +140,9 @@ namespace Lidgren.Network
SendPing();
}
// handle expand mtu
MTUExpansionHeartbeat(now);
if (m_disconnectRequested)
{
ExecuteDisconnect(m_disconnectMessage, true);
@@ -153,7 +157,7 @@ namespace Lidgren.Network
//
byte[] sendBuffer = m_peer.m_sendBuffer;
int mtu = m_peerConfiguration.m_maximumTransmissionUnit;
int mtu = m_currentMTU;
if ((frameCounter % 3) == 0) // coalesce a few frames
{
@@ -236,10 +240,10 @@ namespace Lidgren.Network
m_peer.VerifyNetworkThread();
int sz = om.GetEncodedSize();
if (sz > m_peerConfiguration.m_maximumTransmissionUnit)
if (sz > m_currentMTU)
m_peer.LogWarning("Message larger than MTU! Fragmentation must have failed!");
if (m_sendBufferWritePtr + sz > m_peerConfiguration.m_maximumTransmissionUnit)
if (m_sendBufferWritePtr + sz > m_currentMTU)
{
bool connReset; // TODO: handle connection reset
NetException.Assert(m_sendBufferWritePtr > 0 && m_sendBufferNumMessages > 0); // or else the message should have been fragmented earlier
@@ -279,7 +283,7 @@ namespace Lidgren.Network
if (chan == null)
chan = CreateSenderChannel(tp);
if (msg.GetEncodedSize() > m_peerConfiguration.m_maximumTransmissionUnit)
if (msg.GetEncodedSize() > m_currentMTU)
throw new NetException("Message too large! Fragmentation failure?");
return chan.Enqueue(msg);
@@ -351,6 +355,14 @@ namespace Lidgren.Network
int pongNr = m_peer.m_receiveBuffer[ptr++];
ReceivedPong(now, pongNr);
break;
case NetMessageType.ExpandMTURequest:
SendMTUSuccess(payloadLength);
break;
case NetMessageType.ExpandMTUSuccess:
NetIncomingMessage emsg = m_peer.SetupReadHelperMessage(ptr, payloadLength);
int size = emsg.ReadInt32();
HandleExpandMTUSuccess(now, size);
break;
default:
m_peer.LogWarning("Connection received unhandled library message: " + tp);
break;

View File

@@ -169,5 +169,7 @@ namespace Lidgren.Network
DiscoveryResponse = 137,
NatPunchMessage = 138, // send between peers
NatIntroduction = 139, // send to master server
ExpandMTURequest = 140,
ExpandMTUSuccess = 141,
}
}

View File

@@ -33,8 +33,9 @@ namespace Lidgren.Network
// create fragmentation specifics
int totalBytes = msg.LengthBytes;
int mtu = m_configuration.MaximumTransmissionUnit;
// determine minimum mtu for all recipients
int mtu = GetMTU(recipients);
int bytesPerChunk = NetFragmentationHelper.GetBestChunkSize(group, totalBytes, mtu);
int numChunks = totalBytes / bytesPerChunk;

View File

@@ -43,6 +43,9 @@ namespace Lidgren.Network
{
NetException.Assert(msg.m_incomingMessageType != NetIncomingMessageType.Error);
if (msg.MessageType == NetIncomingMessageType.UnconnectedData)
Console.WriteLine("x");
if (msg.m_isFragment)
{
HandleReleasedFragment(msg);
@@ -465,6 +468,7 @@ namespace Lidgren.Network
internal void AcceptConnection(NetConnection conn)
{
// LogDebug("Accepted connection " + conn);
conn.InitExpandMTU(NetTime.Now);
if (m_handshakes.Remove(conn.m_remoteEndpoint) == false)
LogWarning("AcceptConnection called but m_handshakes did not contain it!");

View File

@@ -121,6 +121,8 @@ namespace Lidgren.Network
int bytesSent = m_socket.SendTo(data, 0, numBytes, SocketFlags.None, target);
if (numBytes != bytesSent)
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
// LogDebug("Sent " + numBytes + " bytes");
}
catch (SocketException sx)
{
@@ -150,7 +152,76 @@ namespace Lidgren.Network
return true;
}
internal bool SendMTUPacket(int numBytes, IPEndPoint target)
{
try
{
m_socket.DontFragment = true;
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
if (numBytes != bytesSent)
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
m_statistics.PacketSent(numBytes, 1);
}
catch (SocketException sx)
{
if (sx.SocketErrorCode == SocketError.MessageSize)
return false;
if (sx.SocketErrorCode == SocketError.WouldBlock)
{
// send buffer full?
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
return true;
}
if (sx.SocketErrorCode == SocketError.ConnectionReset)
return true;
LogError("Failed to send packet: (" + sx.SocketErrorCode + ") " + sx);
}
catch (Exception ex)
{
LogError("Failed to send packet: " + ex);
}
finally
{
m_socket.DontFragment = false;
}
return true;
}
#else
internal bool SendMTUPacket(int numBytes, IPEndPoint target)
{
try
{
m_socket.DontFragment = true;
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
if (numBytes != bytesSent)
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
}
catch (SocketException sx)
{
if (sx.SocketErrorCode == SocketError.MessageSize)
return false;
if (sx.SocketErrorCode == SocketError.WouldBlock)
{
// send buffer full?
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
return true;
}
if (sx.SocketErrorCode == SocketError.ConnectionReset)
return true;
LogError("Failed to send packet: (" + sx.SocketErrorCode + ") " + sx);
}
catch (Exception ex)
{
LogError("Failed to send packet: " + ex);
}
finally
{
m_socket.DontFragment = false;
}
return true;
}
//
// Release - just send the packet straight away
//

View File

@@ -44,7 +44,7 @@ namespace Lidgren.Network
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
int len = msg.LengthBytes;
if (len <= m_configuration.MaximumTransmissionUnit)
if (len <= recipient.m_currentMTU)
{
Interlocked.Increment(ref msg.m_recyclingCount);
return recipient.EnqueueMessage(msg, method, sequenceChannel);
@@ -57,6 +57,18 @@ namespace Lidgren.Network
}
}
internal int GetMTU(IList<NetConnection> recipients)
{
int mtu = int.MaxValue;
foreach (NetConnection conn in recipients)
{
int cmtu = conn.m_currentMTU;
if (cmtu < mtu)
mtu = cmtu;
}
return mtu;
}
public void SendMessage(NetOutgoingMessage msg, IList<NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
{
if (msg == null)
@@ -68,6 +80,8 @@ namespace Lidgren.Network
if (msg.m_isSent)
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
int mtu = GetMTU(recipients);
int len = msg.LengthBytes;
if (len <= m_configuration.MaximumTransmissionUnit)
{

View File

@@ -35,7 +35,6 @@ namespace Lidgren.Network
private IPAddress m_localAddress;
internal bool m_acceptIncomingConnections;
internal int m_maximumConnections;
internal int m_maximumTransmissionUnit;
internal int m_defaultOutgoingMessageCapacity;
internal float m_pingInterval;
internal bool m_useMessageRecycling;
@@ -52,6 +51,13 @@ namespace Lidgren.Network
internal float m_minimumOneWayLatency;
internal float m_randomOneWayLatency;
// MTU
internal int m_maximumTransmissionUnit;
internal bool m_autoExpandMTU;
internal float m_expandMTUFrequency;
internal float m_expandMTUFactor;
internal int m_expandMTUFailAttempts;
public NetPeerConfiguration(string appIdentifier)
{
if (string.IsNullOrEmpty(appIdentifier))
@@ -83,6 +89,9 @@ namespace Lidgren.Network
// Total 1408 bytes
// Note that lidgren headers (5 bytes) are not included here; since it's part of the "mtu payload"
m_maximumTransmissionUnit = 1408;
m_autoExpandMTU = true;
m_expandMTUFrequency = 2.0f;
m_expandMTUFailAttempts = 5;
m_loss = 0.0f;
m_minimumOneWayLatency = 0.0f;
@@ -293,6 +302,38 @@ namespace Lidgren.Network
set { m_acceptIncomingConnections = value; }
}
/// <summary>
/// Gets or sets if the NetPeer should send large messages to try to expand the maximum transmission unit size
/// </summary>
public bool AutoExpandMTU
{
get { return m_autoExpandMTU; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_autoExpandMTU = value;
}
}
/// <summary>
/// Gets or sets how often to send large messages to expand MTU if AutoExpandMTU is enabled
/// </summary>
public float ExpandMTUFrequency
{
get { return m_expandMTUFrequency; }
set { m_expandMTUFrequency = value; }
}
/// <summary>
/// Gets or sets the number of failed expand mtu attempts to perform before setting final MTU
/// </summary>
public int ExpandMTUFailAttempts
{
get { return m_expandMTUFailAttempts; }
set { m_expandMTUFailAttempts = value; }
}
#if DEBUG
/// <summary>
/// Gets or sets the simulated amount of sent packets lost from 0.0f to 1.0f

View File

@@ -69,7 +69,7 @@ namespace ChatClient
Output(chat);
break;
default:
Output("Unhandled type: " + im.MessageType);
Output("Unhandled type: " + im.MessageType + " " + im.LengthBytes + " bytes");
break;
}
}

View File

@@ -77,7 +77,7 @@ namespace ChatServer
s_server.SendMessage(om, all, NetDeliveryMethod.ReliableOrdered, 0);
break;
default:
Output("Unhandled type: " + im.MessageType);
Output("Unhandled type: " + im.MessageType + " " + im.LengthBytes + " bytes " + im.DeliveryMethod + "|" + im.SequenceChannel);
break;
}
}