Skip to content

Commit

Permalink
fix(websocket): Use a buffer for most WS messages in WebGL client res…
Browse files Browse the repository at this point in the history
…ulting in 0 alloc for most messages (#848)

* fix(websocket): Use a buffer for most WS messages resulting in 0 alloc for most messages

* Use a configurable maximum message size

* Use the maximum message size on the server too

* Use <= instead of < for buffer.Length

* Show lengths in Exception message

Co-Authored-By: Katori <znorth@gmail.com>

* Show lengths in close message

Co-Authored-By: Katori <znorth@gmail.com>

* Allow messages of messagesize

Co-Authored-By: Katori <znorth@gmail.com>

* Allow messages of maxMessageSize

Co-Authored-By: Katori <znorth@gmail.com>

* Show message lengths in close message

Co-Authored-By: Katori <znorth@gmail.com>

* Don't initialize buffer if already done

Co-Authored-By: Katori <znorth@gmail.com>

* Always allocate buffer size on client construction

* Use a non-static buffer to avoid allocation issues
  • Loading branch information
Katori authored and miwarnec committed May 4, 2019
1 parent e22f527 commit 8967a20
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 17 deletions.
15 changes: 10 additions & 5 deletions Assets/Mirror/Runtime/Transport/Websocket/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class Client
public event Action Disconnected;
public event Action<Exception> ReceivedError;

const int MaxMessageSize = 1024 * 256;
int _maxMessageSize;
WebSocket webSocket;
CancellationTokenSource cancellation;

Expand All @@ -31,6 +31,11 @@ public class Client

Uri uri;

public Client(int MaxMessageSize)
{
_maxMessageSize = MaxMessageSize;
}

public async void Connect(Uri uri)
{
// not if already started
Expand Down Expand Up @@ -84,7 +89,7 @@ public async void Connect(Uri uri)

async Task ReceiveLoop(WebSocket webSocket, CancellationToken token)
{
byte[] buffer = new byte[MaxMessageSize];
byte[] buffer = new byte[_maxMessageSize];

while (true)
{
Expand Down Expand Up @@ -121,15 +126,15 @@ async Task<ArraySegment<byte>> ReadFrames(WebSocketReceiveResult result, WebSock

while (!result.EndOfMessage)
{
if (count >= MaxMessageSize)
if (count > _maxMessageSize)
{
string closeMessage = string.Format("Maximum message size: {0} bytes.", MaxMessageSize);
string closeMessage = $"Message size {count} exceeds maximum message size {_maxMessageSize}";
await webSocket.CloseAsync(WebSocketCloseStatus.MessageTooBig, closeMessage, CancellationToken.None);
ReceivedError?.Invoke(new WebSocketException(WebSocketError.HeaderError));
return new ArraySegment<byte>();
}

result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, count, MaxMessageSize - count), CancellationToken.None);
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, count, _maxMessageSize - count), CancellationToken.None);
count += result.Count;

}
Expand Down
20 changes: 15 additions & 5 deletions Assets/Mirror/Runtime/Transport/Websocket/ClientJs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,12 @@ public bool IsConnected
int m_NativeRef = 0;
readonly int id;

public Client()
byte[] buffer;

public Client(int BufferSize)
{
id = Interlocked.Increment(ref idGenerator);
buffer = new byte[BufferSize];
}

public void Connect(Uri uri)
Expand Down Expand Up @@ -106,10 +109,17 @@ public static void OnClose(int id)
[MonoPInvokeCallback(typeof(Action))]
public static void OnData(int id, IntPtr ptr, int length)
{
byte[] data = new byte[length];
Marshal.Copy(ptr, data, 0, length);

clients[id].ReceivedData(new ArraySegment<byte>(data));
byte[] buffer = clients[id].buffer;
if (length <= buffer.Length)
{
Marshal.Copy(ptr, buffer, 0, length);
ArraySegment<byte> data = new ArraySegment<byte>(buffer, 0, length);
clients[id].ReceivedData(data);
}
else
{
clients[id].ReceivedError(new Exception($"Message size {length} is larger than specified max message length {buffer.Length}"));
}
}
#endregion
}
Expand Down
15 changes: 10 additions & 5 deletions Assets/Mirror/Runtime/Transport/Websocket/Server.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class Server
public event Action<int> Disconnected;
public event Action<int, Exception> ReceivedError;

const int MaxMessageSize = 256 * 1024;
int _maxMessageSize;

// listener
TcpListener listener;
Expand Down Expand Up @@ -84,6 +84,11 @@ public class SslConfiguration
public bool CheckCertificateRevocation;
}

public Server(int MaxMsgSize)
{
_maxMessageSize = MaxMsgSize;
}

public async void Listen(int port)
{
try
Expand Down Expand Up @@ -178,7 +183,7 @@ async Task ReceiveLoopAsync(WebSocket webSocket, CancellationToken token)
int connectionId = NextConnectionId();
clients.Add(connectionId, webSocket);

byte[] buffer = new byte[MaxMessageSize];
byte[] buffer = new byte[_maxMessageSize];

try
{
Expand Down Expand Up @@ -232,15 +237,15 @@ async Task<ArraySegment<byte>> ReadFrames(int connectionId, WebSocketReceiveResu

while (!result.EndOfMessage)
{
if (count >= MaxMessageSize)
if (count > _maxMessageSize)
{
string closeMessage = string.Format("Maximum message size: {0} bytes.", MaxMessageSize);
string closeMessage = $"Message size {count} exceeds maximum message size {_maxMessageSize}";
await webSocket.CloseAsync(WebSocketCloseStatus.MessageTooBig, closeMessage, CancellationToken.None);
ReceivedError?.Invoke(connectionId, new WebSocketException(WebSocketError.HeaderError));
return new ArraySegment<byte>();
}

result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, count, MaxMessageSize - count), CancellationToken.None);
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, count, _maxMessageSize - count), CancellationToken.None);
count += result.Count;

}
Expand Down
10 changes: 8 additions & 2 deletions Assets/Mirror/Runtime/Transport/Websocket/WebsocketTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ namespace Mirror.Websocket
public class WebsocketTransport : Transport
{

protected Client client = new Client();
protected Server server = new Server();
protected Client client;
protected Server server;

public int port = 7778;

Expand All @@ -20,14 +20,20 @@ public class WebsocketTransport : Transport

public string CertificatePassword;

public int MaxMessageLength = 16384;

public WebsocketTransport()
{
server = new Server(MaxMessageLength);

// dispatch the events from the server
server.Connected += (connectionId) => OnServerConnected.Invoke(connectionId);
server.Disconnected += (connectionId) => OnServerDisconnected.Invoke(connectionId);
server.ReceivedData += (connectionId, data) => OnServerDataReceived.Invoke(connectionId, data);
server.ReceivedError += (connectionId, error) => OnServerError.Invoke(connectionId, error);

client = new Client(MaxMessageLength);

// dispatch events from the client
client.Connected += () => OnClientConnected.Invoke();
client.Disconnected += () => OnClientDisconnected.Invoke();
Expand Down

0 comments on commit 8967a20

Please sign in to comment.