What Is the Correct Way to Read from Networkstream in .Net

What is the correct way to read from NetworkStream in .NET

Setting the underlying socket ReceiveTimeout property did the trick. You can access it like this: yourTcpClient.Client.ReceiveTimeout. You can read the docs for more information.

Now the code will only "sleep" as long as needed for some data to arrive in the socket, or it will raise an exception if no data arrives, at the beginning of a read operation, for more than 20ms. I can tweak this timeout if needed. Now I'm not paying the 20ms price in every iteration, I'm only paying it at the last read operation. Since I have the content-length of the message in the first bytes read from the server I can use it to tweak it even more and not try to read if all expected data has been already received.

I find using ReceiveTimeout much easier than implementing asynchronous read... Here is the working code:

string SendCmd(string cmd, string ip, int port)
{
var client = new TcpClient(ip, port);
var data = Encoding.GetEncoding(1252).GetBytes(cmd);
var stm = client.GetStream();
stm.Write(data, 0, data.Length);
byte[] resp = new byte[2048];
var memStream = new MemoryStream();
var bytes = 0;
client.Client.ReceiveTimeout = 20;
do
{
try
{
bytes = stm.Read(resp, 0, resp.Length);
memStream.Write(resp, 0, bytes);
}
catch (IOException ex)
{
// if the ReceiveTimeout is reached an IOException will be raised...
// with an InnerException of type SocketException and ErrorCode 10060
var socketExept = ex.InnerException as SocketException;
if (socketExept == null || socketExept.ErrorCode != 10060)
// if it's not the "expected" exception, let's not hide the error
throw ex;
// if it is the receive timeout, then reading ended
bytes = 0;
}
} while (bytes > 0);
return Encoding.GetEncoding(1252).GetString(memStream.ToArray());
}

C# Networkstream.read()

No, it will not block.
The Read operation reads as much data as is available, up to the number of bytes specified by the size parameter.
Source: http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.read.aspx

Given it will not wait for that extra 1 byte, if you are expecting it, you should implement a loop to continue reading the stream. You can exit the loop however you feel best.


UPDATE:
I was wrong when I stated "There's no blocking at all. If no data is available for reading, the Read method returns 0", but I was correct when I stated that it wouldn't block waiting to fill the entire buffer which it the scenario described in Kazoom's question.

Updated to demonstrate that NetworkStream.Read blocks waiting for the first byte, but does not block waiting to fill the entire buffer.

Create to console projects

On one end, you have the listener:


IPEndPoint ep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345);
TcpListener listener = new TcpListener(ep);
listener.Start();
TcpClient client = listener.AcceptTcpClient();
NetworkStream s = client.GetStream();
byte[] buffer = new byte[32];
Console.WriteLine(s.Read(buffer, 0, 32));
Console.WriteLine("Press any key to continue...");
Console.Read();

On the other end, we send only one byte:


IPEndPoint ep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345);
TcpClient client = new TcpClient();
client.Connect(ep);
client.GetStream().Write(new byte[] { 60 }, 0, 1);
Console.WriteLine("Press any key to continue...");
Console.Read();

Both sides will run until they reach Console.Read(). Notice that the listener does not block on Read.

The listener will print "1"

C# - NetworkStream.Read - correct way to fill the entire buffer (or throw exceptions otherwise)

Taking an evidence based approach to establishing the facts... If you look at the source code for Stream.CopyTo, which relies on method Stream.InternalCopyTo you'll see the following code for copying one stream to another:

byte[] buffer = new byte[bufferSize];
int read;
while ((read = Read(buffer, 0, buffer.Length)) != 0)
destination.Write(buffer, 0, read);

This makes it absolutely clear to me that 0 represents the end of stream and not that "there is nothing to read now".

Any suggestion that a return value of 0 has a second meaning is incorrect, as this would make the implementation of CopyTo incorrect.

In short, to read a stream to the end, carry on reading until Read returns 0 (or an exception is thrown).

Reading data from NetworkStream

As I can see you put in here some methods which you have created on your own and I suppose you don't have any problems in reading data.
What you really need is some kind of handle at start of a new file because your socket should receiving data continiously.

It obviously depends on which image format you want to use, what you want to send.
For PNG files you should read chapter 12 of this pdf file. Especially section 12. You could look for signature and extracting data from buffer if you hit sequence like this

(decimal)             137 80 78 71 13 10 26   10
(hexadecimal) 89 50 4e 47 0d 0a 1a 0a
(ASCII C notation) \211 P N G \r \n \032 \n

Then you need to simply recreate png file. You must do the same with every type of file you send.

If you do not care about content of a file you could've create some data frame and put the data into payload (compressed and encrypted). Create own signature blahblahblah.
The simplest example is Modbus. You can read more about it here.

edit

Reading the data of tcp stream is very simple. Just look at this code.

var receiveBufferSize = 512; // amount of bytes to read
try
{
var buffer = new byte[receiveBufferSize];
_tcpClient.GetStream().Read(buffer, 0, receiveBufferSize);
// handle the received data chunk
}
catch (Exception e)
{
// handle the error
Debug.WriteLine(ex.Message);
}

How to get all data from NetworkStream

The problem with your code is that you will not get all the data if the data size is bigger than the buffer size (1024 bytes in your case) so you have to Read the stream inside the loop. Then you can Write all the data inside a MemoryStream until the end of the NetworkStream.


      string str;
using (NetworkStream stream = client.GetStream())
{
byte[] data = new byte[1024];
using (MemoryStream ms = new MemoryStream())
{

int numBytesRead ;
while ((numBytesRead = stream.Read(data, 0, data.Length)) > 0)
{
ms.Write(data, 0, numBytesRead);

}
str = Encoding.ASCII.GetString(ms.ToArray(), 0, (int)ms.Length);
}
}

When does TcpClient's NetworkStream finish one read operation?

It reads all the data that is available on the networkstream or when the buffer is full. Whichever comes first. You have already noticed this behaviour.

So you will need to process all the bytes and see whether the message is complete. You do this by framing a message. See .NET question about asynchronous socket operations and message framing on how you can do this.

As for the timeout question, if assuming you are asking whether a beginread has a timeout, I would say no, because it is just waiting for data to arrive on the stream and put it into a buffer, after which you can process the incoming bytes.

The number of bytes available on the read action depends on things like your network (e.g. latency, proxy throttling) and the client sending the data.

BeginRead behaviour summary:

  1. Call BeginRead(); -> Waiting for bytes to arrive on the stream......
  2. 1 byte or more have arrived on the stream
  3. Start putting the byte(s) from step 2 into the buffer that was given
  4. Call EndRead(); -> The byte(s) within the buffer can be processed by EndRead();
  5. Most common practice is to repeat all these steps again.

C# NetworkStream.Read oddity

Not sure if this is helpful or not but with HTTP 1.1 the underlying connection to the server might not be closed so maybe the stream doesn't get closed either? The idea being that you can reuse the connection to send a new request. I think you have to use the content-length. Alternatively use the WebClient or WebRequest classes instead.

Reading from a NetworkStream. What bytesize should i be using?

Make it too small and you'll lose some efficiency from .NET having to make an operating system call more frequently to refill the buffer. Make it too big and you waste some memory.

It is not that critical but 256 is on the low end. A very common I/O buffer size is 4096 bytes. It is a magic number in Windows, the size of a memory page. Albeit that the buffer will exactly straddle one page only by accident.

Writing/reading string through NetworkStream (sockets) for a chat

You're best using the BinaryReader/BinaryWriter classes to correctly format and read out data. This removes the need to process it yourself. For example in the client do:

BinaryWriter writer = new BinaryWriter(clientSocket.GetStream());
writer.Write(str);

And in the server:

BinaryReader reader = new BinaryReader(clientSocket.GetStream());
Console.WriteLine(reader.ReadString());


Related Topics



Leave a reply



Submit