My current named pipe implementation reads like this:
while (true)
{
byte[] data = new byte[256];
int amount = pipe.Read(data, 0, data.Length);
if (amount <= 0)
{
// i was expecting it to go here when a client disconnects but it doesnt
break;
}
// do relevant stuff with the data
}
how can I correctly detect when a client disconnects?
Set a read timeout and poll the NamedPipeClientStream.IsConnected flag when a timeout occurs.
A Read Timeout will cause reads that are idle for the timeout duration to throw InvalidOperationException
If you are not reading, and want to detect disconnections, call this method on a worker thread for the lifetime of your pipe connection.
while(pipe.IsConnected && !isPipeStopped) //use a flag so that you can manually stop this thread
{
System.Threading.Thread.Current.Sleep(500);
}
if(!pipe.IsConnected)
{
//pipe disconnected
NotifyOfDisconnect();
}
One easy way to tell if your pipe has been broken (remotely) is to always use asynchronous reads instead of sync ones, and to always have at least one read submitted asynchronously. That is, for every successful read you get, post another async read, whether you intend to read another or not. If you close the pipe, or the remote end closes it, you'll see the async read complete, but with a null read size. You can use this to detect a pipe disconnection. Unfortunately, the pipe will still show IsConnected, and you still need to manually close it, I think, but it does allow you to detect when something went wonky.
Use WaitForPipeDrain() method after Writing to the Pipe (using WriteByte() or Write()) and catch the exception which is "Pipe is Broken".
You may want to put that in a while loop and keep writing to the pipe.
in the case of Synchronous call you track the -1 return by ReadByte of Stream abstract class, which is inherited by NamedPipeServerStream:
var _pipeServer = new NamedPipeServerStream(PipeConst._PIPE_NAME, PipeDirection.InOut);
int firstByte = _pipeServer.ReadByte();
const int END_OF_STREAM = -1;
if (firstByte == END_OF_STREAM)
{
return null;
}
The docs states indeed:
//
// Summary:
// Reads a byte from a pipe.
//
// Returns:
// The byte, cast to System.Int32, or -1 indicates the end of the stream (the pipe
// has been closed).
public override int ReadByte();
Only after a first failed read your IsConnected property will be correctly set to false:
_pipeServer.IsConnected
You might observe that even on the
official illustration of Microsoft (and more precisely in the StreamString class)
this check is not done:
Do not forget to Vote for this answer and visit my Youtube channel. More info on my profile.
Regards !
Related
I have a strange question that involves NetworkStreams in C# .Net Core 2.2.101
My setup is as follows:
I have a list of meters
Each meter has a list of registers (a register saves a value, for example: the voltage or current)
Meters are connected to a GSM modem via RS485 (irrelevant for the question)
I send commands to the modem to read specific registers for specific meters
For each register of each meter, I send a command using stream.Write(bytesToSend, 0, bytesToSend.Length); to ask the meter to send me the data that is saved in a specific register in the meter. Directly after sending, I read the response using stream.Read(buffer, 0, buffer.Length);. I also set a read timeout of 5 seconds, which will block and wait for 5 seconds before moving on to the next register if no response has been received before the timeout.
The problem:
What is happening is that when I ask the meter for data, sometimes it will take too long and the timeout will be reached, after which I will move on to ask the next register for data, but then sometimes the first register will reply with data after I have moved on to the next register (meaning that the NetworkStream now has the data from the previous register). Since I have already moved on in my for-loop, my program thinks that the data I am reading from the stream is for the current register, when in fact it is for the previous register from the previous iteration. This messes up the data that goes into the database, because I am saving the wrong value for the wrong register.
My question is: Is there a clever way to ignore any incoming data from a previous iteration in my for-loop? Unfortunately there is no information in the data received that could be used to identify for which register the data is for.
Here is a snippet of what my write and read requests look like:
stream.ReadTimeout = 5000;
stream.WriteTimeout = 2000;
foreach (Meter meter in metersToRead)
{
foreach (Register register in meter.Registers)
{
// Write the request to the meter
stream.Write(bytesToSend, 0, bytesToSend.Length);
// Read response from meter
requestedReadingDataCount = stream.Read(buffer, 0, buffer.Length);
// Extract the value from the response buffer and save the value to the database
...
}
}
I want to try and clone the stream and use the cloned stream for communication regarding the current register iteration, so that when a response comes in after I have closed the cloned stream and moved on to the next register, the response will fail since the stream has been closed. However, I am not sure if you can clone a C# NetworkStream? Anybody know?
My last resort will be to make a call to the database after I read the data for each register, to check if the data I received is reasonable for that register, but I fear that this might slow the program down with all the database calls and I would have to build up some rules that will determine whether a value is reasonable for the current register.
Any ideas would be greatly appreciated. If you have any questions, please let me know and I will try my best to explain it further.
Edit
Here is an updated code snippet, as well as an image that will better explain the issue that I am having.
private async Task ReadMeterRegisters(List<MeterWithRegisters> metersWithRegisters, NetworkStream stream)
{
stream.ReadTimeout = 5000; /* Read timeout set to 5 seconds */
stream.WriteTimeout = 2000; /* Write timeout set to 2 seconds */
foreach (Meter meter in metersToRead)
{
foreach (Register register in meter.Registers)
{
// Instantiate a new buffer to hold the response
byte[] readingResponseDataBuffer = new byte[32];
// Variable to hold number of bytes received
int numBytesReceived = 0;
try
{
// Write the request to the meter
stream.Write(bytesToSend, 0, bytesToSend.Length);
// Read response from meter
numBytesReceived = stream.Read(buffer, 0, buffer.Length);
}
catch (IOException) /* catch read/write timeouts */
{
// No response from meter, move on to next register of current meter
continue;
}
// Extract the value from the response buffer and save the value to the database
...
}
}
}
It sounds like the issue here is that in a timeout scenario, the read operation is still completing at some point in the future but writing to the old buffer. If that is the case, perhaps the simplest option is to not reuse the read buffer in the event of a timeout (meaning: assign a new byte[] to buffer), and consider that network-stream burned (since you now can't know what the internal state is).
An alternative approach would be to not read until you know there is data; you can't do that from NetworkStream, but on Socket you can check .Available to see whether there is data to be read; that way, you won't be performing ambiguous reads. You can also perform a zero-length read on a socket (at least on most OS-es); if you pass a zero-length buffer, it will block until either the timeout or until data becomes available, but without consuming any data (the idea being that you follow a zero-length read with a non-zero-length read if you find that data has become available).
In the more general case: you might find you get better throughput here if you switch to asynchronous IO rather than synchronous IO; you could even use the array-pool for the buffers. For dealing with large volumes of connections, async IO is almost always the way to go.
In C# you have 3 ways to try and receive TCP data on a Socket:
Socket.Receive is a synchronous, blocking method. It doesn't return until it succeeds, barring failure or [optionally] timeout.
Socket.BeginReceive is asynchronous, a supplied callback/delegate is called when there is data to receive, using the now-antiquated Begin/End pattern
Socket.ReceiveAsync begins an asynchronous request to receive data
However my understanding is none of these actually let you cancel the receive operation? The docs suggest EndReceive is used for completing a read, not something one could call to terminate the request?
You often see code like
while(socket.Available==0 && !cancel)Sleep(50); if(!cancel)socket.Receive(...);
But that's pretty terrible.
If I want to sit waiting for data but at some point cancel the receive, say the user hits "stop" button how can this neatly be done so I don't get a callback triggered later on when unexpected?
I had wondered about closing the socket, which would cause the Receive operation to fail, but it seems somewhat ugly. Am I thinking along the right lines or do the various API methods listed above allow a direct cancellation so I don't risk dangling async operations?
There is no known way to cancel it (AFAIK)
One thing you can do it set the Socket.Blocking = false. The receive will return immediately when there is no data. This way it will not hang.
You should check the Socket.Blocking property.
I advise you to use the BeginReceive(IList<ArraySegment<Byte>>, SocketFlags, SocketError, AsyncCallback, Object) overload to prevent it throwing exceptions.
Check the SocketError on "Would Block", meaning "there is not data". So you can try again.
Didn't tested it but ->
A nice idea is using the non-async version Receive to receive 0 bytes (use a static byte[] emptyBuffer = new byte[0]) , and if the sockerError returns with a 'would block', you can have a short delay and retry it. When it doesn't return a socketError there is probably data. So you can start an async version.
What you could do is get a NetworkStream from the socket being read and use it's ReadTimeout property, for example:
// Get stream from socket:
using NetworkStream ns = new NetworkStream(socket);
// Set timeout:
stream.ReadTimeout = 10 * 1000; // 10 sec
var buffer = new List<byte>();
try
{
do
{
buffer.Add((byte) stream.ReadByte());
}
while (stream.DataAvailable);
}
catch (IOException)
{
// Timeout
}
return buffer.ToArray();
I have a library that is connected to some network service using TCP sockets and randomly receives a data from it. I need to process these data line by line and for that I have 2 options:
Create a new thread (I don't want to do that) in which I have never ending loop which calls StreamReader.ReadLine() (I don't want to spawn new threads as this is a library and threads should be fully under control of main program)
Using async callback which gets called every time some data arrives to stream buffer. I currently use this option, but I am having troubles getting lines only. I hacked out this simple solution:
// This function reset the callback after it's processed
private void resetCallback()
{
if (this.networkStream == null)
return;
if (!string.IsNullOrEmpty(this.lineBuffer) && this.lineBuffer.EndsWith("\n"))
{
this.processOutput(this.lineBuffer);
this.lineBuffer = "";
}
AsyncCallback callback = new AsyncCallback(OnReceive);
this.networkStream.BeginRead(buffer, 0, buffer.Length, callback, this.networkStream);
}
// This function gets called every time some data arrives to buffer
private void OnReceive(IAsyncResult data)
{
if (this.networkStream == null)
return;
int bytes = this.networkStream.EndRead(data);
string text = System.Text.Encoding.UTF8.GetString(buffer, 0, bytes);
if (!text.Contains("\n"))
{
this.lineBuffer += text;
}
else
{
List<string> parts = new List<string>(text.Split('\n'));
while (parts.Count > 0)
{
this.lineBuffer += parts[0];
if (parts.Count > 1)
{
this.processOutput(this.lineBuffer + "\n");
this.lineBuffer = "";
}
parts.RemoveAt(0);
}
}
this.resetCallback();
}
As you can see I am using very nasty solution where I am basically checking in every "packet" of data that are received on buffer:
Whether data in buffer are whole line (ends with new line)
Whether data in buffer contains more than 1 line (new line is somewhere in middle of data, or there are more than 1 new line)
Data in buffer contains only a part of a line (no new line in text)
The problem here is that callback function can be called any time when some data are received, and these data can be a line, part of a line, or even multiple lines.
Based on the new line I am storing data in another buffers and when I finally get a new line, I process it somehow.
This is actually working just fine, but I am still wondering if there isn't a better solution that is more clean and doesn't require such a hacking in order to read the stream line by line without using threads?
Please note commenter Damien_The_Unbeliever's point about the issue with partial UTF8 characters. As he says, there's nothing in TCP that would guarantee that you only receive whole characters; a sequence of bytes in the stream can be interrupted at any point, including mid-character.
The usual way to address this would be to using an instance of a Decoder (which you can retrieve from the appropriate Encoding subclass, e.g. Encoding.UTF8.GetDecoder()). A decoder instance will buffer characters for you, returning only whole characters as they are available.
But in your case, there is a much easier way: use the TextReader.ReadLineAsync() method.
For example, here's an asynchronous method which will process each line of text read from the stream, with the returned task for the method completing only when the stream itself has reached the end (i.e. graceful closure of the socket):
async Task ProcessLines()
{
using (StreamReader reader = new StreamReader(
this.networkStream, Encoding.UTF8, false, 1024, true))
{
string line;
while ((line = await reader.ReadLineAsync()) != null)
{
this.processOutput(line);
}
}
// Clean up here. I.e. send any remaining response to remote endpoint,
// call Socket.Shutdown(SocketShutdown.Both), and then close the
// socket.
}
You would call that (preferably awaiting the result in another async method, though that would depend on the exact context of the caller) from wherever you call resetCallback() now. Given the lack of a good, minimal, complete code example a more specific explanation than that can't be provided.
The key is that, being an async method, the method will return as soon as you call ReadLineAsync() (assuming the call doesn't complete immediately), and will resume execution later once that operation completes, i.e. a complete line of text is available and can be returned.
This is the standard idiom now in C# for dealing with this kind of asynchronous operation. It allows you to write the code practically as if you are doing everything synchronously, while the compiler rewrites the code for you to actually implement it asynchronously.
(As an aside: you may want to consider using the usual .NET conventions, i.e. Pascal casing, for method names, instead of the Java-style camel-casing. That will help readers more readily understand your code examples).
I am new here and by no means an expert at c# programming.
I am writing an application that connects to a device over TCP. It sends the device a command and the device responds. Sometimes The device will send another message after it has responded to my command. For example if I say "Read Tag" It will respond with the tag value "Tag: abcdefg". But sometimes, after a couple of hundred milliseconds, it will respond with something like "Buffer Low: 14" telling me the size of its buffer.
Here is how I am currently receiving data:
public Task<string> ReceiveDataAsync()
{
receiveBuffer = new byte[receiveBufferSize];
Task<int> streamTask = _networkstream.ReadAsync(receiveBuffer, 0, receiveBufferSize);
// Since the read is async and data arrival is unknown, the event
// must sit around until there is something to be raised.
var resultTask = streamTask.ContinueWith<String>(antecedent =>
{
Array.Resize(ref receiveBuffer, streamTask.Result); // resize the result to the size of the data that was returned
var result = Encoding.ASCII.GetString(receiveBuffer);
OnDataReceived(new TCPEventArgs(result));
return result;
});
return resultTask;
}
I am confused about reading the network stream. When I use the ReadAsync method, and then I get something back, how do I handle the delay? In my mind, I get the first response of the tag data, then I start to work on that task. Even though I work on the task ".ContinueWith" will my stream continue to receive data? Will the task automatically go back and process more data as it comes in the stream? Do I need to call the ReceiveDataAsync method every time I think some data should be arriving or will it remain open until Dispose of the stream?
Yes, you need to call ReceiveDataAsync repeatedly, usually call it in callback of ContinueWith, or just put it in a loop if you use async/await, so that you read some data, process it and then go back to read (or wait) the next bytes.
Like this:
private static void OnContinuationAction(Task<string> text)
{
Console.WriteLine(text);
ReceiveDataAsync().ContinueWith(OnContinuationAction);
}
...
ReceiveDataAsync().ContinueWith(OnContinuationAction);
Or with async/await:
private async void ReceiveDataContinuously()
{
while(true)
{
var text = await ReceiveDataAsync();
Console.WriteLine(text);
}
}
If you don't call ReadAsync on the stream repeatedly, as long as the underlying TCP connection is open it will continue receiving data into the buffer, but your program cannot get them.
When using C# NamedPipeServerStream, in case a client doesn't send any message-end-pattern (like \r\n when server reads with ReadLine()) NamedPipeServerStream Read methods will wait forever and no Abort() or Interupt() methods will work on that thread.
Since:
1) Stream.ReadTimeout not supported for NamedPipeServerStream
2) Abort() or Interupt() doesn't work on thread
3) NamedPipeServerStream.Disconnect() nether work
It is unclear, how to setup timeout on NamedPipeServerStream read operations?
Let me introduce an example. The specification of IPC we have require an exchange of \0-terminated strings. A client sends message, the server processes the message and as 'a must' sends a response.
If the client doesn't send \0 in the end (client is not ours so we can't guarantee correctness of its working), the Read method will wait forever and client (since we don't control it) may wait forever for a response too.
Next is a simplified example of an implementation:
public void RestartServer()
{
_pipeServerThread.Interrupt(); //doesn't affect Read wait
_pipeServerThread.Abort(); //doesn't affect Read wait
}
private void PipeServerRun(object o) //runs on _pipeServerThread
{
_pipeServer = new NamedPipeServerStream(_pipeName, InOut, 100,
PipeTransmissionMode.Message, PipeOptions.WriteThrough);
//_pipeServer.ReadTimeout = 100; //System.InvalidOperationException: Timeouts are not supporte d on this stream.
// Wait for a client to connect
while (true)
{
_pipeServer.WaitForConnection();
string request = ReadPipeString();
//... process request, send response and disconnect
}
}
/// <summary>
/// Read a \0 terminated string from the pipe
/// </summary>
private string ReadPipeString()
{
StringBuilder builder = new StringBuilder();
var streamReader = new StreamReader(_pipeServer);
while (true)
{
//read next byte
char[] chars = new char[1];
streamReader.Read(chars, 0, 1); // <- This will wait forever if no \0 and no more data from client
if (chars[0] == '\0') return builder.ToString();
builder.Append(chars[0]);
}
}
So how to set timeout on NamedPipeServerStream read operations?
Since you are running the pipe in message mode, you should first read the whole message into a byte[] buffer or a memory stream and then decide whether it's valid and decode it. Pipe messages have a definite length. It cannot be retrieved explicitly, but it shows up when you are reading from a message-mode pipe. Win32 ReadFile fails with ERROR_MORE_DATA if there still are unread bytes in the message, then it returns TRUE to indicate that the message is over. After this, a call to ReadFile will block until a new message is available. StreamReader naturally doesn't know any of this and blocks your thread.
Update: to implement timeouts, use asynchronous I/O (Stream.BeginRead). StreamReader does not support this directly. If you absolutely must use it, write a wrapper stream which will implement Read in terms of BeginRead on the underlying stream and support timeouts, cancellation etc.
Try setting NamedPipeServerStream.ReadMode and/or .TransmissionMode to Byte.
Regardless of these you should use the available BeginRead / EndRead methods with NamedPipeServerStream.
This way you can implement the timeout logic yourself.