James-Frowen/SimpleWebTransport

Messages larger than 65535 bytes

Closed this issue · 28 comments

Any idea how to solve this limitation?
Normally this is fine, however, in our use case, we send a lot of game data at the start when a client connects since we stream pretty much the entire game's content, much of which is in the Megabytes of size.

I think for message larger than 65k you'll have to manually split them up and send them as separate message. They should still arrive on the other side in the right order, but you might want to add a sequence number to each part just to be sure.

I forget how many message SWT can buffer at once, so if it the size is big enough you might also need send in parts over multiple seconds.

In our case splitting the work into multiple packets is more of a last resort since it would require a lot of work rewriting our structure. Websockets themselves we know can handle way more than 65k, it seems it's just SimpleWebTransport is restricted to it.

I think the websocket protocol has 2 ways to handle large message, Increased payload size, and fragmentation. But I left other out of SimpleWebTransport because for games those.

I think it would be difficult for SimpleWebTransport to handle large message itself because of the way it buffers sending and receiving. The receive buffer is created at the start so needs to know the max size of the message.

It might be possible for me to add a helper that will split up a large array into smaller packets.

In our case, we probably don't need buffers and whatnot to reduce allocations.
We could generate the buffers when needed.
Do you know if there's anything specific to Websocket that's restricting the packet size in SimpleWebTransport or is it solely just Buffers and the structure of SimpleWebTransport not being able to handle large packets?
I see in the Header there are checks to what size they are, is that just a matter of improving the packet size or is the header something that needs to be like that for Websockets? We don't need extreme efficiency, so in our case could we rewrite the header to just be a single int aka 4 bytes. and drop all the buffers?

the websocket header has the option to be 8 byte, This part here would need to be changed to allow it

else if (lenByte == Constants.UlongPayloadLength)
{
throw new InvalidDataException("Max length is longer than allowed in a single message");
}

this section described about the websocket header https://datatracker.ietf.org/doc/html/rfc6455#section-5.2

Sending it should be easy, but receiving it might be more complicated because it would need to allocate a buffer large enough for the message before it can read it from the socket.

I'll take another look at the code and see if I can add an easy way to do thing.

I've got some stuff working on #2 would be great if you could test it.

so far i've got it working when sending to/from c#, but not when sending from JS. Seems like sending from JS is fragmenting the message instead of sending it as a larger message.

Thanks! I'll test it in an hour or two!

Alright so Sent some data from a Non-Unity/Mirror c# server to a Unity WebGL Client.
The connection worked, Sending some small packets worked, the moment we sent a large packet the client never got anything after that point, even small packets, Server seemed to be fine never complained or said anything. Gonna keep looking to see if it was something on our side of things, but everything else is working so it's most likely SimpleWebTransports client never properly processed the data.

Oh It may have been us, We didn't have the "Fixed Receiving" Commit

Unfortunately, that commit didn't fix it.
So I'm getting packets just the first Large packet coming in seems to break packets from that point onwards, and I never actually even get the large packet.
No Client or Server Errors, except after a few seconds the server disconnects the client due to being unresponsive
image

I think that disconnect is just from it timing out from not sending anything.

It might that that the data is taking too long so it timing out before the whole thing reaches the other side. Can you try with something smaller (anything over 70k bytes), just to check it is working. If that works then you might need to increase the send/receive timeout values so it doesn't timeout on your data.

The timeouts have already been set to 1 minute, None of this data is even 1MB, all of it is around the 100kb range Largest is around 200kb.
So something is strange for it to fail to send 100kb in 1 minute.
I'll try to set up a better test environment and control the byte count today.

The WebGL client doesn't use the RecieveLoop and all that stuff, it feeds the message straight to the RecieveQueue which is then processed by the game immediately after.
So if it's client-sided it must be in the Javascript library, since the game is never getting the packet, there's a bit going on in there I'm not super knowledgeable on Javascript so I'm not sure if anything there needs to be updated to support a new header size.

image
This is a list of all the Messages the server is sending in bytes. I put the Write in the SendLoop next to the "stream.Write(writeBuffer, 0, length);"
So this is the Data that should be actually getting sent.
The client receives all the ones up to the "626" message, after that no more messages are received.
So it's not even getting to the 200kb one, the server is sending it, just the client never receives it.
Here's is what the client receives:
image
The 4583 bytes message was never received, that message would be logged there as "SUpdateAllTitles"

Just to confirm, are you using the new function AllowLargeMessage and SendLargeMessage?

I've just tested with this and it seems to receive up to 200kb ok

byte[] bytes = new byte[size];
var random = new System.Random();
random.NextBytes(bytes);

var segment = new ArraySegment<byte>(bytes);

server.AllowLargeMessage(1, true);
server.SendLargeMessage(1, segment);

Oh, I was using AllowLargeMessage, but was unaware of SendLargeMessage!
However using it doesn't seem to have made a difference

What's weird is that it's so suddenly stopping, it hasn't even got up to the Large message, the client stops processing before it. There might be something else wrong, gonna keep investigating.

Gonna revert to before large messages and examine the client log, see if its stopping at the same point, if so this may be a seperate issue.

I've done some testing, I am see an issue when the client disconnect if it doesn't send any messages.

SimpleWebTransport doesn't have its own ping/pong to handle keep connection alive, Do you have your client sending ping/pong to keep it alive?

I can't seem to find a way to set the timeout for the webgl version, I have read that it is 300 seconds which should be enough for this stuff.

tested it with webgl client sending keep alive message, server is managed to successfully send all sizes up to 200kb (where i stopped the test), and I assume it'd work for higher numbers too

There is a Ping pong happening, which is working.
Reverting to before adding large message support is even more broken, it fails on the first packet since the server disconnects the client probably due to large messages or something.

I'll keep testing were using SWT in a very different way than it was intended, so it might be something with our setup.
If you want we could hire you to come to take a closer look into our codebase

Tried doing an infinite ping/pong with 50kb of data, seemed fine, on our client/server setup.
Same with 70kb, 100kb, and even 1mb.
So it's something specific we're doing, some specific data were sending or something.
Were gonna slowly step through all our login packets untill it fails.
-Update, Didnt find anything doing that

Runtime.dynCall in the jslib might be broken.
Debugging it in Chrome the Websocket on the client is indeed receiving everything.
Doing a Log inside of the jslib in the OnMessage event shows that every packet is being recieved and processed correctly on the websocket, its just not being sent to c#.

image
Gets some packets, then just stops
"Recieved Data From" is from the jslib
The rest are all in c#

what version of unity are you using?

Sorry for the late reply. having renovations done at my house.
Im using Unity 2019.4.0f1

I've been testing on 2019.4.14 without problem. Might be a problem with the unity version, can you try a different version and see if it works any better?

tried 2020.2.2f same issue, Do you want to take a look at our code? We can set up a Discord group and I can share the project with you. There's something clearly wrong but i haven't a clue what's causing it, all your test cases work in our environment. its just sending Our data instead of test data that breaks it. So its either something with our Server/Client or with the Data were sending

Sent, My username is Wulferis.