How to recover from ChannelOption.MaxPendingBuffers data loss?

Based on the documentation here
https://docs.unity3d.com/ScriptReference/Networking.ChannelOption.MaxPendingBuffers.html

It states that in regards to reliable channels, if the buffer gets filled, there will be data loss.
What I think this means is that once the buffers get filled, any other reliable messages you try to send will just be ignored and get lost.

Imagine being in a game and then all of the sudden your internet goes out for 20 seconds or so, this seems to happen to me and in TF2 I get like 30 seconds or so before the game kicks me out. During my loss of internet connection, someone can be trying to send me a lot of reliable packets for whatever reason, but I cant receive them since I have no internet, so the buffer fills and now important reliable packets are being lost.
What am I suppose to do? Are we meant to now keep our own buffer that has no limits?

An Idea I had was to assume the client lost connection and just delete all reliable packets and if they happen to reconnect, Ill treat them as if they just joined the game. Ill send them a message letting them know to just delete any spawned objects they had and respawn all the objects I tell them to so that I know everything is up to date.
Is this a good way to go about doing this? Does unet already handle this in some way?

Hi,

I can’t find any documentation on how to use the “ChannelOption” Enum.
I am using the 2017.2 version of Unity. Is there a way to programmatically change the max number of the pending buffers (“MaxPendingBuffers”)?

Thank you,
Max

Its a HLAPI thing, you use it with NetworkConnections.
https://docs.unity3d.com/ScriptReference/Networking.NetworkConnection.SetChannelOption.html

Thank you for your reply!
Currently i am setting the"MaxPendingBuffers" option in the “OnClientConnect” function of the “NetworkManager” class, however, at this point i don’t know how big my data would be. Is there a way to know how many free “Pending Buffers” are left at some point (e.g. before sending yet another chunk of data)? Or is there a way to somehow clear (flush) the data that is pending to be sent in order to free the “Pending Buffers”?

Max

I dont see anything public that will let you know how many are pending.
I see a FlushChannels that will try to send all messages, however, if it cant for some reason then it will just put them back in the queue.

Here is the source code for NetworkConnection
https://bitbucket.org/Unity-Technologies/networking/src/6047ccb13d56c81668a3c946e8437025fe43be98/Runtime/NetworkConnection.cs?at=2017.2&fileviewer=file-view-default

Do you know what is the correlation between the number of “MaxPendingBuffers” and the size of a data?

First - I wrongly told you that the NetworkConnection has a method called SendInternalBuffer, when thats what ChannelBuffer has. Instead you would call NetworkConnection.FlushChannels() to try and send all channel messages to try and clear their buffers.
I’ll edit my other post to fix that.

Im not sure as to what you mean.

The NetworkConnection class SendBytes methods and what not dont instantly send the bytes if the sendDelay isnt 0. What it does is store the bytes or messages you are trying to send into its ChannelBuffers ChannelPackets, which I think each ChannelPacket can hold by default 1400 bytes of data. The ChannelBuffer tries to store as many messages you are trying to send into a single ChannelPacket. When the ChannelPacket gets full, it queues that ChannelPacket into a buffer to be sent later. It then starts a new ChannelPacket and starts storing any messages you are trying to send into that. The MaxPendingBuffers controls how many ChannelPackets you are willing to queue when they get full. By default I think its 16 that will be queued. When your queue of ChannelPackets reach the MaxPendingBuffers count, any new messages you try to send will be lost. After some time based on the sendDelay you set, the ChannelBuffer will call its SendInternalBuffer method that will try and send everything in the queue to the NetworkTransport. However, the NetworkTransport has its own queue, so if that gets full then the ChannelBuffer will requeue what it couldnt send.

So, since the ChannelBuffer I think can handle by default 1400 bytes per ChannelPacket (and that might be max allowed as well, but you can make lower), which can be changed with NetworkConnection.SetChannelOption(…, ChannelOption.MaxPacketSize,…), and it can handle a default of 16 MaxPendingBuffers, which can be changed with NetworkConnection.SetChannelOption(…, ChannelOption.MaxPendingBuffers,…), then that means it stores ((1400 * 16) - (100 * 16)) bytes.
The 100 bytes is what unity reserves for packet header stuff, although it seems when calling the SetChannelOption for MaxPacketSize they dont subtract it, but they do when initially creating the ChannelBuffer =/.
However, if you try to send a message larger then the MaxPacketSize, it will fail unless if you have the Channel set to fragmented, which can be done with NetworkConnection.SetChannelOption(…, ChannelOption.AllowFragmentation,…), which means the max size message you could send I think would be ((MaxPacketSize * MaxPendingBuffers) - (132 * MaxPendingBuffers)).
The 132 is 100 bytes unity reserves for header stuff, and 32 bytes unity reserves for Fragmented header stuff.

2 Likes

I would like to thank you, i couldn’t have asked for more detailed answer!

I have tried using the “NetworkConnection.FlushChannels” function but it wont help. My guess is that the amount of data i am trying to send is too big for the network so the messages just put back in the queue.

The data i need to send to a client is much bigger than 1300 * 512 (512 is the maximum value we can put in the “MaxPendingBuffers” configuration). Is there a way to know how many free/empty “ChannelPacket” i still have in the run time so i could postpone further sending of a data?

I dont think they have anything that shows you how much space there is left in the ChannelBuffer, but there is a way to know if there is no more room left. When you call any of the NetworkConnection send methods, such as SendBytes(…), it will return a bool. If it returns true, then everything is fine, but if it returned false, then that means it failed to send the message, possibly because its too full.

As for sending large amounts of data at once, such as a large file or something, unity doesnt really handle that as far as I know. That doesnt mean you cant get it to work though. I had to write my own fragmented message system with unitys LLAPI to be able to send any amount of data over time. You could probably get it to work with the HLAPI as well. Pretty much just send a “Starting large fragmented message” message, then over time slowly send the messages, and then when your done you could send a “Finished sending large fragmented message” message. Theres more to it then just that though, and speed is a concern as well.

Here is an answer someone made to solve this issue with unets HLAPI. However, they use the ReliableSequenced channel to do this, which might be slow (not tested though, so might be fine).

How I do it

The way I do it, based on memory, is I send a “Start Fragment” message that says the total amount of bytes I am going to be sending. I also have a sequence number in each packet to preserve order.
I then wait for the confirmation that they got the message. With the totalBytes, they are able to know how many fragments I will be sending since I think I have a constant value of like 400 bytes per fragment ( I use 400 because 400 + 100 for unity header stuff = 500 which is lower than what people say is a safe amount to avoid packet fragmentation over network, which is 500 or so). So now that they know how many fragments I am sending, they also know how many chunks I am sending, since I have a constant value of (250 * 8) fragments per chunk (The 8 is how many bit in each byte. I wanted it to be no greater then 400 due to reason above, so you can make it 300 or so if you want, doesnt really matter).
I send no more than (250 * 8) fragments of data over time, and then I request an “Ack” message from them to make sure they got everything. So the Ack they send is based on what sequence numbers they missed due to packet loss (since I am sending all this on a unreliable channel for speed) and they just send 250 bytes over and I read over each bit to see what bit was or wasnt set so I know what they missed. If they missed anything, I resend and request an Ack again.
Once they have everything in that chunk, I move to the next chunk and repeat the process until everything is sent. I dont think I even send a “Finished” message since they already know when we are done because I sent them the total bytes I will be sending.

1 Like