Skip to content

Commit b2129fe

Browse files
fix: close connection on reliable send queue overflow (#1747)
1 parent 3dd5dfe commit b2129fe

File tree

4 files changed

+109
-43
lines changed

4 files changed

+109
-43
lines changed

com.unity.netcode.adapter.utp/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ All notable changes to this package will be documented in this file. The format
99
### Changed
1010

1111
- Updated Unity Transport package to 1.0.0-pre.13. (#1696)
12+
- Overflowing the reliable send queue of a connection will now result in the connection being closed, rather than spamming the log with errors about not being able to send a payload. It is deemed better to close the connection than to lose reliable traffic (which could cause all sorts of weird synchronization issues). (#1747)
1213

1314
### Fixed
1415

com.unity.netcode.adapter.utp/Runtime/UnityTransport.cs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -886,9 +886,45 @@ public override void Send(ulong clientId, ArraySegment<byte> payload, NetworkDel
886886

887887
if (!queue.PushMessage(payload))
888888
{
889-
Debug.LogError($"Couldn't add payload of size {payload.Count} to batched send queue. " +
890-
$"Perhaps configured 'Max Send Queue Size' ({m_MaxSendQueueSize}) is too small for workload.");
891-
return;
889+
if (pipeline == m_ReliableSequencedPipeline)
890+
{
891+
// If the message is sent reliably, then we're over capacity and we can't
892+
// provide any reliability guarantees anymore. Disconnect the client since at
893+
// this point they're bound to become desynchronized.
894+
895+
var ngoClientId = NetworkManager?.TransportIdToClientId(clientId) ?? clientId;
896+
Debug.LogError($"Couldn't add payload of size {payload.Count} to reliable send queue. " +
897+
$"Closing connection {ngoClientId} as reliability guarantees can't be maintained. " +
898+
$"Perhaps 'Max Send Queue Size' ({m_MaxSendQueueSize}) is too small for workload.");
899+
900+
if (clientId == m_ServerClientId)
901+
{
902+
DisconnectLocalClient();
903+
}
904+
else
905+
{
906+
DisconnectRemoteClient(clientId);
907+
908+
// DisconnectRemoteClient doesn't notify SDK of disconnection.
909+
InvokeOnTransportEvent(NetcodeNetworkEvent.Disconnect,
910+
clientId,
911+
default(ArraySegment<byte>),
912+
Time.realtimeSinceStartup);
913+
}
914+
}
915+
else
916+
{
917+
// If the message is sent unreliably, we can always just flush everything out
918+
// to make space in the send queue. This is an expensive operation, but a user
919+
// would need to send A LOT of unreliable traffic in one update to get here.
920+
921+
m_Driver.ScheduleFlushSend(default).Complete();
922+
SendBatchedMessages(sendTarget, queue);
923+
924+
// Don't check for failure. If it still doesn't work, there's nothing we can do
925+
// at this point and the message is lost (it was sent unreliable anyway).
926+
queue.PushMessage(payload);
927+
}
892928
}
893929
}
894930

com.unity.netcode.adapter.utp/Tests/Runtime/TransportTests.cs

Lines changed: 68 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
using System.Text;
99
using UnityEngine;
1010
using UnityEngine.TestTools;
11-
using Unity.Netcode.UTP.Utilities;
1211
using static Unity.Netcode.UTP.RuntimeTests.RuntimeTestsHelpers;
1312

1413
namespace Unity.Netcode.UTP.RuntimeTests
@@ -161,44 +160,6 @@ public IEnumerator SendMaximumPayloadSize([ValueSource("k_DeliveryParameters")]
161160
yield return null;
162161
}
163162

164-
[UnityTest]
165-
public IEnumerator FilledSendQueueMultipleSends([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
166-
{
167-
InitializeTransport(out m_Server, out m_ServerEvents);
168-
InitializeTransport(out m_Client1, out m_Client1Events);
169-
170-
m_Server.StartServer();
171-
m_Client1.StartClient();
172-
173-
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
174-
175-
var numSends = UnityTransport.InitialMaxSendQueueSize / 1024;
176-
177-
for (int i = 0; i < numSends; i++)
178-
{
179-
// We remove 4 bytes because each send carries a 4 bytes overhead in the send queue.
180-
// Without that we wouldn't fill the send queue; it would get flushed right when we
181-
// try to send the last message.
182-
var payload = new ArraySegment<byte>(new byte[1024 - BatchedSendQueue.PerMessageOverhead]);
183-
m_Client1.Send(m_Client1.ServerClientId, payload, delivery);
184-
}
185-
186-
// Manually wait. This ends up generating quite a bit of packets and it might take a
187-
// while for everything to make it to the server.
188-
yield return new WaitForSeconds(numSends * 0.02f);
189-
190-
// Extra event is the connect event.
191-
Assert.AreEqual(numSends + 1, m_ServerEvents.Count);
192-
193-
for (int i = 1; i <= numSends; i++)
194-
{
195-
Assert.AreEqual(NetworkEvent.Data, m_ServerEvents[i].Type);
196-
Assert.AreEqual(1024 - BatchedSendQueue.PerMessageOverhead, m_ServerEvents[i].Data.Count);
197-
}
198-
199-
yield return null;
200-
}
201-
202163
// Check making multiple sends to a client in a single frame.
203164
[UnityTest]
204165
public IEnumerator MultipleSendsSingleFrame([ValueSource("k_DeliveryParameters")] NetworkDelivery delivery)
@@ -305,6 +266,74 @@ public IEnumerator ReceiveMultipleClients([ValueSource("k_DeliveryParameters")]
305266

306267
yield return null;
307268
}
269+
270+
// Check that we get disconnected when overflowing the reliable send queue.
271+
[UnityTest]
272+
public IEnumerator DisconnectOnReliableSendQueueOverflow()
273+
{
274+
InitializeTransport(out m_Server, out m_ServerEvents);
275+
InitializeTransport(out m_Client1, out m_Client1Events);
276+
277+
m_Server.StartServer();
278+
m_Client1.StartClient();
279+
280+
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
281+
282+
m_Server.Shutdown();
283+
284+
var numSends = (UnityTransport.InitialMaxSendQueueSize / 1024) + 1;
285+
286+
for (int i = 0; i < numSends; i++)
287+
{
288+
var payload = new ArraySegment<byte>(new byte[1024]);
289+
m_Client1.Send(m_Client1.ServerClientId, payload, NetworkDelivery.Reliable);
290+
}
291+
292+
LogAssert.Expect(LogType.Error, "Couldn't add payload of size 1024 to reliable send queue. " +
293+
$"Closing connection {m_Client1.ServerClientId} as reliability guarantees can't be maintained. " +
294+
$"Perhaps 'Max Send Queue Size' ({UnityTransport.InitialMaxSendQueueSize}) is too small for workload.");
295+
296+
Assert.AreEqual(2, m_Client1Events.Count);
297+
Assert.AreEqual(NetworkEvent.Disconnect, m_Client1Events[1].Type);
298+
299+
yield return null;
300+
}
301+
302+
// Check that it's fine to overflow the unreliable send queue (traffic is flushed on overflow).
303+
[UnityTest]
304+
public IEnumerator SendCompletesOnUnreliableSendQueueOverflow()
305+
{
306+
InitializeTransport(out m_Server, out m_ServerEvents);
307+
InitializeTransport(out m_Client1, out m_Client1Events);
308+
309+
m_Server.StartServer();
310+
m_Client1.StartClient();
311+
312+
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
313+
314+
var numSends = (UnityTransport.InitialMaxSendQueueSize / 1024) + 1;
315+
316+
for (int i = 0; i < numSends; i++)
317+
{
318+
var payload = new ArraySegment<byte>(new byte[1024]);
319+
m_Client1.Send(m_Client1.ServerClientId, payload, NetworkDelivery.Unreliable);
320+
}
321+
322+
// Manually wait. This ends up generating quite a bit of packets and it might take a
323+
// while for everything to make it to the server.
324+
yield return new WaitForSeconds(numSends * 0.02f);
325+
326+
// Extra event is the connect event.
327+
Assert.AreEqual(numSends + 1, m_ServerEvents.Count);
328+
329+
for (int i = 1; i <= numSends; i++)
330+
{
331+
Assert.AreEqual(NetworkEvent.Data, m_ServerEvents[i].Type);
332+
Assert.AreEqual(1024, m_ServerEvents[i].Data.Count);
333+
}
334+
335+
yield return null;
336+
}
308337
}
309338
}
310339
#endif

com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1335,7 +1335,7 @@ private IEnumerator ApprovalTimeout(ulong clientId)
13351335
}
13361336
}
13371337

1338-
private ulong TransportIdToClientId(ulong transportId)
1338+
internal ulong TransportIdToClientId(ulong transportId)
13391339
{
13401340
return transportId == m_ServerTransportId ? ServerClientId : m_TransportIdToClientIdMap[transportId];
13411341
}

0 commit comments

Comments
 (0)