Async TCP Server - Posting Tips

I have an asynchronous TCP socket server in C # that I created using the TcpListener / TcpClient covers for Socket. I am new to networking in general, so I did not know how TCP handles sent data and how it does not preserve message boundaries. After a little research, I think I came up with a lasting solution, but I wonder if anyone has any more tips for me.

Currently, the data I'm sending is the bytes [] of the serial object of the class using protobuf (Google data exchange library) https://code.google.com/p/protobuf/

After serializing my data and before sending it, I decided to add 4 Int32 bytes at the beginning of the byte array. My idea is that when a packet is sent to the server, it will analyze Int32, and then wait until it receives this number of bytes before doing anything with the data, otherwise just call BeginRead again.

Here is the code he worked with before writing data, and it seems to work fine, but I'm open to any performance suggestions:

public byte[] PackByteArrayForSending(byte[] data) { // [0-3] Packet Length // [3-*] original data // Get Int32 of the packet length byte[] packetLength = BitConverter.GetBytes(data.Length); // Allocate a new byte[] destination array the size of the original data length plus the packet length array size byte[] dest = new byte[packetLength.Length + data.Length]; // Copy the packetLength array to the dest array Buffer.BlockCopy(packetLength, 0, dest, 0, packetLength.Length); // Copy the data array to the dest array Buffer.BlockCopy(data, 0, dest, packetLength.Length, data.Length); return dest; } 

I am a bit stuck on the server. I am reading the packetLength variable using Buffer.BlockCopy to copy the first 4 bytes and then BitConverter.ToInt32 to read the length I should get. I'm not sure if I should constantly read the input to a client-specific Stream object, or just use a while loop. Here is an example of the code that I have on the server:

 NetworkStream networkStream = client.NetworkStream; int bytesRead = networkStream.EndRead(ar); if (bytesRead == 0) { Console.WriteLine("Got 0 bytes from {0}, marking as OFFLINE.", client.User.Username); RemoveClient(client); } Console.WriteLine("Received {0} bytes.", bytesRead); // Allocate a new byte array the size of the data that needs to be deseralized. byte[] data = new byte[bytesRead]; // Copy the byte array into the toDeserialize buffer Buffer.BlockCopy( client.Buffer, 0, data, 0, bytesRead); // Read the first Int32 tp get the packet length and then use the byte[4] to get the packetLength byte[] packetLengthBytes = new byte[4]; Buffer.BlockCopy( data, 0, packetLengthBytes, 0, packetLengthBytes.Length); int packetLength = BitConverter.ToInt32(packetLengthBytes, 0); // Now what do you recommend? // If not all data is received, call // networkStream.BeginRead(client.Buffer, 0, client.Buffer.Length, ReadCallback, client); // and preserve the initial data in the client object 

Thanks for your time and advice, I look forward to learning more about this topic.

+4
source share
1 answer

TCP ensures that bytes packed into a stream at one end will drop out from the other end in the same order and without loss or duplication. Do not expect anything else, of course, do not support entities larger than bytes.

Usually I added a header with the message length, type and subtype. Often there is a correlation identifier to match requests for responses.

The basic pattern is to get bytes and add them to the buffer. If there is enough data in the buffer to contain the message header, retrieve the message length. If there is enough data in the buffer to contain the message, delete the message from the buffer and process it. Repeat with any remaining data until complete messages are processed. Depending on your application, this may be a waiting point when reading or checking the stream for additional data. Some applications may need to continue reading a stream from a separate stream to avoid sender throttling.

Note that you cannot assume that you have a full message length field. You can have three bytes after processing the message and cannot extract int .

Depending on your messages, using a circular buffer may be more efficient, rather than shuffling any remaining bytes each time the message is processed.

+2
source

All Articles