Receive the correct amount of TCP data

I am running a TCP asynchronous server using C #, and I am struggling to get the correct amount of data from the server. My question is this: due to the fact that TCP is a STREAM protocol, there is no separator at the end of each received message, so the only thing I can do is add the size of the upcoming message at the beginning of the message and respond accordingly; the thing is, how can I recv and be sure that I am not reading the β€œnext” message in the stream?

My pseudo code is as follows:

// client accepted, begin receiving client.BeginReceive(state.buffer, 0, StateObject.bufferSize, 0, new AsyncCallback(_cbck_Read), state); private void _cbck_Read(IAsyncResult ar) { StateObject state = (StateObject)ar.AsyncState; Socket client = state.clientSocket; int bytesRead = client.EndReceive(ar); // if data have been received if (bytesRead > 0) { state.bytesReceived += bytesRead; // no message header received so far go on reading if (state.bytesReceived < Marshal.SizeOf(header)) { client.BeginReceive(state.buffer, 0, StateObject.bufferSize, 0, new AsyncCallback(_cbck_Read), state); } // ... go ahead reading 

If the first recv does not receive the whole message, can the next recv go far beyond the first and possibly add some unwanted bytes to the message that I really want to read?

0
source share
3 answers

Here's how to do it using "async / await" with some helper extension methods.

 Socket s = new Socket(SocketType.Stream, ProtocolType.Tcp); await s.ConnectTaskAsync("stackoverflow.com", 80); await s.SendTaskAsync(Encoding.UTF8.GetBytes("GET /\r\n\r\n")); var buf1 = await s.ReceiveExactTaskAsync(100); //read exactly 100 bytes Console.Write(Encoding.UTF8.GetString(buf1)); var buf2 = await s.ReceiveExactTaskAsync(100); //read exactly 100 bytes Console.Write(Encoding.UTF8.GetString(buf2)); 

 public static class SocketExtensions { public static Task<int> ReceiveTaskAsync(this Socket socket, byte[] buffer, int offset, int count) { return Task.Factory.FromAsync<int>( socket.BeginReceive(buffer, offset, count, SocketFlags.None, null, socket), socket.EndReceive); } public static async Task<byte[]> ReceiveExactTaskAsync(this Socket socket, int len) { byte[] buf = new byte[len]; int totalRead = 0; do{ int read = await ReceiveTaskAsync(socket, buf, totalRead, buf.Length - totalRead); if (read <= 0) throw new SocketException(); totalRead += read; }while (totalRead != buf.Length); return buf; } public static Task ConnectTaskAsync(this Socket socket, string host, int port) { return Task.Factory.FromAsync( socket.BeginConnect(host, port, null, null), socket.EndConnect); } public static Task SendTaskAsync(this Socket socket, byte[] buffer) { return Task.Factory.FromAsync<int>( socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, null, socket), socket.EndSend); } } 
+2
source

As you noticed, TCP does not have built-in framing. Worse, I / O synchronization events are reported for each TCP segment received from the network stack, and may not even correspond to send calls.

When creating the contents of the resulting stream, you will need to use one of the following elements:

  • fixed-length messages, possibly with some kind of leading type discrimination,
  • pointer to the maximum length of the prefix or
  • unambiguous message separator.

The choice usually depends on non-technical factors.

+1
source

if you know the expected length (which, as you say, you use based on a protocol header of some kind), then just read the known expected length. In your case, Marshal.Sizeof (header) is state.bytesReceived

0
source

Source: https://habr.com/ru/post/1486085/


All Articles