SerialPort.DataReceived repeatedly subscribe/unsubscribe - c#

I am using a serial port to communicate with a remote diagnostics device.
The length of the response from the remote device varies depending upon the command but is known ahead of time. So, currently I send the command and wait for the required number of response bytes to be received.
I subscribe to the 'SerialPort.DataReceived' event whenever I'm not actively soliciting data. The handler for this event simply dumps any 'unsolicited' received data to a log (unsolicited data is typically only received if the remote device restarts unexpectedly, etc).
In some cases I want to send commands at a rate of about 60Hz.
My question is whether it's best to unsubscribe/subscribe to the 'SerialPort.DataReceived' event every time I call my 'SendCommand' method to actively solicit data, or should I leave the event subscription alone and just toggle a boolean 'TransferInProgress' flag that the DataReceived handler can use to ignore incoming data when I'm actively soliciting it?
Here's the current implementation:
public virtual bool SendCommand(byte[] command, ref byte[] response) {
try {
TransferInProgress = true;
OnTransferStarted();
// temporarily unsubscribe since we're actively soliciting data
_port.DataReceived -=
new SerialDataReceivedEventHandler(SerialPort_DataReceived);
_port.DiscardInBuffer();
_port.Write(command, 0, command.Length);
OnCommandSent(command);
// read the requested number of response bytes
int responseBytesRead = 0;
while (responseBytesRead < response.Length) {
responseBytesRead +=
_port.Read(response, responseBytesRead, (response.Length - responseBytesRead));
}
OnCommandResponseReceived(response);
return true;
}
catch (Exception ex) {
OnCommandSendFailed(ex.Message);
return false;
}
finally {
_port.DataReceived +=
new SerialDataReceivedEventHandler(SerialPort_DataReceived);
OnTransferComplete();
TransferInProgress = false;
}
}
-Trevor

Have you thought about handling all of your data reception in one place? You could treat the commands you send as fire and forget, parsing the data received for the responses. If the responses do not have an identifying header and the ONLY way you know how to parse them is by knowing which command you sent and the length of the response, then you could keep track of the commands sent in a queue. The way that would work, is that in your Data Received handler you would check the queue of commands you're waiting on a response for, and then parse the data received like you do now.
Long story short, I would recommend handling all incoming data in one place.

My opinion if I'm understanding correctly would be to simply handle all your receiving data in the DataReceived handler or you have one other options.
If the data received between actual request isn't much you could just read the buffer and log it before transmitting your request. The serial driver receive buffer may be enough to store a small amount of data. Then send the request and read in just the response. This will probable be the easier method and simpler code.

I normally toggle a boolean. With subscribing/unsubscribing you run the risk of subscribing to the same event more than once. For instance in your code if OnTransferStarted() throws an exception you will subscribe twice to the DataReceived event.

Related

How to Properly Read from a SerialPort in .NET

I'm embarrassed to have to ask such a question, but I'm having a rough time figuring out how to reliably read data over a serial port with the .NET SerialPort class.
My first approach:
static void Main(string[] args)
{
_port = new SerialPort
{
PortName = portName,
BaudRate = 57600,
DataBits = 8,
Parity = Parity.None,
StopBits = StopBits.One,
RtsEnable = true,
DtrEnable = false,
WriteBufferSize = 2048,
ReadBufferSize = 2048,
ReceivedBytesThreshold = 1,
ReadTimeout = 5000,
};
_port.DataReceived += _port_DataReceived;
_port.Open();
// whatever
}
private void _port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var buf = new byte[_port.BytesToRead];
var bytesRead = _port.Read(buf, 0, buf.Length);
_port.DiscardInBuffer();
for (int i = 0; i < bytesRead; ++i)
{
// read each byte, look for start/end values,
// signal complete packet event if/when end is found
}
}
So this has an obvious problem; I am calling DiscardInBuffer, so any data which came in after the event was fired is discarded, i.e., I'm dropping data.
Now, the documentation for SerialPort.Read() does not even state if it advances the current position of the stream (really?), but I have found other sources which claim that it does (which makes sense). However, if I do not call DiscardInBuffer I eventually get an RXOver error, i.e., I'm taking too long to process each message and the buffer is overflowing.
So... I'm really not a fan of this interface. If I have to process each buffer on a separate thread I'll do that, but that comes with its own set of problems, and I'm hoping that I am missing something as I don't have much experience with this interface.
Jason makes some good points about reducing UI access from the worker thread, but an even better option is to not receive the data on a worker thread in the first place.
Use port.BaseStream.ReadAsync to get your data, event-driven, on the thread where you want it. I've written more about this approach at http://www.sparxeng.com/blog/software/must-use-net-system-io-ports-serialport
To correctly handle data from a serial port you need to do a couple of things.
First, don't handle the data in your receive event. Copy the data somewhere else and do any processing on another thread. (This is true of most events - it is a bad idea to do any time-consuming processing in an event handler as it delays the caller and can introduce problems. You also need to be careful as your event is raised on a different thread to your main application)
Secondly, you can't guarantee that you will receive exactly one packet, or a complete packet when you receive data - it may come to you in small fragments.
So the upshot of this is that you should create your own buffer (big enough to hold several packets), and when you receive data, append it to your buffer. Then in another thread you can process the buffer, looking to see if you can decode a packet from it and then consume that data. You may have to skip the end of a partial packet before you find the start of a valid one. If you don't have enough data to build a full packet, then you may need to wait for a bit until more data arrives.
You shouldn't call Discard on the port - just read the data and consume it. Each time you are called, there will be another fragment of data to process. It does not remember the data from previous calls - each time your event is called, it is given a small burst of data that has arrived since you were last called. Just use the data you've been given and return.
As a last suggestion: Don't change any settings for the port unless you specifically need to for it to operate properly. So you must set the baud rate, data/stop bits and parity, but avoid trying to change properties like the Rts/Dtr, buffer sizes and read thresholds unless you have a good reason to think you know better than the author of the serial port. Most serial devices work in an industry standard manner these days, and changing these low-level options is very likely to cause trouble unless you're talking to some unusual equipment and you intimately know the hardware.
In particular setting the ReceivedBytesThreshold to 1 is probably what is causing the failure you've mentioned, because you are asking the serial port to call your event handler with only one byte at a time, 57,600 times per second - giving your event handler only 0.017 milliseconds to process each byte before you'll start to get re-entrant calls.
DiscardInBuffer is typically only used immediately after opening a serial port. It is not required for standard serial port communication so you should not have it in your dataReceived handler.

Sending data in order with SocketAsyncEventArgs

I originally had a race condition when sending data, the issue was that I was allowing multiple SocketAsyncEventArgs to be used to send data, but the first packet didn't send fully before the 2nd packet, this is because I have it so if the data doesn't fit in the buffer it loops until all the data is sent, and the first packet was larger than the second packet which is tiny, so the second packet was being sent and reached to the client before the first packet.
I have solved this by assigning 1 SocketAyncEventArgs to an open connection to be used for sending data and used a Semaphore to limit the access to it, and make the SocketAsyncEventArgs call back once it completed.
Now this works fine because all data is sent, calls back when its complete ready for the next send. The issue with this is, its causing blocking when I want to send data randomly to the open connection, and when there is a lot of data sending its going to block my threads.
I am looking for a work around to this, I thought of having a Queue which when data is requested to be sent, it simply adds the packet to the Queue and then 1 SocketAsyncEventArgs simply loops to send that data.
But how can I do this efficiently whilst still being scalable? I want to avoid blocking as much as I can whilst sending my packets in the order they are requested to be sent in.
Appreciate any help!
If the data needs to be kept in order, and you don't want to block, then you need to add a queue. The way I do this is by tracking, on my state object, whether we already have an active send async-loop in process for that connection. After enqueue (which obviously must be synchronized), just check what is in-progress:
public void PromptToSend(NetContext context)
{
if(Interlocked.CompareExchange(ref writerCount, 1, 0) == 0)
{ // then **we** are the writer
context.Handler.StartSending(this);
}
}
Here writerCount is the count of write-loops (which should be exactly 1 or 0) on the connection; if there aren't any, we start one.
My StartSending tries to read from that connection's queue; if it can do so, it does the usual SendAsync etc:
if (!connection.Socket.SendAsync(args)) SendCompleted(args);
(note that SendCompleted here is for the "sync" case; it would have got to SendCompleted via the event-model for the "async" case). SendCompleted repeats this "dequeue, try send async" step, obviously.
The only thing left is to make sure that when we try to dequeue, we note the lack of action if we find nothing more to do:
if (bufferedLength == 0)
{ // nothing to do; report this worker as inactive
Interlocked.Exchange(ref writerCount, 0);
return 0;
}
Make sense?

How do I know when an asynchronous socket read ends?

I have an asynchronous read method...
private void read(IAsyncResult ar) {
//Get the Server State Object
ServerState state = (ServerState)ar.AsyncState;
//read from the socket
int readCount = state.socket.EndReceive(ar);
//check if reading is done, move on if so, trigger another read if not
if (readCount > 0) {
//purge the buffer and start another read
state.purgeBuffer();
state.socket.BeginReceive(state.buffer, 0, ServerState.bufferSize, 0, new AsyncCallback(read), state);
}
else {
//all bytes have been read, dispatch the message
dispatch(state);
}
}
The problem that I am having is that read is only 0 if the connection is closed. How do I say, this is the end of that message and pass the data on to the dispatcher, while leaving the socket open to accept new messages.
Thank you!
You should not rely on what is in the TCP buffer. You must process the incoming bytes as a stream somewhere. You can't really know whether its complete. Only one layer above can know when the message completed.
Example:
If you read HTTP responses the HTTP header will contain the byte count which is in the HTTP body. So you know how much to read.
You only know how much to read if the data follows a certain protocol and you interprete it. Imagine you receive a file over the socket. The first thing you would receive is the file size. Without that you would never know how much to read.
You should make your messages fit a particular format so that you can distinguish when they start and when end. Even if it is a stream of data it should be sent in packets.
One option is to send length of message first and then you know how much data to expect. But problem with that is if you loose sync you can never recover and you will never know what is message length and what is its content. It is good to use some special marking sequence to know when message begins. It is is not 100% error proof (sequence might appear in data) but certainly helps and allows to recover from sync loose. This is particularly important when reading from a binary stream like socket.
Even ancient RS232 serial protocol had its frame and stop bit to know when you got all the data.

C# Sockets. Sending data through TCP/IP on slow connections

I have a TCP/IP connection between a server and a client. Once the connection is established, the server will start sending a lot of data to the client. The problem that I'm having is if the connection is slow, at some point the server will become unresponsive. I guess this is because it's buffering data waiting for the data that has already been sent to be acknowledged.
Is there are way before trying to send data to check the size of the current buffer, so I know I should wait before continuing sending data?
Thanks.
This is what I'm doing to send data:
System.Net.Sockets.Socket ClientSocket;
...
public void Send(byte[] data, int size)
{
try
{
SocketAsyncEventArgs e = new SocketAsyncEventArgs();
e.SetBuffer(data, 0, size);
bool pending = ClientSocket.SendAsync(e);
}
catch (Exception ex)
{
}
}
The documentation for SendAsync says that it will raise the SocketAsyncEventArgs.Completed event on the e parameter when the operation is complete. So if you write an event handler that responds to that event, you can always know how many operations you have outstanding.
There's a reasonably good example of using the event here: http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx
I found the solution. Basically I should only send data when
ClientSocket.Poll(0, SelectMode.SelectWrite) is true.
Easy and effective!

C# How to implement waiting for a confirmation in data communication?

I have an class which should send/receive data in packet form. This class contains an event handler which runs when new data is available to be read from the physical medium.
In the event handler I read the data from the medium and parse the available data for complete packets. Once a packet is identified an event is raised to pass the new packet to subscribers. The subscribers to this event decide if they want to use the packet or not.
So far so good... Now to my question. While the scenario above works to distribute the incoming data to a multitude of subscribers and place further processing logic further up in the application it leaves me with a problem:
Sometimes the class will receive a data packet which is just a reply (think ACK/FAIL) to another packet which was sent by the class.
How would I implement a method to send something which would wait for such a confirmation while not breaking the aforementioned concept of handling incoming raw data in the event handler?
Some pseudo code to illustrate the problem:
class CommCenter
{
public event NewPacketAvailable;
private void _newRawDataAvailable_EventHandler
{
// read incoming data from physical medium
// parse incoming data for packet structure
// the received packet may either contain a reply to
// a previously sent message or unrelated
// if packet found raise NewPacketAvailable event
// so that subscribers can handle the packet
}
public void SendMessage(DataPacket packet, Int32 timeoutInMilliseconds)
{
// put message on the physical medium
// wait for a packet confirming the previously sent packet
// How would I have to do this??
// the packet confirmation would arrive in _newRawDataAvailable_EventHandler
}
}
Maybe has a good idea or even implemented such a logic before. I'm a bit confused at the moment what to do. Glad for any help or pointers in the right direction ;-)
Okay, this is how it works:
In the event handler, read the data from the medium. Check if it's data or just a confirmation of a previous packet (ACK).
If it is data, pass it on.
If it is ACK, put it into a special Queue for ACKs.
In the SendMessage method iterate over this ACK Queue after sending your message until:
Timeout occurs
The confirmation for your sent packet arrives
That's it.

Categories

Resources