Reputation: 122
I'm bringing up a TCP async server using C# and I'm struggling on the right way to receive the correct amount of data from the server. My question is this one: due to the nature of TCP being a STREAM protocol, there is no delimiter at the end of each message received so the only thing I can do is add at the beginning of the message the upcoming message size and react consequently; the thing is, how can I recv and be sure that I'm not reading the "next" message in the stream?
My pseudo code looks like this:
// 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 get the whole message, could the next recv go far beyond the boundaries of the first and possibly add some unwanted bytes to the message I'm actually wanting to read?
Upvotes: 0
Views: 866
Reputation: 3644
As you observe, TCP provides no native framing. Worse, the a sync I/O events are reported for each TCP segment received from the network stack, and may not even match the send calls.
While building up the contents of the received stream, you will need to use one of:
The choice will generally depend on non-technical factors.
Upvotes: 1
Reputation: 116108
Here is how it can be done 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);
}
}
Upvotes: 2
Reputation: 50110
if you know the pending length (which you say you do based on protocol header of some sort) then only read the known pending length. IN your case Marshal.Sizeof(header) - state.bytesReceived
Upvotes: 0