From 27b3d8e3863c2a37dc6b76a8a7db47e03cc8a4ef Mon Sep 17 00:00:00 2001 From: Frank Versnel Date: Mon, 15 Feb 2016 16:36:12 +0100 Subject: [PATCH] Added improved NAT punch. Clients that request an introduction now first request a confirmation from the server to make sure unconnected messages can be sent in both directions before attempting to make a connection. Similarly the server automatically sends a confirmation to the client to reduce the possiblity that either one of the endpoints has issues with sending messages to the other. --- Lidgren.Network/NetMessageType.cs | 4 +- Lidgren.Network/NetNatIntroduction.cs | 73 ++++++++++++++++++++------- Lidgren.Network/NetPeer.Internal.cs | 14 +++-- 3 files changed, 70 insertions(+), 21 deletions(-) diff --git a/Lidgren.Network/NetMessageType.cs b/Lidgren.Network/NetMessageType.cs index a14e99c..8dd53de 100644 --- a/Lidgren.Network/NetMessageType.cs +++ b/Lidgren.Network/NetMessageType.cs @@ -169,7 +169,9 @@ namespace Lidgren.Network DiscoveryResponse = 137, NatPunchMessage = 138, // send between peers NatIntroduction = 139, // send to master server + NatIntroductionConfirmRequest = 142, + NatIntroductionConfirmed = 143, ExpandMTURequest = 140, ExpandMTUSuccess = 141, } -} \ No newline at end of file +} diff --git a/Lidgren.Network/NetNatIntroduction.cs b/Lidgren.Network/NetNatIntroduction.cs index d134e14..1df7e42 100644 --- a/Lidgren.Network/NetNatIntroduction.cs +++ b/Lidgren.Network/NetNatIntroduction.cs @@ -9,8 +9,10 @@ using NetEndPoint = System.Net.IPEndPoint; namespace Lidgren.Network { - public partial class NetPeer - { + public partial class NetPeer { + private const byte HostByte = 1; + private const byte ClientByte = 0; + /// /// Send NetIntroduction to hostExternal and clientExternal; introducing client to host /// @@ -92,16 +94,61 @@ namespace Lidgren.Network { NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length - byte fromHostByte = tmp.ReadByte(); - if (fromHostByte == 0) + var isFromClient = tmp.ReadByte() == ClientByte; + string token = tmp.ReadString(); + if (isFromClient) { - // it's from client - LogDebug("NAT punch received from " + senderEndPoint + " we're host, so we ignore this"); - return; // don't alert hosts about nat punch successes; only clients + LogDebug("NAT punch received from " + senderEndPoint + " we're host, so we send a NatIntroductionConfirmed message - token is " + token); + + var confirmResponse = CreateMessage(1); + confirmResponse.m_messageType = NetMessageType.NatIntroductionConfirmed; + confirmResponse.Write(HostByte); + confirmResponse.Write(token); + Interlocked.Increment(ref confirmResponse.m_recyclingCount); + m_unsentUnconnectedMessages.Enqueue(new NetTuple(senderEndPoint, confirmResponse)); } + else + { + LogDebug("NAT punch received from " + senderEndPoint + " we're client, so we send a NatIntroductionConfirmRequest - token is " + token); + + var confirmRequest = CreateMessage(1); + confirmRequest.m_messageType = NetMessageType.NatIntroductionConfirmRequest; + confirmRequest.Write(ClientByte); + confirmRequest.Write(token); + Interlocked.Increment(ref confirmRequest.m_recyclingCount); + m_unsentUnconnectedMessages.Enqueue(new NetTuple(senderEndPoint, confirmRequest)); + } + } + + private void HandleNatPunchConfirmRequest(int ptr, NetEndPoint senderEndPoint) + { + NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length + var isFromClient = tmp.ReadByte() == ClientByte; string token = tmp.ReadString(); - LogDebug("NAT punch received from " + senderEndPoint + " we're client, so we've succeeded - token is " + token); + LogDebug("Received NAT punch confirmation from " + senderEndPoint + " sending NatIntroductionConfirmed - token is " + token); + + var confirmResponse = CreateMessage(1); + confirmResponse.m_messageType = NetMessageType.NatIntroductionConfirmed; + confirmResponse.Write(isFromClient ? HostByte : ClientByte); + confirmResponse.Write(token); + Interlocked.Increment(ref confirmResponse.m_recyclingCount); + m_unsentUnconnectedMessages.Enqueue(new NetTuple(senderEndPoint, confirmResponse)); + } + + private void HandleNatPunchConfirmed(int ptr, NetEndPoint senderEndPoint) + { + NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length + var isFromClient = tmp.ReadByte() == ClientByte; + if (isFromClient) + { + LogDebug("NAT punch confirmation received from " + senderEndPoint + " we're host, so we ignore this"); + return; + } + + string token = tmp.ReadString(); + + LogDebug("NAT punch confirmation received from " + senderEndPoint + " we're client so we go ahead and succeed the introduction"); // // Release punch success to client; enabling him to Connect() to msg.Sender if token is ok @@ -110,14 +157,6 @@ namespace Lidgren.Network punchSuccess.m_senderEndPoint = senderEndPoint; punchSuccess.Write(token); ReleaseMessage(punchSuccess); - - // send a return punch just for good measure - var punch = CreateMessage(1); - punch.m_messageType = NetMessageType.NatPunchMessage; - punch.Write((byte)0); - punch.Write(token); - Interlocked.Increment(ref punch.m_recyclingCount); - m_unsentUnconnectedMessages.Enqueue(new NetTuple(senderEndPoint, punch)); - } + } } } diff --git a/Lidgren.Network/NetPeer.Internal.cs b/Lidgren.Network/NetPeer.Internal.cs index d6b0e01..303059e 100644 --- a/Lidgren.Network/NetPeer.Internal.cs +++ b/Lidgren.Network/NetPeer.Internal.cs @@ -120,7 +120,7 @@ namespace Lidgren.Network m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); if (reBind) - m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, (int)1); + m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, (int)1); m_socket.ReceiveBufferSize = m_configuration.ReceiveBufferSize; m_socket.SendBufferSize = m_configuration.SendBufferSize; @@ -244,7 +244,7 @@ namespace Lidgren.Network Heartbeat(); NetUtility.Sleep(10); - + lock (m_initializeLock) { try @@ -412,7 +412,7 @@ namespace Lidgren.Network switch (sx.SocketErrorCode) { case SocketError.ConnectionReset: - // connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable" + // connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable" // we should shut down the connection; but m_senderRemote seemingly cannot be trusted, so which connection should we shut down?! // So, what to do? LogWarning("ConnectionReset"); @@ -629,6 +629,14 @@ namespace Lidgren.Network if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.NatIntroductionSuccess)) HandleNatPunch(ptr, senderEndPoint); return; + case NetMessageType.NatIntroductionConfirmRequest: + if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.NatIntroductionSuccess)) + HandleNatPunchConfirmRequest(ptr, senderEndPoint); + return; + case NetMessageType.NatIntroductionConfirmed: + if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.NatIntroductionSuccess)) + HandleNatPunchConfirmed(ptr, senderEndPoint); + return; case NetMessageType.ConnectResponse: lock (m_handshakes)