NetworkStream BeginRead only reads once? - c#

I have some pretty simple code that reads lines from a network stream that it connects to. In the code example only one line is every read and it doesn't carry on getting more from the server.
What is wrong?
byte[] readBuffer = new byte[1024];
byte[] tempBuff = new byte[1024];
int tempBuffSize = 0;
private void btnConnect_Click(object sender, EventArgs e)
{
TcpClient tcpClient = new TcpClient("192.168.1.151", 5505);
NetworkStream stream = tcpClient.GetStream();
stream.BeginRead(readBuffer, 0, 1024, readHandler, tcpClient);
}
void readHandler(IAsyncResult result)
{
TcpClient tcpClient = (TcpClient)result.AsyncState;
int dataLen = tcpClient.GetStream().EndRead(result);
int currStart = 0;
int currEnd = -1;
for (int i = 0; i < dataLen; i++)
{
if (readBuffer[i] == '\r' && i < (readBuffer.Length - 1) &&
readBuffer[i + 1] == '\n')
{
// Set the end of the data
currEnd = i - 1;
// If we have left overs from previous runs:
if (tempBuffSize != 0)
{
byte[] joinedData = new byte[tempBuffSize + (currEnd - currStart + 1)];
Array.Copy(tempBuff, 0, joinedData, 0, tempBuffSize);
Array.Copy(readBuffer, currStart, joinedData, tempBuffSize, (currEnd - currStart + 1));
System.Text.Encoding enc = System.Text.Encoding.ASCII;
string myString = enc.GetString(joinedData);
System.Diagnostics.Debug.Write(myString);
tempBuffSize = 0;
}
else
{
System.Text.Encoding enc = System.Text.Encoding.ASCII;
string myString = enc.GetString(readBuffer);
System.Diagnostics.Debug.Write(myString);
// HandleData(readBuffer, currStart, currEnd);
}
// Set the new start - after our delimiter
currStart = i + 2;
}
}
// See if we still have any leftovers
if (currStart < dataLen)
{
Array.Copy(readBuffer, currStart, tempBuff, 0, dataLen - currStart);
tempBuffSize = dataLen - currStart;
}
}

Why do you expect it to read the whole information in the first place? I am not an expert but it seems to me that neither the synchronous nor the asynchronous methods guarantee reading all the data (whatever that means because as long as a socket is open more data can arrive). After the code in your EndRead method you should call Read or BeginRead again if you expect more data. You should know if more data is expected based on the protocol you've established with the client.

I have faced similar issues when i developed tcp apps for an embedded device. In my case the problem was the device was giving out data in a delayed time and hence before the rest of the data can come in the control moves to the next line in the program fetching only the initial data from the server. I got around this by introducing a delay.
Just after the line where you read data from the server introduce a delay and for that reason it would be better to run this on a separate thread
thread.sleep(3000)
This should be your problem most probably.

maybe your stream object got disposed when it got out of scope, before the readHandler could be called again. try promoting tcpClient and stream to class scope instead of method scope, or move the reading to a separate thread that exits when the operation has finished.

Related

C# Read from SslStream continuously (long connection, last for up to days) and Efficiently without infinite loop

I am completely new to C#, and need to encrypt the data sent and received between client and server, after googled it for two days, learnt the best way is to use SslStream, some answers I found give good examples but they all somehow assume we just need to read one message and then close the connection, which is totally not my case, I have to read whenever a user triggers his device to send a message through the persistent connection.
one example from Microsoft documentation:
static string ReadMessage(SslStream sslStream)
{
// Read the message sent by the client.
// The client signals the end of the message using the
// "<EOF>" marker.
byte [] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
do
{
// Read the client's test message.
bytes = sslStream.Read(buffer, 0, buffer.Length);
// Use Decoder class to convert from bytes to UTF8
// in case a character spans two buffers.
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer,0,bytes)];
decoder.GetChars(buffer, 0, bytes, chars,0);
messageData.Append (chars);
// Check for EOF or an empty message. <------ In my case,I don't have EOF
if (messageData.ToString().IndexOf("<EOF>") != -1)
{
break;
}
} while (bytes !=0);
return messageData.ToString();
}
and other answers actually tell me how to continuously read from a SslStream, but they are using infinite loop to do it, on the server side, there could be thousands clients connected to it, so the possible poor performance concerns me,like this one :
Read SslStream continuously in C# Web MVC 5 project
So I want to know if there is a better way to continuously read from a persistent SslStream connection.
I know with bare socket I can use SocketAsyncEventArgs to know when there is new data ready, I hope I could do this with SslStream, probably I misunderstand something, any ideas would be appreciated, thanks in advance.
Here's my shot at it. Instead of looping forever, I chose recursion. This method will return immediately but will fire an event when EOF is hit and continue to keep reading:
public static void ReadFromSSLStreamAsync(
SslStream sslStream,
Action<string> result,
Action<Exception> error,
StringBuilder stringBuilder = null)
{
const string EOFToken = "<EOF>";
stringBuilder = stringBuilder ?? new StringBuilder();
var buffer = new byte[4096];
try
{
sslStream.BeginRead(buffer, 0, buffer.Length, asyncResult =>
{
// Read all bytes avaliable from stream and then
// add them to string builder
{
int bytesRead;
try
{
bytesRead = sslStream.EndRead(asyncResult);
}
catch (Exception ex)
{
error?.Invoke(ex);
return;
}
// Use Decoder class to convert from bytes to
// UTF8 in case a character spans two buffers.
var decoder = Encoding.UTF8.GetDecoder();
var buf = new char[decoder.GetCharCount(buffer, 0, bytesRead)];
decoder.GetChars(buffer, 0, bytesRead, buf, 0);
stringBuilder.Append(buf);
}
// Find the EOFToken, if found copy all data before the token
// and send it to event, then remove it from string builder
{
int tokenIndex;
while((tokenIndex = stringBuilder.ToString().IndexOf(EOFToken)) != -1)
{
var buf = new char[tokenIndex];
stringBuilder.CopyTo(0, buf, 0, tokenIndex);
result?.Invoke(new string(buf));
stringBuilder.Remove(0, tokenIndex + EOFToken.Length);
}
}
// Continue reading...
ReadFromSSLStreamAsync(sslStream, result, error, stringBuilder);
}, null);
}
catch(Exception ex)
{
error?.Invoke(ex);
}
}
You could call it as so:
ReadFromSSLStreamAsync(sslStream, sslData =>
{
Console.WriteLine($"Finished: {sslData}");
}, error =>
{
Console.WriteLine($"Errored: {error}");
});
It's not TaskAsync, so you don't have to await on it. But it is asynchronous so your thread can go on to do other things.
Consider checking out the following asnwer. SSLStream was derived from the Stream class therefore the ReadAsnyc method can be used. Code below, read until the <EOF> delimiter characters then return with the received message as string.
internal static readonly byte[] EOF = Encoding.UTF8.GetBytes("<EOF>");
internal static async Task<string> ReadToEOFAsync(Stream stream)
{
byte[] buffer = new byte[8192];
using (MemoryStream memoryStream = new MemoryStream())
{
long eofLength = EOF.LongLength;
byte[] messageTail = new byte[eofLength];
while (!messageTail.SequenceEqual(EOF))
{
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
await memoryStream.WriteAsync(buffer, 0, bytesRead);
Array.Copy(memoryStream.GetBuffer(), memoryStream.Length - eofLength, messageTail, 0, eofLength);
}
// Truncate the EOF tail from the data stream
byte[] result = new byte[memoryStream.Length - eofLength];
Array.Copy(memoryStream.GetBuffer(), 0, result, 0, result.LongLength);
return Encoding.UTF8.GetString(result);
}
}
The received messages was appended to the memoryStream. The first Array.Copy copies the message tail from the buffer. If the message tail is euqals to the <EOF> then it stops reading from the stream. Second copy is to ensure truncating the delimiter characters from the message.
Note: There is a more sophisticated way of slicing using Span introduced in .NET Core 2.1.

C# Stream read in a constant loop

Using c#, I would like to make a loop where it repeats the task only after the function before completes. Here's my code
TcpListener serv = new TcpListener(IPAddress.Any, 19148);
serv.Start();
TcpClient clie = serv.AcceptTcpClient();
NetworkStream stre = clie.GetStream();
byte[] buffer = new byte[2048];
Form1 f1 = new Form1();
f1.Show();
f1.TopMost = true;
///the code to repeat starts here
stre.Write(Encoding.UTF8.GetBytes("Enter Message: "), 0, Encoding.UTF8.GetByteCount("Enter Message: "));
stre.Read(buffer, 0, buffer.Length);
f1.label1.Text = Encoding.UTF8.GetString(buffer);
///ends here
I have read up on loops but every time I use one it crashes my program.
I have tried most things, but the most promising one so far has been this but it only shows the form on the last input.
int i = 0;
while(i < 3)
{
stre.Write(Encoding.UTF8.GetBytes("Enter Message: "), 0, Encoding.UTF8.GetByteCount("Enter Message: "));
stre.Read(buffer, 0, buffer.Length);
f1.label1.Text = Encoding.UTF8.GetString(buffer);
i++;
}
Any help would be appreciated as I have no leads to even point me in the right direction.
You really need to move the read to it's own thread (or use Async methods). You called .Show on the form, but the actual show isn't instantaneous, and it needs the thread you are using for your input to be able to draw itself. If you absolutely can't move your read loop to another thread (Async, BackgroundWorker, Task, etc), you could change your code to the following:
TcpListener serv = new TcpListener(IPAddress.Any, 19148);
serv.Start();
TcpClient clie = serv.AcceptTcpClient();
NetworkStream stre = clie.GetStream();
byte[] buffer = new byte[2048];
Form1 f1 = new Form1();
f1.Show();
f1.Refresh(); // Do it RIGHT NOW!
f1.TopMost = true;
int i = 0;
while(i < 3)
{
stre.Write(Encoding.UTF8.GetBytes("Enter Message: "), 0, Encoding.UTF8.GetByteCount("Enter Message: "));
stre.Read(buffer, 0, buffer.Length);
f1.label1.Text = Encoding.UTF8.GetString(buffer);
f1.label1.Refresh(); // Do it right now!
i++;
}
You will find that your form will display, but it is absolutely frozen. You can't resize it, it won't repaint itself until your loop finishes. Not a very good experience.

Arduino ethernet c# client not receiving data

I am having a problem receiving data (a string of text) from an Arduino via a C# Winform app. The sketch on the Arduino basically reply with whatever I send. I am able to send data. What is strange is that if I telnet the device and type any text it responds correctly so it appears the problem is my C# code, however, I can't seem to figure out where I am incorrect.
Here is what I have
public partial class Form1 : Form
{
System.Net.Sockets.TcpClient clientSocket = new System.Net.Sockets.TcpClient();
NetworkStream serverStream = default(NetworkStream);
string readData = null;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
byte[] outStream = System.Text.Encoding.ASCII.GetBytes(textBox2.Text);
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
}
private void button2_Click(object sender, EventArgs e)
{
clientSocket.Connect("192.168.1.177", 5223);
readData = "Conected Arduino ...";
msg();
serverStream = clientSocket.GetStream();
byte[] outStream = System.Text.Encoding.ASCII.GetBytes(textBox2.Text);
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
Thread ctThread = new Thread(getData);
ctThread.Start();
}
private void getData()
{
while (true)
{
while (true)
{
serverStream = clientSocket.GetStream();
int buffSize = clientSocket.ReceiveBufferSize;
byte[] inStream = new byte[10025];
serverStream.Read(inStream, 0, buffSize);
string returndata = System.Text.Encoding.ASCII.GetString(inStream);
readData = "" + returndata;
msg();
}
}
}
private void msg()
{
this.BeginInvoke(new Action(() =>
{
textBox1.Text = String.Format("{0}{1} >> {2}", textBox1.Text, Environment.NewLine, readData);
}
));
}
}
Here is part of the Arduino sketch
void loop() {
// wait for a new client:
EthernetClient client = server.available();
// when the client sends the first byte, say hello:
if (client) {
if (!alreadyConnected) {
// clead out the input buffer:
client.flush();
Serial.println("We have a new client");
client.println("Hello, client!");
alreadyConnected = true;
}
if (client.available() > 0) {
// read the bytes incoming from the client:
char thisChar = client.read();
// echo the bytes back to the client:
//server.write(thisChar);
// echo the bytes to the server as well:
//Serial.write(thisChar);
if (inputPos < maxLength-1)
{
if (thisChar == '\n')
{
inputString[inputPos] = 0;
server.write(inputString);
Serial.write(inputString);
inputPos = 0;
} else {
// add it to the inputString:
inputString[inputPos] = thisChar;
inputPos++;
}
} else {
inputPos = 0;
}
}
}
}
Your code has a number of things wrong with it, including what has to be the most common novice error I've seen in any context: you fail to take into account the number of bytes actually read. Plus you have another common variation on the theme, which is that you process the entire receive buffer array as if the whole thing had valid data in it.
Without a way to test, it's hard to know for sure. But at the very least, you should change your receive method to look like this:
private void getData()
{
serverStream = clientSocket.GetStream();
while (true)
{
byte[] inStream = new byte[10025];
int bytesRead = serverStream.Read(inStream, 0, inStream.Length);
readData = System.Text.Encoding.ASCII.GetString(inStream, 0, bytesRead);
msg();
}
}
DON'T pointlessly nest a while (true) loop inside another while (true) loop
DON'T create a new NetworkStream every time you want to read
DON'T concern yourself with the Socket.ReceiveBufferSize property value
DO capture and use the return value from the read operation
DON'T concatenate the empty string with other strings (even in an iterated scenario, one should be using StringBuilder instead, and here you're not even iterating the concatenation!)
Of course, not all of those flaws are fatal. The biggest issues are the new NetworkStream on each read and the mismanagement of the receive buffer and result value. But really, you should strive for all of the code to be good.
Note that the above merely improves the code. Even the above still has a variation on "the most common novice error I've seen in any context": while it does use the return value from the read operation, it does not do everything with it that it should. In particular:
You are not actually guaranteed that you will receive all of the bytes that were sent to you in one read operation. This means your application protocol really should have some way for you to identify within the stream of bytes you're reading where one message ends and the next one starts.
When the connection is gracefully closed, the read operation will return 0 as the byte count, at which point your own code is supposed to respond by finishing up whatever writes to the socket you need to (if any) and then gracefully closing your own socket by calling Socket.Shutdown(SocketShutdown.Both) and finally Socket.Close().
But the above should at least help you make forward progress in your project.
One other thing you really should change IMHO which I didn't bother in the above, is that you should not use an instance field (i.e. readData) to pass data when simply passing it as a method parameter would suffice. You should avoid side-effects in your code as much as possible. It will make the code much easier to comprehend and thus to write correctly, among other things.

setting NetworkStream.ReceiveTimeout not triggering exception

This is a continuation of the this question. I am new to network programming, so I am just writing small sample stuff to gain understanding, but somewhat struggling with explaining results.
It seems setting NetworkStream.ReceiveTimeout is not working correctly when client that was supposed to be sending data simply closes before sending all the expected data.
Here is the sample code:
public static void Main(string[] args)
{
TcpListener listener = new TcpListener(IPAddress.Any, 10001);
listener.Start();
ThreadPool.QueueUserWorkItem(WriterThread);
using (TcpClient client = listener.AcceptTcpClient())
using (NetworkStream stream = client.GetStream())
{
client.ReceiveTimeout = (int)new TimeSpan(0, 0, 2).TotalMilliseconds;
stream.ReadTimeout = (int)new TimeSpan(0, 0, 2).TotalMilliseconds;
ReceiveMessage(stream, 1024);
}
listener.Stop();
Console.WriteLine("Done.");
Console.ReadKey(true);
}
private static void WriterThread(object state)
{
using (TcpClient client = new TcpClient())
{
client.Connect(new IPEndPoint(IPAddress.Loopback, 10001));
using (NetworkStream stream = client.GetStream())
{
byte[] bytes = Encoding.ASCII.GetBytes("obviously less than 1024 bytes");
stream.Write(bytes, 0, bytes.Length);
Thread.Sleep(10000); // comment out
}
}
}
private static byte[] ReceiveMessage(Stream stream, int length)
{
byte[] buffer = new byte[length];
int bufferFill = 0;
while (true)
{
bufferFill += stream.Read(buffer, bufferFill, buffer.Length - bufferFill);
if (buffer.Length == bufferFill)
return buffer;
Thread.Sleep(100);
}
}
This version works correctly triggering exception on the stream.Read() call. However If I comment out Thread.Sleep(10000), the client closes connection, but listener fails to recognize it. Main thread gets stuck inside the while(true) loop. The stream.Read() keeps returning zero, but no exception thrown.
Is this normal? If so how am I expected to handle abnormal client disconnections?
Yes, this sounds normal. There is no receive- or read timeout because the client has disconnected. This means that no more data is available for reading and the stream will return 0 immediately just as documented.
I would modify your ReceiveMessage method to something like the following:
private static byte[] ReceiveMessage(Stream stream, int length)
{
byte[] buffer = new byte[length];
int bufferFill = 0;
while (true)
{
int bytesRead = stream.Read(buffer, bufferFill, buffer.Length - bufferFill);
if (bytesRead == 0)
throw new Exception("No more data available.");
bufferFill += bytesRead;
if (buffer.Length == bufferFill)
return buffer;
Thread.Sleep(100);
}
}
Clearly if the stream.Read() call returns 0 before we have received all the expected bytes there must have been some form of disconnection or similar. Either way we will never get any more data from the stream.
Edit: The Stream class has no notion of a "message". The Read method blocks until more data becomes available if none is already in the buffer. It will however return 0 when no more data can be received, which in this case means the connection is closed.

How do i judge when the NetWorkStream finishes by using .net TcpClient to communicate

I try to use stream.DataAvailable to judge if it is finished,but sometimes the value is false but after a little while it is true again,i have to set a counter and judge the end by the symbol '>' like this
int connectCounter = 0;
while (connectCounter < 1200)
{
if (stream.DataAvailable)
{
while (stream.DataAvailable)
{
byte[] buffer = new byte[bufferSize];
int flag = stream.Read(buffer, 0, buffer.Length);
string strReadXML_t = System.Text.Encoding.Default.GetString(buffer);
strReadXML = strReadXML + strReadXML_t.Replace("\0", string.Empty);
}
if (strReadXML.Substring(strReadXML.Length - 1, 1).Equals(">"))
{
break;
}
}
Thread.Sleep(100);
connectCounter++;
}
is there any good methord to deal with it?Thank you!
You have a couple options. You can use a synchronous, blocking Read, or you can use an asynchronous IO pattern.
If you simply call stream.Read(), the call will block, and wait forever (until the TCP timeout), until data is available. It seems you don't want to do that. You want to wait, at most, 120 seconds (1200ms * 100), for the data to be completely read.
Something like this:
private class AsyncState
{
public NetworkStream ns;
public ManualResetEvent e;
public byte[] b;
public String strReadXML;
}
public void Run()
{
TcpClient client ;//= ...
NetworkStream networkStream = client.GetStream();
byte[] buffer = new byte[1024];
var completedEvent = new ManualResetEvent(false);
networkStream.BeginRead(buffer, 0, buffer.Length,
AsyncRead,
new AsyncState
{
b = buffer,
ns = networkStream,
e = completedEvent,
strReadXML = ""
});
// do other stuff here. ...
// finally, wait 120s for the reading to complete
bool success = completedEvent.WaitOne(1200*100, false);
if (!success)
{
client.Close();
}
}
private void AsyncRead(IAsyncResult ar)
{
AsyncState state = ar as AsyncState;
int n = state.ns.EndRead(ar);
if (n == 0)
{
// no more bytes to read
// signal completion
state.e.Set();
return;
}
// state.buffer now contains the bytes read
string s = System.Text.Encoding.Default.GetString(state.b);
state.strReadXML = state.strReadXML + s.Replace("\0", string.Empty);
if (state.strReadXML.EndsWith(">"))
{
// got the "end". signal completion
state.e.Set();
return;
}
// read again
state.ns.BeginRead(state.b, 0, state.b.Length, AsyncRead, state);
}
Try asynchronous reading.
When your callback is called, you can read the existing data buffer and then call
BeginRead again. So that when somre more data is received, you callback will be called again.
Something like:
void DataReceived(IAsyncResult result) ///I am not sure about the parameters.
{
///read data from buffer
networkstream.BeginRead(
buffer, 0, buffer.Length,
new AsyncCallback(DataReceived),
null);
}
I think this is a pretty decent approach.
Do you have control over the sending application? If you do you can change the sending application to wrap the NetworkStream in a BinaryWriter and use .Write( string ) which will send the length of the string, and then the string itself. On the receiving application (as above) you can wrap the NetworkStream in a BinaryReader and call .ReadString() which will read the length from the stream, then read the correct length string in for you.
If you don't have control over the sending application then you can check the return from the stream.Read() call as it returns 0 at the end of the stream - though this will require the sending application to have closed the socket.
Also, stream.Read() is not guaranteed to return the number of bytes you requested, it could return less. Your flag variable should really be bytesread, and you should then pass this bytesread variable to the .GetString method.

Categories

Resources