You've already forked lidgren-network-gen3
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:
@@ -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" />
|
||||
|
||||
@@ -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)
|
||||
|
||||
176
Lidgren.Network/NetConnection.MTU.cs
Normal file
176
Lidgren.Network/NetConnection.MTU.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -169,5 +169,7 @@ namespace Lidgren.Network
|
||||
DiscoveryResponse = 137,
|
||||
NatPunchMessage = 138, // send between peers
|
||||
NatIntroduction = 139, // send to master server
|
||||
ExpandMTURequest = 140,
|
||||
ExpandMTUSuccess = 141,
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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!");
|
||||
|
||||
@@ -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
|
||||
//
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace ChatClient
|
||||
Output(chat);
|
||||
break;
|
||||
default:
|
||||
Output("Unhandled type: " + im.MessageType);
|
||||
Output("Unhandled type: " + im.MessageType + " " + im.LengthBytes + " bytes");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user