-
Notifications
You must be signed in to change notification settings - Fork 1
/
NetworkHandler.cs
249 lines (235 loc) · 9.05 KB
/
NetworkHandler.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Threading;
public class NetworkHandler
{
private const long ACK_INTERVAL = TimeSpan.TicksPerMillisecond * 20;
private ConcurrentDictionary<int, TcpClient> tcpMappings = new ConcurrentDictionary<int, TcpClient>();
private ConcurrentDictionary<int, IPEndPoint> udpMappings = new ConcurrentDictionary<int, IPEndPoint>();
private ConcurrentDictionary<int, long> udpIgnore = new ConcurrentDictionary<int, long>();
private ConcurrentQueue<int> udpSendDisconnect = new ConcurrentQueue<int>();
private ConcurrentDictionary<int, ReliableHandler> reliableHandlers = new ConcurrentDictionary<int, ReliableHandler>();
private TunnelServer tunnelServer;
private TunnelClient tunnelClient;
private byte[] sendBuffer = new byte[516];
//Stats
private long tcpBytesReceivedSecond = 0;
private long tcpBytesSentSecond = 0;
private long tcpBytesReceivedTotal = 0;
private long tcpBytesSentTotal = 0;
private long udpBytesReceivedSecond = 0;
private long udpBytesSentSecond = 0;
private long udpBytesReceivedTotal = 0;
private long udpBytesSentTotal = 0;
private long retransmitBytes = 0;
public void SetServer(TunnelServer tunnelServer)
{
this.tunnelServer = tunnelServer;
}
public void SetClient(TunnelClient tunnelClient)
{
this.tunnelClient = tunnelClient;
}
public void HandleUDPMessage(byte[] data, int length, IPEndPoint endpoint)
{
if (length < 16)
{
return;
}
int clientID = ReadInt32(data, 0);
int sequence = ReadInt32(data, 4);
int ack = ReadInt32(data, 8);
int dataLength = ReadInt32(data, 12);
if (sequence == -2)
{
if (!udpIgnore.ContainsKey(clientID))
{
Console.WriteLine("Tunnel connection close for " + clientID);
ForgetClient(clientID);
}
}
if (length != (16 + dataLength))
{
return;
}
udpBytesReceivedSecond += length;
udpBytesReceivedTotal += length;
if (udpIgnore.TryGetValue(clientID, out long ignoreTime))
{
long currentTime = DateTime.UtcNow.Ticks;
if (ignoreTime > currentTime)
{
return;
}
else
{
udpIgnore.TryRemove(clientID, out long _);
udpMappings.TryRemove(clientID, out IPEndPoint _2);
}
}
if (!udpMappings.ContainsKey(clientID))
{
Console.WriteLine("Adding new connection: " + clientID);
if (tunnelServer != null)
{
TcpClient newClient = tunnelServer.GetNewConnection(clientID);
tcpMappings.TryAdd(clientID, newClient);
}
udpMappings.TryAdd(clientID, new IPEndPoint(endpoint.Address, endpoint.Port));
}
ReliableHandler rh = GetClient(clientID);
rh.HandleUDPMessage(data, dataLength, sequence, ack);
}
public void HandleTCPMessage(int clientID, byte[] data, int length)
{
tcpBytesReceivedSecond += length;
tcpBytesReceivedTotal += length;
ReliableHandler rh = GetClient(clientID);
rh.HandleTCPData(data, length);
}
public void ConnectClient(int clientID, TcpClient newClient, IPEndPoint udpServer)
{
tcpMappings.TryAdd(clientID, newClient);
udpMappings.TryAdd(clientID, udpServer);
}
public ReliableHandler GetClient(int clientID)
{
ReliableHandler retVal = null;
if (!reliableHandlers.TryGetValue(clientID, out retVal))
{
retVal = new ReliableHandler(clientID);
long currentTime = DateTime.UtcNow.Ticks;
retVal.lastUDPSend = currentTime;
retVal.lastUDPReceive = currentTime;
retVal.lastTCPReceive = currentTime;
reliableHandlers.TryAdd(clientID, retVal);
}
return retVal;
}
public bool ShouldDisconnect(int clientID)
{
if (reliableHandlers.TryGetValue(clientID, out ReliableHandler reliableHandler))
{
long currentTime = DateTime.UtcNow.Ticks;
return (currentTime > (reliableHandler.lastUDPReceive + TimeSpan.TicksPerMinute)) || (currentTime > (reliableHandler.lastTCPReceive + TimeSpan.TicksPerMinute));
}
return udpIgnore.ContainsKey(clientID);
}
public void ForgetClient(int clientID)
{
//Ignore client
long currentTime = DateTime.UtcNow.Ticks;
udpIgnore.TryAdd(clientID, currentTime + TimeSpan.TicksPerHour);
udpSendDisconnect.Enqueue(clientID);
reliableHandlers.TryRemove(clientID, out ReliableHandler _);
if (tunnelClient != null)
{
tunnelClient.DisconnectClient(clientID);
}
if (tunnelServer != null)
{
tunnelServer.DisconnectClient(clientID);
}
}
public bool GetUDPMessage(out byte[] data, out int length, out IPEndPoint endPoint)
{
long currentTime = DateTime.UtcNow.Ticks;
foreach (KeyValuePair<int, ReliableHandler> kvp in reliableHandlers)
{
int clientID = kvp.Key;
if (udpMappings.TryGetValue(clientID, out endPoint))
{
ReliableHandler rh = kvp.Value;
rh.SendToUDP(out int sendSequence, out int sendACK, out byte[] sendData);
//Skip ACK only messages if we sent a message within ACK_INTERVAL.
if (sendSequence == -1 && (currentTime - rh.lastUDPSend) < ACK_INTERVAL)
{
continue;
}
rh.lastUDPSend = currentTime;
WriteInt32(clientID, sendBuffer, 0);
WriteInt32(sendSequence, sendBuffer, 4);
WriteInt32(sendACK, sendBuffer, 8);
WriteInt32(0, sendBuffer, 12);
length = 16;
if (sendData != null)
{
WriteInt32(sendData.Length, sendBuffer, 12);
Array.Copy(sendData, 0, sendBuffer, 16, sendData.Length);
length = 16 + sendData.Length;
}
data = sendBuffer;
udpBytesSentSecond += length;
udpBytesSentTotal += length;
return true;
}
}
if (udpSendDisconnect.TryDequeue(out int sendDisconnectClientID))
{
if (udpMappings.TryGetValue(sendDisconnectClientID, out endPoint))
{
WriteInt32(sendDisconnectClientID, sendBuffer, 0);
WriteInt32(-2, sendBuffer, 4);
WriteInt32(-2, sendBuffer, 8);
WriteInt32(0, sendBuffer, 12);
length = 16;
data = sendBuffer;
udpBytesSentSecond += length;
udpBytesSentTotal += length;
return true;
}
}
retransmitBytes = 0;
foreach (KeyValuePair<int, ReliableHandler> kvp in reliableHandlers)
{
retransmitBytes += kvp.Value.retransmitBytes;
}
data = null;
endPoint = null;
length = 0;
return false;
}
public byte[] GetTCPMessage(int clientID)
{
if (reliableHandlers.TryGetValue(clientID, out ReliableHandler rh))
{
byte[] sendMessage = rh.SendToTCP();
if (sendMessage != null)
{
tcpBytesSentSecond += sendMessage.Length;
tcpBytesSentTotal += sendMessage.Length;
}
return sendMessage;
}
return null;
}
private int ReadInt32(byte[] data, int start)
{
return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, start));
}
public static void WriteInt32(int number, byte[] data, int index)
{
uint unumber = (uint)number;
data[index] = (byte)((unumber >> 24) & 0xFF);
data[index + 1] = (byte)((unumber >> 16) & 0xFF);
data[index + 2] = (byte)((unumber >> 8) & 0xFF);
data[index + 3] = (byte)((unumber) & 0xFF);
}
public void PrintStats(int sleepTime)
{
Console.WriteLine("==========");
Console.WriteLine("TCP Sent: {0} Total: {1}", (tcpBytesSentSecond / (sleepTime * 1024)), (tcpBytesSentTotal / 1024));
Console.WriteLine("TCP Receive: {0} Total: {1}", (tcpBytesReceivedSecond / (sleepTime * 1024)), (tcpBytesReceivedTotal / 1024));
Console.WriteLine("UDP Sent: {0} Total: {1}", (udpBytesSentSecond / (sleepTime * 1024)), (udpBytesSentTotal / 1024));
Console.WriteLine("UDP Receive: {0} Total: {1}", (udpBytesReceivedSecond / (sleepTime * 1024)), (udpBytesReceivedTotal / 1024));
Console.WriteLine("Retransmit queue: " + (retransmitBytes / 1024));
tcpBytesSentSecond = 0;
tcpBytesReceivedSecond = 0;
udpBytesReceivedSecond = 0;
udpBytesSentSecond = 0;
Thread.Sleep(1000 * sleepTime);
}
}