Skip to content

Commit 89bdd30

Browse files
committed
modernize hole punch. increase protocol to 11
1 parent d9d5e60 commit 89bdd30

File tree

6 files changed

+104
-128
lines changed

6 files changed

+104
-128
lines changed

LibSample/HolePunchServerTest.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ namespace LibSample
88
{
99
class WaitPeer
1010
{
11-
public IPEndPoint InternalAddr { get; private set; }
12-
public IPEndPoint ExternalAddr { get; private set; }
11+
public IPEndPoint InternalAddr { get; }
12+
public IPEndPoint ExternalAddr { get; }
1313
public DateTime RefreshTime { get; private set; }
1414

1515
public void Refresh()
1616
{
17-
RefreshTime = DateTime.Now;
17+
RefreshTime = DateTime.UtcNow;
1818
}
1919

2020
public WaitPeer(IPEndPoint internalAddr, IPEndPoint externalAddr)
@@ -39,8 +39,7 @@ class HolePunchServerTest : INatPunchListener
3939

4040
void INatPunchListener.OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token)
4141
{
42-
WaitPeer wpeer;
43-
if (_waitingPeers.TryGetValue(token, out wpeer))
42+
if (_waitingPeers.TryGetValue(token, out var wpeer))
4443
{
4544
if (wpeer.InternalAddr.Equals(localEndPoint) &&
4645
wpeer.ExternalAddr.Equals(remoteEndPoint))
@@ -168,7 +167,7 @@ public void Run()
168167
}
169168
}
170169

171-
DateTime nowTime = DateTime.Now;
170+
DateTime nowTime = DateTime.UtcNow;
172171

173172
_c1.NatPunchModule.PollEvents();
174173
_c2.NatPunchModule.PollEvents();

LibSample/Program.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ static void Main(string[] args)
2020
ntpRequest.Send();
2121

2222
//new EchoMessagesTest().Run();
23-
//new HolePunchServerTest().Run();
23+
new HolePunchServerTest().Run();
2424
//new BroadcastTest().Run();
2525
//new BenchmarkTest.TestHost().Run();
26-
new SerializerBenchmark().Run();
26+
//new SerializerBenchmark().Run();
2727
//new SpeedBench().Run();
2828
//new PacketProcessorExample().Run();
2929
//new AesEncryptionTest().Run();

LiteNetLib/NatPunchModule.cs

Lines changed: 93 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.Net.Sockets;
44
using LiteNetLib.Utils;
55

6-
//Some code parts taken from lidgren-network-gen3
76
namespace LiteNetLib
87
{
98
public interface INatPunchListener
@@ -51,60 +50,87 @@ struct SuccessEventData
5150
public string Token;
5251
}
5352

54-
private readonly NetSocket _socket;
55-
private readonly Queue<RequestEventData> _requestEvents;
56-
private readonly Queue<SuccessEventData> _successEvents;
57-
private const byte HostByte = 1;
58-
private const byte ClientByte = 0;
59-
public const int MaxTokenLength = 256;
53+
class NatIntroduceRequestPacket
54+
{
55+
public IPEndPoint Internal { get; set; }
56+
public string Token { get; set; }
57+
}
6058

59+
class NatIntroduceResponsePacket
60+
{
61+
public IPEndPoint Internal { get; set; }
62+
public IPEndPoint External { get; set; }
63+
public string Token { get; set; }
64+
}
65+
66+
class NatPunchPacket
67+
{
68+
public string Token { get; set; }
69+
}
70+
71+
private readonly NetSocket _socket;
72+
private readonly Queue<RequestEventData> _requestEvents = new Queue<RequestEventData>();
73+
private readonly Queue<SuccessEventData> _successEvents = new Queue<SuccessEventData>();
74+
private readonly NetDataReader _cacheReader = new NetDataReader();
75+
private readonly NetDataWriter _cacheWriter = new NetDataWriter();
76+
private readonly NetPacketProcessor _netPacketProcessor = new NetPacketProcessor(MaxTokenLength);
6177
private INatPunchListener _natPunchListener;
78+
public const int MaxTokenLength = 256;
6279

6380
internal NatPunchModule(NetSocket socket)
6481
{
6582
_socket = socket;
66-
_requestEvents = new Queue<RequestEventData>();
67-
_successEvents = new Queue<SuccessEventData>();
83+
_netPacketProcessor.SubscribeReusable<NatIntroduceResponsePacket>(OnNatIntroductionResponse);
84+
_netPacketProcessor.SubscribeReusable<NatIntroduceRequestPacket, IPEndPoint>(OnNatIntroductionRequest);
85+
_netPacketProcessor.SubscribeReusable<NatPunchPacket, IPEndPoint>(OnNatPunch);
86+
}
87+
88+
internal void ProcessMessage(IPEndPoint senderEndPoint, NetPacket packet)
89+
{
90+
_cacheReader.SetSource(packet.RawData, NetConstants.HeaderSize, packet.Size);
91+
_netPacketProcessor.ReadAllPackets(_cacheReader, senderEndPoint);
6892
}
6993

7094
public void Init(INatPunchListener listener)
7195
{
7296
_natPunchListener = listener;
7397
}
7498

99+
private void Send<T>(T packet, IPEndPoint target) where T : class, new()
100+
{
101+
SocketError errorCode = 0;
102+
_cacheWriter.Reset();
103+
_cacheWriter.Put((byte)PacketProperty.NatMessage);
104+
_netPacketProcessor.Write(_cacheWriter, packet);
105+
_socket.SendTo(_cacheWriter.Data, 0, _cacheWriter.Length, target, ref errorCode);
106+
}
107+
75108
public void NatIntroduce(
76109
IPEndPoint hostInternal,
77110
IPEndPoint hostExternal,
78111
IPEndPoint clientInternal,
79112
IPEndPoint clientExternal,
80113
string additionalInfo)
81114
{
82-
NetDataWriter dw = new NetDataWriter();
83-
84-
//First packet (server)
85-
//send to client
86-
dw.Put((byte)PacketProperty.NatIntroduction);
87-
dw.Put(ClientByte);
88-
dw.Put(hostInternal);
89-
dw.Put(hostExternal);
90-
dw.Put(additionalInfo, MaxTokenLength);
91-
SocketError errorCode = 0;
92-
_socket.SendTo(dw.Data, 0, dw.Length, clientExternal, ref errorCode);
93-
94-
//Second packet (client)
95-
//send to server
96-
dw.Reset();
97-
dw.Put((byte)PacketProperty.NatIntroduction);
98-
dw.Put(HostByte);
99-
dw.Put(clientInternal);
100-
dw.Put(clientExternal);
101-
dw.Put(additionalInfo, MaxTokenLength);
102-
_socket.SendTo(dw.Data, 0, dw.Length, hostExternal, ref errorCode);
115+
var req = new NatIntroduceResponsePacket
116+
{
117+
Token = additionalInfo
118+
};
119+
120+
//First packet (server) send to client
121+
req.Internal = hostInternal;
122+
req.External = hostExternal;
123+
Send(req, clientExternal);
124+
125+
//Second packet (client) send to server
126+
req.Internal = clientInternal;
127+
req.External = clientExternal;
128+
Send(req, hostExternal);
103129
}
104130

105131
public void PollEvents()
106132
{
107-
if (_natPunchListener == null)
133+
if (_natPunchListener == null || (_successEvents.Count == 0 && _requestEvents.Count == 0))
108134
return;
109135
lock (_successEvents)
110136
{
@@ -132,110 +158,68 @@ public void SendNatIntroduceRequest(string host, int port, string additionalInfo
132158
public void SendNatIntroduceRequest(IPEndPoint masterServerEndPoint, string additionalInfo)
133159
{
134160
//prepare outgoing data
135-
NetDataWriter dw = new NetDataWriter();
136161
string networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv4);
137162
if (string.IsNullOrEmpty(networkIp))
138163
{
139164
networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv6);
140165
}
141-
IPEndPoint localEndPoint = NetUtils.MakeEndPoint(networkIp, _socket.LocalPort);
142-
dw.Put((byte)PacketProperty.NatIntroductionRequest);
143-
dw.Put(localEndPoint);
144-
dw.Put(additionalInfo, MaxTokenLength);
145166

146-
//prepare packet
147-
SocketError errorCode = 0;
148-
_socket.SendTo(dw.Data, 0, dw.Length, masterServerEndPoint, ref errorCode);
167+
Send(
168+
new NatIntroduceRequestPacket
169+
{
170+
Internal = NetUtils.MakeEndPoint(networkIp, _socket.LocalPort),
171+
Token = additionalInfo
172+
},
173+
masterServerEndPoint);
149174
}
150175

151-
private void HandleNatPunch(IPEndPoint senderEndPoint, NetDataReader dr)
176+
//We got request and must introduce
177+
private void OnNatIntroductionRequest(NatIntroduceRequestPacket req, IPEndPoint senderEndPoint)
152178
{
153-
byte fromHostByte = dr.GetByte();
154-
if (fromHostByte != HostByte && fromHostByte != ClientByte)
155-
{
156-
//garbage
157-
return;
158-
}
159-
160-
//Read info
161-
string additionalInfo = dr.GetString(MaxTokenLength);
162-
NetDebug.Write(NetLogLevel.Trace, "[NAT] punch received from {0} - additional info: {1}", senderEndPoint, additionalInfo);
163-
164-
//Release punch success to client; enabling him to Connect() to msg.Sender if token is ok
165-
lock (_successEvents)
179+
lock (_requestEvents)
166180
{
167-
_successEvents.Enqueue(new SuccessEventData { TargetEndPoint = senderEndPoint, Token = additionalInfo });
181+
_requestEvents.Enqueue(new RequestEventData
182+
{
183+
LocalEndPoint = req.Internal,
184+
RemoteEndPoint = senderEndPoint,
185+
Token = req.Token
186+
});
168187
}
169188
}
170189

171-
private void HandleNatIntroduction(NetDataReader dr)
190+
//We got introduce and must punch
191+
private void OnNatIntroductionResponse(NatIntroduceResponsePacket req)
172192
{
173-
// read intro
174-
byte hostByte = dr.GetByte();
175-
IPEndPoint remoteInternal = dr.GetNetEndPoint();
176-
IPEndPoint remoteExternal = dr.GetNetEndPoint();
177-
string token = dr.GetString(MaxTokenLength);
178-
179-
NetDebug.Write(NetLogLevel.Trace, "[NAT] introduction received; we are designated " + (hostByte == HostByte ? "host" : "client"));
180-
NetDataWriter writer = new NetDataWriter();
193+
NetDebug.Write(NetLogLevel.Trace, "[NAT] introduction received");
181194

182195
// send internal punch
183-
writer.Put((byte)PacketProperty.NatPunchMessage);
184-
writer.Put(hostByte);
185-
writer.Put(token);
186-
SocketError errorCode = 0;
187-
_socket.SendTo(writer.Data, 0, writer.Length, remoteInternal, ref errorCode);
188-
NetDebug.Write(NetLogLevel.Trace, "[NAT] internal punch sent to " + remoteInternal);
189-
190-
// send external punch
191-
writer.Reset();
192-
writer.Put((byte)PacketProperty.NatPunchMessage);
193-
writer.Put(hostByte);
194-
writer.Put(token);
196+
var punchPacket = new NatPunchPacket {Token = req.Token};
197+
Send(punchPacket, req.Internal);
198+
NetDebug.Write(NetLogLevel.Trace, "[NAT] internal punch sent to " + req.Internal);
195199

196200
// hack for some routers
201+
SocketError errorCode = 0;
197202
_socket.Ttl = 2;
198-
_socket.SendTo(new []{(byte)PacketProperty.Empty}, 0, 1, remoteExternal, ref errorCode);
203+
_socket.SendTo(new[] { (byte)PacketProperty.Empty }, 0, 1, req.External, ref errorCode);
199204

200-
// actual send
205+
// send external punch
201206
_socket.Ttl = NetConstants.SocketTTL;
202-
_socket.SendTo(writer.Data, 0, writer.Length, remoteExternal, ref errorCode);
203-
204-
NetDebug.Write(NetLogLevel.Trace, "[NAT] external punch sent to " + remoteExternal);
207+
Send(punchPacket, req.External);
208+
NetDebug.Write(NetLogLevel.Trace, "[NAT] external punch sent to " + req.External);
205209
}
206210

207-
private void HandleNatIntroductionRequest(IPEndPoint senderEndPoint, NetDataReader dr)
211+
//We got punch and can connect
212+
private void OnNatPunch(NatPunchPacket req, IPEndPoint senderEndPoint)
208213
{
209-
IPEndPoint localEp = dr.GetNetEndPoint();
210-
string token = dr.GetString(MaxTokenLength);
211-
lock (_requestEvents)
212-
{
213-
_requestEvents.Enqueue(new RequestEventData
214-
{
215-
LocalEndPoint = localEp,
216-
RemoteEndPoint = senderEndPoint,
217-
Token = token
218-
});
219-
}
220-
}
214+
//Read info
215+
NetDebug.Write(NetLogLevel.Trace, "[NAT] punch received from {0} - additional info: {1}",
216+
senderEndPoint, req.Token);
221217

222-
internal void ProcessMessage(IPEndPoint senderEndPoint, NetPacket packet)
223-
{
224-
var dr = new NetDataReader(packet.RawData, NetConstants.HeaderSize, packet.Size);
225-
switch (packet.Property)
218+
//Release punch success to client; enabling him to Connect() to Sender if token is ok
219+
lock (_successEvents)
226220
{
227-
case PacketProperty.NatIntroductionRequest:
228-
//We got request and must introduce
229-
HandleNatIntroductionRequest(senderEndPoint, dr);
230-
break;
231-
case PacketProperty.NatIntroduction:
232-
//We got introduce and must punch
233-
HandleNatIntroduction(dr);
234-
break;
235-
case PacketProperty.NatPunchMessage:
236-
//We got punch and can connect
237-
HandleNatPunch(senderEndPoint, dr);
238-
break;
221+
_successEvents.Enqueue(new SuccessEventData
222+
{ TargetEndPoint = senderEndPoint, Token = req.Token });
239223
}
240224
}
241225
}

LiteNetLib/NetConstants.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public static class NetConstants
4949
public const ushort HalfMaxSequence = MaxSequence / 2;
5050

5151
//protocol
52-
internal const int ProtocolId = 10;
52+
internal const int ProtocolId = 11;
5353
internal const int MaxUdpHeaderSize = 68;
5454

5555
internal static readonly int[] PossibleMtu =

LiteNetLib/NetManager.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,6 @@ private struct IncomingData
261261
/// </summary>
262262
public bool EnableStatistics = false;
263263

264-
//modules
265264
/// <summary>
266265
/// NatPunchModule for NAT hole punching operations
267266
/// </summary>
@@ -966,9 +965,7 @@ private void DataReceived(byte[] reusableBuffer, int count, IPEndPoint remoteEnd
966965
CreateEvent(NetEvent.EType.ReceiveUnconnected, remoteEndPoint: remoteEndPoint, readerSource: packet);
967966
break;
968967

969-
case PacketProperty.NatIntroduction:
970-
case PacketProperty.NatIntroductionRequest:
971-
case PacketProperty.NatPunchMessage:
968+
case PacketProperty.NatMessage:
972969
if (NatPunchEnabled)
973970
NatPunchModule.ProcessMessage(remoteEndPoint, packet);
974971
break;

LiteNetLib/NetPacket.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,15 @@ internal enum PacketProperty : byte
1414
ConnectAccept,
1515
Disconnect,
1616
UnconnectedMessage,
17-
NatIntroductionRequest,
18-
NatIntroduction,
19-
NatPunchMessage,
2017
MtuCheck,
2118
MtuOk,
2219
Broadcast,
2320
Merged,
2421
ShutdownOk,
2522
PeerNotFound,
2623
InvalidProtocol,
27-
Empty
24+
NatMessage,
25+
Empty,
2826
}
2927

3028
internal sealed class NetPacket
@@ -121,8 +119,6 @@ public static int GetHeaderSize(PacketProperty property)
121119
return NetConstants.HeaderSize + 8;
122120
case PacketProperty.Pong:
123121
return NetConstants.HeaderSize + 10;
124-
case PacketProperty.Empty:
125-
return 1;
126122
default:
127123
return NetConstants.HeaderSize;
128124
}

0 commit comments

Comments
 (0)