j00hi
j00hi

Reputation: 5931

Consecutive calls to NetworkStream.Write - does it make a difference?

Consider the following two methods for sending data and the one method for reading data:

public static void SendConsecutively(this NetworkStream stream)
{
    byte[] header = {1, 2, 3, 4};
    byte[] message = {5, 6, 7, 8, 9, 10};
    stream.Write(header, 0, header.Length);
    stream.Write(message, 0, message.Length);
}

public static void SendAllInOne(this NetworkStream stream)
{
    byte[] headerAndMessage = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    stream.Write(headerAndMessage, 0, headerAndMessage.Length);
}

public static byte[] ReadMessage(this NetworkStream stream)
{
    byte[] data = new byte[10];
    int bytesRead = stream.Read(data, 0, 10);
    return data;        
}

Does it make a difference whether or not to split the data into two chunks (like in SendConsecutively) compared to not splitting the data (like in SendAllInOne)?

The thing is, in my tests, ReadMessage always reads 10 bytes. And the 3rd-party-server, which I am sending header and message to (but do not know how it is implemented), usually also receives 10 bytes. But sometimes - in rare cases - they tell me that the 3rd-party-server receives only 4 bytes. I have to deal with the problem, that that server only received 4 bytes, despite I am sure that I have sent 10 byte using SendConsecutively. (Because there was no Exception in my logs, which means that both stream.Write call have been issued, for sure.)

So, one question is: What happens between two consecutive stream.Write calls? I thought: nothing, because the documentation to NetworkStream.Flush says: "The Flush method implements the Stream.Flush method; however, because NetworkStream is not buffered, it has no affect on network streams." [1]

I am provided with Wireshark logs where a TCP packet can be seen that only consists of the four header bytes. Does each stream.Write call produce one TCP packet? But then again, do I even have to care about how my data is split into TCP packets? Because large messages will be segmented into multiple TCP packets anyways, won't they? (Yes, there are also large messages sent which are way larger than the 6 bytes I have used for the example above - e.g.: 4 bytes header + 3000 bytes message)

Is my SendConsecutively method flawed in any way which could cause such problems on the receiver-side (Also considering that message could be 3000 bytes, not just 6 like in the example code)?

Could it be, that it is rather a problem of the 3rd-party-server? If that server would be implemented just like the ReadMessage method above, it could be a problem. Because sometimes (I think this must happen, when messages get segmented over TCP), ReadMessage would read fewer bytes than have been sent and return a smaller number in bytesRead than the actual message's length. The rest of the message is available on the stream (a few milliseconds?) later. Any thoughts on this?

[1] MSDN, NetworkStream.Flush, https://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.write(v=vs.110).aspx

Upvotes: 1

Views: 1371

Answers (1)

Alex Netkachov
Alex Netkachov

Reputation: 13522

The network stream is an abstraction - it hides the implementation details. Normally the developer can ignore how exactly the sent data is transferred - in multiple packets or as single one, which protocol is used, etc. However, it is important (at least for me, and probably for everyone working with the network streams) to remember a few facts about the I/O:

  • usually the sequential order of the data is guaranteed (it gets received in the order in which it was written)
  • it can be interrupted at any time (by throwing exception)
  • synchronous read may block indefinitely (it is not in most of the cases, but there is a chance)
  • I/O is much slower than other instructions
  • the size of data chunks is different on different ends of the stream
  • writing small chunks frequently may be less efficient than writing a one big chunk (due to additional payload caused by underlying transport layers)
  • reading data slowly than writing it may overflow internal buffers

Most of these facts do not depend on the technology used on both ends of the wire. It can be Java on one side and .NET on another - normally it does not matter as long as the channel is just a byte stream.

With this in mind, the answer to your questions:

Does it make a difference whether or not to split the data into two chunks (like in SendConsecutively) compared to not splitting the data (like in SendAllInOne)? ... What happens between two consecutive stream.Write calls? ... Is my SendConsecutively method flawed in any way which could cause such problems on the receiver-side (Also considering that message could be 3000 bytes, not just 6 like in the example code)?

There can be difference depending on the internal implementation of the socket. At may result in two sequential sends or be merged into one send. NetworkStream sends calls Send of the underlying socket. This call is converted to the socket's Send() and then it really depends on the implementation of WinSock's send.

For the receiver it does not matter how exactly the data was sent. Network layer can split data in different chunks without any correlation to the size of sent chunks.

Upvotes: 1

Related Questions