|
3 | 3 | using System.Net.Sockets; |
4 | 4 | using LiteNetLib.Utils; |
5 | 5 |
|
6 | | -//Some code parts taken from lidgren-network-gen3 |
7 | 6 | namespace LiteNetLib |
8 | 7 | { |
9 | 8 | public interface INatPunchListener |
@@ -51,60 +50,87 @@ struct SuccessEventData |
51 | 50 | public string Token; |
52 | 51 | } |
53 | 52 |
|
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 | + } |
60 | 58 |
|
| 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); |
61 | 77 | private INatPunchListener _natPunchListener; |
| 78 | + public const int MaxTokenLength = 256; |
62 | 79 |
|
63 | 80 | internal NatPunchModule(NetSocket socket) |
64 | 81 | { |
65 | 82 | _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); |
68 | 92 | } |
69 | 93 |
|
70 | 94 | public void Init(INatPunchListener listener) |
71 | 95 | { |
72 | 96 | _natPunchListener = listener; |
73 | 97 | } |
74 | 98 |
|
| 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 | + |
75 | 108 | public void NatIntroduce( |
76 | 109 | IPEndPoint hostInternal, |
77 | 110 | IPEndPoint hostExternal, |
78 | 111 | IPEndPoint clientInternal, |
79 | 112 | IPEndPoint clientExternal, |
80 | 113 | string additionalInfo) |
81 | 114 | { |
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); |
103 | 129 | } |
104 | 130 |
|
105 | 131 | public void PollEvents() |
106 | 132 | { |
107 | | - if (_natPunchListener == null) |
| 133 | + if (_natPunchListener == null || (_successEvents.Count == 0 && _requestEvents.Count == 0)) |
108 | 134 | return; |
109 | 135 | lock (_successEvents) |
110 | 136 | { |
@@ -132,110 +158,68 @@ public void SendNatIntroduceRequest(string host, int port, string additionalInfo |
132 | 158 | public void SendNatIntroduceRequest(IPEndPoint masterServerEndPoint, string additionalInfo) |
133 | 159 | { |
134 | 160 | //prepare outgoing data |
135 | | - NetDataWriter dw = new NetDataWriter(); |
136 | 161 | string networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv4); |
137 | 162 | if (string.IsNullOrEmpty(networkIp)) |
138 | 163 | { |
139 | 164 | networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv6); |
140 | 165 | } |
141 | | - IPEndPoint localEndPoint = NetUtils.MakeEndPoint(networkIp, _socket.LocalPort); |
142 | | - dw.Put((byte)PacketProperty.NatIntroductionRequest); |
143 | | - dw.Put(localEndPoint); |
144 | | - dw.Put(additionalInfo, MaxTokenLength); |
145 | 166 |
|
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); |
149 | 174 | } |
150 | 175 |
|
151 | | - private void HandleNatPunch(IPEndPoint senderEndPoint, NetDataReader dr) |
| 176 | + //We got request and must introduce |
| 177 | + private void OnNatIntroductionRequest(NatIntroduceRequestPacket req, IPEndPoint senderEndPoint) |
152 | 178 | { |
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) |
166 | 180 | { |
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 | + }); |
168 | 187 | } |
169 | 188 | } |
170 | 189 |
|
171 | | - private void HandleNatIntroduction(NetDataReader dr) |
| 190 | + //We got introduce and must punch |
| 191 | + private void OnNatIntroductionResponse(NatIntroduceResponsePacket req) |
172 | 192 | { |
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"); |
181 | 194 |
|
182 | 195 | // 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); |
195 | 199 |
|
196 | 200 | // hack for some routers |
| 201 | + SocketError errorCode = 0; |
197 | 202 | _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); |
199 | 204 |
|
200 | | - // actual send |
| 205 | + // send external punch |
201 | 206 | _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); |
205 | 209 | } |
206 | 210 |
|
207 | | - private void HandleNatIntroductionRequest(IPEndPoint senderEndPoint, NetDataReader dr) |
| 211 | + //We got punch and can connect |
| 212 | + private void OnNatPunch(NatPunchPacket req, IPEndPoint senderEndPoint) |
208 | 213 | { |
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); |
221 | 217 |
|
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) |
226 | 220 | { |
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 }); |
239 | 223 | } |
240 | 224 | } |
241 | 225 | } |
|
0 commit comments