Skip to content

Commit d1b6feb

Browse files
authored
Merge pull request #5 from Yellow-Dog-Man/froox/feat/dynamic-window-size
Dynamic Window Size
2 parents 95f16b1 + 8fa5228 commit d1b6feb

File tree

7 files changed

+456
-40
lines changed

7 files changed

+456
-40
lines changed

LibSample/Program.cs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public static class Program
1414
new PacketProcessorExample(),
1515
new AesEncryptionTest(),
1616
new NtpTest(),
17+
new ThroughputTest(),
1718
};
1819

1920
static void Main(string[] args)

LibSample/ThroughputTest.cs

+259
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Net;
4+
using System.Net.Sockets;
5+
using System.Text;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using LiteNetLib;
9+
using LiteNetLib.Utils;
10+
11+
namespace LibSample
12+
{
13+
public class ThroughputTest : IExample
14+
{
15+
public const long TOTAL_BYTES = 1024L * 1024L * 16; // 256 MB
16+
public const int SEND_CHUNK_SIZE = 1024 * 1024; // 1 MB
17+
public const int SEND_CHUNK_COUNT = (int)(TOTAL_BYTES / SEND_CHUNK_SIZE);
18+
19+
public class Server : INetEventListener, IDisposable
20+
{
21+
public int ReliableReceived { get; private set; }
22+
public bool HasCompleted { get; private set; }
23+
24+
readonly NetManager _server;
25+
26+
public TimeSpan TransferTime => _lastChunkReceiveTime - _peerConnectTime;
27+
28+
DateTime _peerConnectTime;
29+
DateTime _lastChunkReceiveTime;
30+
31+
public NetStatistics Stats => _server.Statistics;
32+
33+
public Server(int latency, int jitter, int packetLoss)
34+
{
35+
_server = new NetManager(this)
36+
{
37+
AutoRecycle = true,
38+
UpdateTime = 1,
39+
SimulatePacketLoss = true,
40+
SimulationPacketLossChance = packetLoss,
41+
SimulateLatency = true,
42+
SimulationMinLatency = latency,
43+
SimulationMaxLatency = latency + jitter,
44+
EnableStatistics = true,
45+
UnsyncedEvents = true
46+
};
47+
_server.Start(9050);
48+
}
49+
50+
void INetEventListener.OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode)
51+
{
52+
Console.WriteLine($"Server: error: {socketErrorCode}");
53+
}
54+
55+
void INetEventListener.OnNetworkLatencyUpdate(NetPeer peer, int latency)
56+
{
57+
}
58+
59+
public void OnConnectionRequest(ConnectionRequest request)
60+
{
61+
request.AcceptIfKey("ConnKey");
62+
}
63+
64+
void INetEventListener.OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod)
65+
{
66+
if (++ReliableReceived == SEND_CHUNK_COUNT)
67+
{
68+
_lastChunkReceiveTime = DateTime.UtcNow;
69+
HasCompleted = true;
70+
}
71+
}
72+
73+
void INetEventListener.OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader,
74+
UnconnectedMessageType messageType)
75+
{
76+
}
77+
78+
void INetEventListener.OnPeerConnected(NetPeer peer)
79+
{
80+
Console.WriteLine($"Server: client connected: {peer}");
81+
82+
_peerConnectTime = DateTime.UtcNow;
83+
}
84+
85+
void INetEventListener.OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo)
86+
{
87+
Console.WriteLine($"Server: client disconnected: {disconnectInfo.Reason}");
88+
}
89+
90+
public void Dispose()
91+
{
92+
_server.Stop();
93+
}
94+
}
95+
96+
public class Client : INetEventListener, IDisposable
97+
{
98+
public int ReliableSent;
99+
100+
public bool HasCompleted { get; private set; }
101+
public bool IsRunning => _peer.ConnectionState == ConnectionState.Connected;
102+
103+
readonly NetManager _client;
104+
NetPeer _peer;
105+
106+
public NetStatistics Stats => _client.Statistics;
107+
108+
public Client(int latency, int jitter, int packetLoss)
109+
{
110+
_client = new NetManager(this)
111+
{
112+
UnsyncedEvents = true,
113+
AutoRecycle = true,
114+
SimulatePacketLoss = true,
115+
SimulationPacketLossChance = packetLoss,
116+
SimulateLatency = true,
117+
SimulationMinLatency = latency,
118+
SimulationMaxLatency = latency + jitter,
119+
EnableStatistics = true
120+
};
121+
_client.Start();
122+
}
123+
124+
public void SendReliable(byte[] data)
125+
{
126+
_peer.Send(data, DeliveryMethod.ReliableOrdered);
127+
ReliableSent++;
128+
}
129+
130+
public void Connect()
131+
{
132+
_peer = _client.Connect("localhost", 9050, "ConnKey");
133+
}
134+
135+
void INetEventListener.OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode)
136+
{
137+
Console.WriteLine($"Client: error: {socketErrorCode}");
138+
}
139+
140+
void INetEventListener.OnNetworkLatencyUpdate(NetPeer peer, int latency)
141+
{
142+
}
143+
144+
public void OnConnectionRequest(ConnectionRequest request)
145+
{
146+
request.RejectForce();
147+
}
148+
149+
void INetEventListener.OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod)
150+
{
151+
152+
}
153+
154+
void INetEventListener.OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader,
155+
UnconnectedMessageType messageType)
156+
{
157+
}
158+
159+
void INetEventListener.OnPeerConnected(NetPeer peer)
160+
{
161+
Task.Run(() =>
162+
{
163+
var data = new byte[SEND_CHUNK_SIZE];
164+
var r = new Random();
165+
166+
for (int i = 0; i < SEND_CHUNK_COUNT; i++)
167+
{
168+
r.NextBytes(data);
169+
SendReliable(data);
170+
}
171+
172+
HasCompleted = true;
173+
});
174+
}
175+
176+
void INetEventListener.OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo)
177+
{
178+
Console.WriteLine($"Client: Disconnected {disconnectInfo.Reason}");
179+
}
180+
181+
public void Dispose()
182+
{
183+
_client.Stop();
184+
}
185+
}
186+
187+
public void Run()
188+
{
189+
Console.WriteLine("Testing Throughput...");
190+
191+
int[] packetLossValues = new int[] { 0, 1, 2, 5, 10 };
192+
int[] latencies = new int[] { 5, 10, 20, 40, 60, 80, 100, 150, 200, 300 };
193+
194+
Dictionary<int, List<TimeSpan>> resultGroups = new Dictionary<int, List<TimeSpan>>();
195+
196+
foreach (var packetLoss in packetLossValues)
197+
{
198+
var results = new List<TimeSpan>();
199+
200+
resultGroups.Add(packetLoss, results);
201+
202+
foreach (var latency in latencies)
203+
{
204+
var jitter = Math.Max(1, (int)Math.Round(latency / 5f));
205+
206+
var serverThread = new Thread(() => StartServer(latency, jitter, packetLoss, results));
207+
serverThread.Start();
208+
209+
var clientThread = new Thread(() => StartClient(latency, jitter, packetLoss));
210+
clientThread.Start();
211+
212+
Console.WriteLine($"Processing, latency: {latency} ms, jitter: {jitter}, packet loss: {packetLoss} %...");
213+
214+
serverThread.Join();
215+
clientThread.Join();
216+
}
217+
}
218+
219+
foreach (var resultGroup in resultGroups)
220+
{
221+
Console.WriteLine($"Results for packet loss: {resultGroup.Key} %");
222+
223+
for (int i = 0; i < latencies.Length; i++)
224+
{
225+
var bytesPerSec = TOTAL_BYTES / resultGroup.Value[i].TotalSeconds;
226+
Console.WriteLine($"\t{latencies[i]} ms -> {resultGroup.Value[i]}\t{bytesPerSec / 1024:F2} kB/s");
227+
}
228+
}
229+
}
230+
231+
static void StartServer(int latency, int jitter, int packetLoss, List<TimeSpan> results)
232+
{
233+
using (Server s = new Server(latency, jitter, packetLoss))
234+
{
235+
while (!s.HasCompleted)
236+
Thread.Sleep(100);
237+
238+
Console.WriteLine("SERVER RECEIVED -> Reliable: " + s.ReliableReceived + " in " + s.TransferTime);
239+
Console.WriteLine("SERVER STATS:\n" + s.Stats);
240+
241+
results.Add(s.TransferTime);
242+
}
243+
}
244+
245+
static void StartClient(int latency, int jitter, int packetLoss)
246+
{
247+
using (Client c = new Client(latency, jitter, packetLoss))
248+
{
249+
c.Connect();
250+
251+
while (!c.HasCompleted || c.IsRunning)
252+
Thread.Sleep(100);
253+
254+
Console.WriteLine("CLIENT SENT -> Reliable: " + c.ReliableSent);
255+
Console.WriteLine("CLIENT STATS:\n" + c.Stats);
256+
}
257+
}
258+
}
259+
}

LiteNetLib/BaseChannel.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace LiteNetLib
66
internal abstract class BaseChannel
77
{
88
protected readonly NetPeer Peer;
9-
protected readonly Queue<NetPacket> OutgoingQueue = new Queue<NetPacket>(NetConstants.DefaultWindowSize);
9+
protected readonly Queue<NetPacket> OutgoingQueue = new Queue<NetPacket>(NetConstants.MaximumWindowSize);
1010
private int _isAddedToPeerChannelSendQueue;
1111

1212
public int PacketsInQueue => OutgoingQueue.Count;

LiteNetLib/NetConstants.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ public enum DeliveryMethod : byte
3838
public static class NetConstants
3939
{
4040
//can be tuned
41-
public const int DefaultWindowSize = 64;
41+
public const int MinimumWindowSize = 8;
42+
public const int MaximumWindowSize = 512;
43+
public const int StartingDynamicWindowSize = 64;
4244
public const int SocketBufferSize = 1024 * 1024; //1mb
4345
public const int SocketTTL = 255;
4446

LiteNetLib/NetPeer.cs

+44-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Concurrent;
66
using System.Collections.Generic;
77
using System.Diagnostics;
8+
using System.Linq;
89
using System.Net;
910
using System.Threading;
1011
using LiteNetLib.Utils;
@@ -81,6 +82,8 @@ private set
8182
}
8283
}
8384

85+
internal bool HasUnsentData => _mergeCount > 0;
86+
8487
//Channels
8588
private readonly Queue<NetPacket> _unreliableChannel;
8689
private readonly ConcurrentQueue<BaseChannel> _channelSendQueue;
@@ -302,6 +305,27 @@ private void OverrideMtu(int mtuValue)
302305
_finishMtu = true;
303306
}
304307

308+
public void SetDynamicWindowSize(int size)
309+
{
310+
foreach (var channel in _channels)
311+
if (channel is ReliableChannel reliableChannel)
312+
reliableChannel.CurrentDynamicWindowSize = size;
313+
}
314+
315+
public int TotalReliablePacketsInFlight
316+
{
317+
get
318+
{
319+
int count = 0;
320+
321+
foreach (var channel in _channels)
322+
if (channel is ReliableChannel reliableChannel)
323+
count += reliableChannel.CurrentPacketsInFlight;
324+
325+
return count;
326+
}
327+
}
328+
305329
/// <summary>
306330
/// Returns packets count in queue for reliable channel
307331
/// </summary>
@@ -1277,10 +1301,19 @@ private void SendMerged()
12771301
_mergeCount = 0;
12781302
}
12791303

1280-
internal void SendUserData(NetPacket packet)
1304+
internal int ComputeMergedPacketSize(NetPacket packet) => NetConstants.HeaderSize + packet.Size + 2;
1305+
1306+
internal bool CanMerge(NetPacket packet)
1307+
{
1308+
int mergedPacketSize = ComputeMergedPacketSize(packet);
1309+
1310+
return _mergePos + mergedPacketSize <= _mtu;
1311+
}
1312+
1313+
internal bool SendUserData(NetPacket packet)
12811314
{
12821315
packet.ConnectionNumber = _connectNum;
1283-
int mergedPacketSize = NetConstants.HeaderSize + packet.Size + 2;
1316+
int mergedPacketSize = ComputeMergedPacketSize(packet);
12841317
const int sizeTreshold = 20;
12851318
if (mergedPacketSize + sizeTreshold >= _mtu)
12861319
{
@@ -1293,16 +1326,23 @@ internal void SendUserData(NetPacket packet)
12931326
Statistics.AddBytesSent(bytesSent);
12941327
}
12951328

1296-
return;
1329+
return true;
12971330
}
1298-
if (_mergePos + mergedPacketSize > _mtu)
1331+
1332+
bool packetWasSent = false;
1333+
if (!CanMerge(packet))
1334+
{
12991335
SendMerged();
1336+
packetWasSent = true;
1337+
}
13001338

13011339
FastBitConverter.GetBytes(_mergeData.RawData, _mergePos + NetConstants.HeaderSize, (ushort)packet.Size);
13021340
Buffer.BlockCopy(packet.RawData, 0, _mergeData.RawData, _mergePos + NetConstants.HeaderSize + 2, packet.Size);
13031341
_mergePos += packet.Size + 2;
13041342
_mergeCount++;
13051343
//DebugWriteForce("Merged: " + _mergePos + "/" + (_mtu - 2) + ", count: " + _mergeCount);
1344+
1345+
return packetWasSent;
13061346
}
13071347

13081348
internal void Update(int deltaTime)

0 commit comments

Comments
 (0)