C# NetworkStream detect all kind of disconnections and exceptions - c#

I have this code to connect to streaming server. the server disconnect from time to time and I want to detect it and restart the connection when neede.
How can I detect in this code any kind of exceptions?
Because now I get disconnected and can't catch it.
this.ns = new NetworkStream(server);
while (true)
{
// Incoming message may be larger than the buffer size.
do
{
byte[] myReadBuffer = new byte[10240 * 5];
await this.ns.ReadAsync(myReadBuffer, 0, myReadBuffer.Length).ContinueWith((numberOfBytesRead) =>
{
string message = Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead.Result);
p.Invoke(message);
});
}
while (this.ns.DataAvailable);
}

Your variable numberOfBytesRead is actually the previous task that has finished from where you can check whether it is completed or failed.
if(numberOfBytesRead.IsFaulted)
{
var aggregatedEx = numberOfBytesRead.Exception;
//do something
}

Related

How to communicate between two Unity apps with TCP?

Update
I figured out what the problem was. I was trying to move too much data over TCP, and it was causing freeze-ups. For some reason, this wasn't manifesting in the editor...who knows for what reason. If anyone else stumbles upon this problem (in a program like Unity, where functions are looping constantly and data is always being processed), consider that you're moving too much irrelevant data.
Original Post
I've run into quite the problem, and I'm hoping I can receive some guidance.
In short, I'm wondering how to use TCP to communicate two Unity apps over the same computer. I've gotten it functioning in editor, but when both apps are built, communication quickly breaks down.
This is really stumping me, because I don't understand why an app would work in the Editor environment, but not in the official build.
When I use TCP to communicate between two Unity apps (on the same computer), it works so long as one of them is kept in the Unity environment. That is, if I build one app, and open the other in the Unity editor, TCP communication works flawlessly.
Here is some more background: One of my apps is functioning as a User Interface, and the other is interfacing with a Looking Glass to provide a holographic display of in-game objects. Originally, they were combined into one App - but I had a lot of trouble getting Unity's multidisplay support to function between two monitors of different resolutions. Looking Glass factory even provides a prefab to do just this, but it is broken in the current SDK. So I have resorted to using sockets to interface between two apps, one for each monitor.
I'm using C#'s TCP listener class: https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.tcplistener?view=netframework-4.8
And TCP client class: https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.tcpclient?view=netframework-4.8
Presently, the UI is acting as the TCPListener, and the application that produces holograms is the TCPClient. Within each of these applications, I'm using two Queues - an IncomingMessages queue and an Outgoing Messages queue - which are global variables shared between the main thread and the networking thread.
TCP Listener:
private void Start()
{
incomingMessages = new Queue();
outgoingMessages = new Queue();
Application.runInBackground = true;
thread = new Thread(new ThreadStart(Receive));
thread.Start();
//stuff happens that's irrelevant to this question. And then...
}
void Receive()
{
TcpListener server = null;
try
{
// Set the TcpListener on port 13000.
Int32 port = 13000;
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
// TcpListener server = new TcpListener(port);
server = new TcpListener(localAddr, port);
// Start listening for client requests.
server.Start();
// Buffer for reading data
Byte[] bytes = new Byte[256];
String data = null;
// Enter the listening loop.
Debug.Log("About to reenter main while in Server...");
while (threadContinue)
{
Debug.Log("Waiting for a connection... ");
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
TcpClient client = server.AcceptTcpClient();
Debug.Log("Connected!");
data = null;
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
int i;
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Debug.Log("Received from Client: " + data);
lock (this)
incomingMessages.Enqueue(data);
string response = supplyData();
byte[] msg = System.Text.Encoding.ASCII.GetBytes(response);
// Send back a response.
stream.Write(msg, 0, msg.Length);
Debug.Log("Sent to Client: " + response);
}
// Shutdown and end connection
client.Close();
}
}
catch (SocketException e)
{
Debug.Log("SocketException: ");
Debug.Log(e);
}
finally
{
// Stop listening for new clients.
server.Stop();
}
Debug.Log("Exiting 'Receive'");
}
And here is the TCP Client. It attempts to connect a regular intervals, and also whenever new data is available. This is so that it can receive information from the server regularly and share new data whenever it is available:
void Start()
{
//prepare networking
Application.runInBackground = true;
outgoingMessages = new Queue();
incomingMessages = new Queue();
thread = new Thread(new ThreadStart(Connect));
thread.Start();
//stuff happens that's irrelevant to this question...
}
private void Connect()
{
String server = "127.0.0.1";
Int32 port = 13000;
string message = "";
while (threadContinue == true)
{
if (timeToConnect())
{
lastConnection = ourTime;
if (outgoingMessages.Count > 0)
message = outgoingMessages.Dequeue().ToString();
else
message = "Nothing to report.";
try
{
// Create a TcpClient.
// Note, for this client to work you need to have a TcpServer
// connected to the same address as specified by the server, port
// combination.
client = new TcpClient(server, port);
// Translate the passed message into ASCII and store it as a Byte array.
Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
// Get a client stream for reading and writing.
// Stream stream = client.GetStream();
stream = client.GetStream();
// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);
Debug.Log("Sent to Server: " + message);
// Buffer to store the response bytes.
data = new Byte[256];
// String to store the response ASCII representation.
String responseData = String.Empty;
// Read the first batch of the TcpServer response bytes.
Int32 bytes = stream.Read(data, 0, data.Length);
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
lock (this)
incomingMessages.Enqueue(responseData);
Debug.Log("Received from Server: " + responseData);
stream.Close();
client.Close();
}
catch (ArgumentNullException e)
{
Debug.Log("ArgumentNullException: ");
Debug.Log(e);
outgoingMessages.Enqueue(message);
}
catch (SocketException e)
{
Debug.Log("SocketException: ");
Debug.Log(e);
outgoingMessages.Enqueue(message);
}
}
}
}
private bool timeToConnect()
{
if ((ourTime - lastConnection > NETWORK_DELAY) || (outgoingMessages.Count > 0))
return true;
return false;
}
Instantiated in separate threads so that Unity's main thread can continue unhindered.
Again - it works in Editor, but when I build it, it breaks.
Update
I figured out what the problem was. I was trying to move too much data over TCP, and it was causing freeze-ups. For some reason, this wasn't manifesting in the editor...just in the exported app. Who knows for what reason. If anyone else stumbles upon this problem...where you're bypassing Unity's multidisplay functionality by building multiple apps that communicate over network...consider that you're burdening your queues with too much data.

TcpListener and NetworkStream doesn't get data

I am trying to receive a message from an equipment. This equipment is an authentication terminal, and it will send the message as soon as the user set his credentials.
Also, the manual of the equipment says the message will be sent in the ILV format, standing I for identification, L for length and V for value.
a normal message would be:
I -> 0x00 (byte 0 indicating success)
L -> 0x04 0x00 (two bytes for length, being 4 the length in this case)
V -> 0x35 0x32 0x38 0x36 (the message itself)
The message is sent in TCP protocol, so I created a socket using the TcpListener class, following this sample from Microsoft:
https://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener(v=vs.110).aspx
new Thread(() =>
{
TcpListener server = null;
try
{
Int32 port = 11020;
IPAddress localAddr = IPAddress.Parse("192.168.2.2");
server = new TcpListener(localAddr, port);
server.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
server.Start();
byte[] bytes = new byte[256];
String data = null;
while (true)
{
TcpClient client = server.AcceptTcpClient();
data = null;
NetworkStream stream = client.GetStream();
int i = 0;
while((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// this code is never reached as the stream.Read above runs for a while and receive nothing
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
}
client.Close();
}
}
catch (SocketException ex)
{
// Actions for exceptions.
}
finally
{
server.Stop();
}
}).Start();
If the stream.Read is removed, then the code flows (although I got nothing either), however if I put any stream.Read statement the execution holds for a while like it was waiting for some response, and then it ends with no response, all bytes read is zero.
I am running Wireshark on the computer and the data is being sent.
Anybody knows what I am doing wrong?
I think the problem is right in the Read method, which halts until the buffer fills completely, but that's obviously won't happen anytime.
In that while-loop, you should spin by checking for available data first, then read them and append to the byte-array. Moreover, since the loop become too "tight", it's better to yield the control to the task scheduler for a bit.
Here is an example:
while (true)
{
if (stream.DataAvailable)
{
int count = stream.Read(bytes, i, bytes.Length);
i += count;
// ...add something to detect the end of message
}
else
{
Thread.Sleep(0);
}
}
It's worthwhile to notice that there's no timeout, but that's a very common feature in order to quit the loop when no data (or broken) are incoming.
Furthermore, the above pattern is not the best way to accumulate data, because you may get an exception when too many bytes are received. Consider a spurious stream of 300 bytes: that will overflow the available buffer. Either use the "bytes" as a temporary buffer for what the Read method gives, or provide a safety check before calling the Read method (so that you may provide the best byte count to read).
Useful links here:
https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.networkstream.read?view=netframework-4.7.1#System_Net_Sockets_NetworkStream_Read_System_Byte___System_Int32_System_Int32_

Reading/Writing TcpClient/SslStream in separate threads

I have an SSL connection to a server and post requests to it. The act of posting a message should be instant with no delay to read the response, because there would be consequent posts that should come without the delay.
That's why i just do
this.sslStream.Write(byteArray, 0, byteArray.Length);
However, I need some responses to be actually received by my app, so I have a parallel thread:
this.threadReadStream = new Thread(new ThreadStart(this.ThreadReadStream));
this.threadReadStream.Start();
In that thread I have a loop which reads data from sslStream:
public void ThreadReadStream()
{
int bufferLen = 4096, byteCount, pos, pos2;
var buffer = new byte[bufferLen];
string responseBuffer = "", responsePart, response = "";
bool err = true;
while (true)
{
err = true;
byteCount = 0;
while (err)
{
err = false;
byteCount = 0;
try
{
byteCount = this.sslStream.Read(buffer, 0, bufferLen);
}
catch (Exception exception)
{
err = true;
this.TcpConnect();
}
}
if (byteCount > 0)
{
// do something
}
}
}
The problem is that sslStream.Read always returns 0 while being used in separate thread, but works fine if called in the same thread with sslStream.Write.
Few comments about the things that are not likely to influece the problem, however, it is better to clarify the stuff:
1) I use while(err) cycle to check that the connection was not broken. If so, I reconnect and read again.
2) For the ones who don't like while(true) thing. Yes, I have the exit condition from the loop in the parallel thread which shut downs all the tcp-related threads.
How does the server identify that the write process was ended?
Maybe there is actually a data on the way to the server so it don't starting to send the response.
In addition, sharing a TCP connection between different threads is not a good practice.

Best Way to write large data over network stream of TcpClient

We have a requirement to upload large firmware files to printers to upgrade the firmware of the device. The printer device is in the same network as my server, and the size of firmware that we are trying to upload is approximately to 200 - 500 MB. The approach that we have chosen is to load the firmware (.bin file) into Memory stream and write it in chunks over the network using TcpClient.
Based on the response from the network stream, we are displaying the status of firmware upgrade to the client. Following is the code snippet that we have used for firmware upgrade. I want to know if it is the best approach, as wrong one may harm the device.
EDIT:
class MyClass
{
int port = 9100;
string _deviceip;
byte[] m_ReadBuffer = null;
TcpClient _tcpclient;
NetworkStream m_NetworkStream;
static string CRLF = "\r\n";
public event EventHandler<DeviceStatus> onReceiveUpdate;
public async Task<bool> UploadFirmware(Stream _stream)
{
bool success = false;
try
{
_tcpclient = new TcpClient();
_tcpclient.Connect(_deviceip, port);
_stream.Seek(0, SeekOrigin.Begin);
m_NetworkStream = _tcpclient.GetStream();
byte[] buffer = new byte[1024];
m_ReadBuffer = new byte[1024];
int readcount = 0;
m_NetworkStream.BeginRead(m_ReadBuffer, 0, m_ReadBuffer.Length,
new AsyncCallback(EndReceive), null);
await Task.Run(() =>
{
while ((readcount = _stream.Read(buffer, 0, buffer.Length)) > 0)
{
m_NetworkStream.Write(buffer, 0, readcount);
m_NetworkStream.Flush();
}
});
success = true;
}
catch (Exception ex)
{
upgradeStatus = false;
}
return success;
}
private void EndReceive(IAsyncResult ar)
{
try
{
int nBytes;
nBytes = m_NetworkStream.EndRead(ar);
if (nBytes > 0)
{
string res = Encoding.UTF8.GetString(m_ReadBuffer, 0, nBytes);
DeviceStatus status = new DeviceStatus();
string[] readlines = res.Split(new string[] { CRLF },
StringSplitOptions.RemoveEmptyEntries);
foreach (string readline in readlines)
{
if (readline.StartsWith("CODE"))
{
//read readline string here
break;
}
}
}
if (m_NetworkStream.CanRead)
{
do
{
m_NetworkStream.BeginRead(m_ReadBuffer, 0, m_ReadBuffer.Length, new
AsyncCallback(EndReceive), null);
} while (m_NetworkStream.DataAvailable);
}
}
catch (ObjectDisposedException ods)
{
return;
}
catch (System.IO.IOException ex)
{
}
}
}
Any help will be really appreciated.
Your code is basically fine with a few issues:
m_NetworkStream.Flush(); AFAIK this does nothing. If it did something it would harm throughput. So delete that.
_stream.Seek(0, SeekOrigin.Begin); seeking is the callers concern, remove that. This is a layering violation.
Use bigger bigger buffers. Determine the right size experimentally. I usually start at 64KB for bulk transfers. This makes the IOs less chatty.
Turn on nagling which helps with bulk transfers because it saves you from spurious small packets.
You can replace the entire read-write-loop with Stream.Copy.
The way you report exceptions to the callers hides a lot of information. Just let the exception bubble out. Don't return a bool.
Use using for all resource to ensure they are cleaned up in the error case.
nBytes = m_NetworkStream.EndRead(ar); here, you assume that a single read will return all data that will be coming. You might receive just the first byte, though. Probably, you should use StreamReader.ReadLine in a loop until you know you are done.
catch (System.IO.IOException ex) { } What is that about? If firmware updates are such a critical thing suppressing errors appears very dangerous. How else can you find out about bugs?
I would convert the reading code to use await.
As the maximum length for a TcpPacket is 65535 (2^16-1), if you send any packet breaching this lenght it will be truncated. If I were you, I think the best way of sending large packets, is setting a Header of every packet and enumerating them. For example:
C->S ; [P1] <content>
and then the same structure, just plus 1 [P2] <content>
To do so, just use few substrings to truncate the data and sending them.
Cheers!

SerialPort.BaseStream.ReadAsync missing the first byte

I am trying to use the SerialPort class in .net.
I've opted to keep my service async, so I am using the async-methods on SerialPort.BaseStream.
In my async method, I write a byte[] to the serial port, then start reading until I haven't received any more data in n milliseconds, and return that result.
The problem is, however, that I seem to miss the first byte in all replies other than the very first reply after opening the serial port.
If I close the port after every response (Read), and open it again before doing a new request (Write), the first byte is not missing. This, however, often results in a "Access to the port 'COM4' is denied." exception, if I try to open the port too soon after closing. It also seems very unnecessary to open/close for every write/read.
This is basically what my method looks like:
private async Task<byte[]> SendRequestAsync(byte[] request)
{
// Write the request
await _serialPort.BaseStream.WriteAsync(request, 0, request.Length);
var buffer = new byte[BUFFER_SIZE];
bool receiveComplete = false;
var bytesRead = 0;
// Read from the serial port
do
{
var responseTask = _serialPort.BaseStream.ReadAsync(buffer, bytesRead, BUFFER_SIZE - bytesRead);
if (await Task.WhenAny(responseTask, Task.Delay(300)) == responseTask)
{
bytesRead += responseTask.Result;
}
else
receiveComplete = true;
} while (!receiveComplete);
var response = new byte[bytesRead];
Array.Copy(buffer, 0, response, 0, bytesRead);
return response;
}
Is there anything obviously wrong in the way I am doing this? Is there a smarter way to achieve the same asynchronously?
Just because you're not observing the last ReadAsync() doesn't mean it gets canceled, it's still running, which apparently manifests by it reading the first byte of the following message.
What you should do is to cancel the last ReadAsync() by using a CancellationToken. Note that there is a possible race between the timeout and the read, but I'm assuming that if the timeout elapsed, it's not possible for the read to complete without another write.
The code would look like this:
var cts = new CancellationTokenSource();
do
{
var responseTask = _serialPort.BaseStream.ReadAsync(
buffer, bytesRead, BUFFER_SIZE - bytesRead, cts.Token);
if (await Task.WhenAny(responseTask, Task.Delay(300)) == responseTask)
{
bytesRead += responseTask.Result;
}
else
{
cts.Cancel();
receiveComplete = true;
}
} while (!receiveComplete);
Note that both the cause and the solution are my guesses, it's certainly possible that I'm wrong about one or both of them.

Categories

Resources