Improved WebSocket receive performance and reduced allocations
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
using Discord.Extensions;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -12,7 +11,7 @@ namespace Discord.Net.WebSockets
|
|||||||
{
|
{
|
||||||
public class DefaultWebSocketClient : IWebSocketClient
|
public class DefaultWebSocketClient : IWebSocketClient
|
||||||
{
|
{
|
||||||
public const int ReceiveChunkSize = 12 * 1024; //12KB
|
public const int ReceiveChunkSize = 16 * 1024; //16KB
|
||||||
public const int SendChunkSize = 4 * 1024; //4KB
|
public const int SendChunkSize = 4 * 1024; //4KB
|
||||||
private const int HR_TIMEOUT = -2147012894;
|
private const int HR_TIMEOUT = -2147012894;
|
||||||
|
|
||||||
@@ -137,50 +136,64 @@ namespace Discord.Net.WebSockets
|
|||||||
private async Task RunAsync(CancellationToken cancelToken)
|
private async Task RunAsync(CancellationToken cancelToken)
|
||||||
{
|
{
|
||||||
var buffer = new ArraySegment<byte>(new byte[ReceiveChunkSize]);
|
var buffer = new ArraySegment<byte>(new byte[ReceiveChunkSize]);
|
||||||
var stream = new MemoryStream();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (!cancelToken.IsCancellationRequested)
|
while (!cancelToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
WebSocketReceiveResult result = null;
|
WebSocketReceiveResult socketResult = await _client.ReceiveAsync(buffer, cancelToken).ConfigureAwait(false);
|
||||||
do
|
byte[] result;
|
||||||
|
int resultCount;
|
||||||
|
|
||||||
|
if (socketResult.MessageType == WebSocketMessageType.Close)
|
||||||
{
|
{
|
||||||
if (cancelToken.IsCancellationRequested) return;
|
var _ = Closed(new WebSocketClosedException((int)socketResult.CloseStatus, socketResult.CloseStatusDescription));
|
||||||
|
return;
|
||||||
try
|
|
||||||
{
|
|
||||||
result = await _client.ReceiveAsync(buffer, cancelToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (Win32Exception ex) when (ex.HResult == HR_TIMEOUT)
|
|
||||||
{
|
|
||||||
throw new Exception("Connection timed out.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.Count > 0)
|
|
||||||
stream.Write(buffer.Array, 0, result.Count);
|
|
||||||
}
|
}
|
||||||
while (result == null || !result.EndOfMessage);
|
|
||||||
|
|
||||||
var array = stream.ToArray();
|
if (!socketResult.EndOfMessage)
|
||||||
stream.Position = 0;
|
|
||||||
stream.SetLength(0);
|
|
||||||
|
|
||||||
switch (result.MessageType)
|
|
||||||
{
|
{
|
||||||
case WebSocketMessageType.Binary:
|
//This is a large message (likely just READY), lets create a temporary expandable stream
|
||||||
await BinaryMessage(array, 0, array.Length).ConfigureAwait(false);
|
using (var stream = new MemoryStream())
|
||||||
break;
|
{
|
||||||
case WebSocketMessageType.Text:
|
stream.Write(buffer.Array, 0, socketResult.Count);
|
||||||
string text = Encoding.UTF8.GetString(array, 0, array.Length);
|
do
|
||||||
await TextMessage(text).ConfigureAwait(false);
|
{
|
||||||
break;
|
if (cancelToken.IsCancellationRequested) return;
|
||||||
case WebSocketMessageType.Close:
|
socketResult = await _client.ReceiveAsync(buffer, cancelToken).ConfigureAwait(false);
|
||||||
var _ = Closed(new WebSocketClosedException((int)result.CloseStatus, result.CloseStatusDescription));
|
stream.Write(buffer.Array, 0, socketResult.Count);
|
||||||
return;
|
}
|
||||||
|
while (socketResult == null || !socketResult.EndOfMessage);
|
||||||
|
|
||||||
|
//Use the internal buffer if we can get it
|
||||||
|
resultCount = (int)stream.Length;
|
||||||
|
ArraySegment<byte> streamBuffer;
|
||||||
|
if (stream.TryGetBuffer(out streamBuffer))
|
||||||
|
result = streamBuffer.Array;
|
||||||
|
else
|
||||||
|
result = stream.ToArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Small message
|
||||||
|
resultCount = socketResult.Count;
|
||||||
|
result = buffer.Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socketResult.MessageType == WebSocketMessageType.Text)
|
||||||
|
{
|
||||||
|
string text = Encoding.UTF8.GetString(result, 0, resultCount);
|
||||||
|
await TextMessage(text).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
await BinaryMessage(result, 0, resultCount).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Win32Exception ex) when (ex.HResult == HR_TIMEOUT)
|
||||||
|
{
|
||||||
|
var _ = Closed(new Exception("Connection timed out.", ex));
|
||||||
|
}
|
||||||
catch (OperationCanceledException) { }
|
catch (OperationCanceledException) { }
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user