Why does 'using' statement not seem to release serial port? - c#

I am developing a C# WinForms Windows application that runs from the tray. I need to provide some reasonable level of error handling and instruction to the user. In order to test if I am able to open a serial port for communication, I wish to have a way to test if it is already open or if it is unopenable for whatever reason.
I came up with this:
if (SerialPort.GetPortNames().Select((n) =>
n.ToUpperInvariant()).Contains(mycomportname))
{
// Port found, check to see if we can use it by test-opening
using (var sp = new SerialPort(mycomportname))
{
// Check to see if we can open this port
try
{
if (sp.IsOpen) throw new Exception("Serial port is already open");
sp.Open();
sp.Close();
}
catch (Exception ex)
{
throw new Exception("Serial port is in use");
}
}
}
else
{
// ...
}
commManager.PortName = mycomportname;
if (commManager.OpenPort())
{
// .. always returns false because causes UnauthorizedAccessException on open
}
For some reason the serial port does not seem to be fully released by the 'using' statement. The UnauthorizedAccessException does not occur when I delete the using statement and the statements inside it. How do I write robust error-tolerant serial port opening code?

The MSDN article for SerialPort warns about this explicitly, albeit vaguely. SerialPort uses a worker thread to generate events like DataReceived and ErrorReceived. That thread gets started when you call Open() but it needs time to exit again after you call Close() or Dispose(). The physical port is in use until that happens. Exactly how long that takes is unpredictable. Usually within a millisecond but the worst-case is seconds when the machine is heavily loaded. Your code only waits for a nanosecond so you'll always get an exception.
The approach otherwise just doesn't make sense. Once you opened the port and got no exception then just keep it open. No point in closing it again and reopening it. Which is the simple solution.
And never do this kind of port scanning when GetPortNames() returns more than one port. The odds that the first one will open are very high, the odds that it is the right one are low. Murphy ensures that fifty-fifty odds turn into 1%. You always need to provide a config file so the user can pick the correct one. Only consider doing the port scanning when you populate a combobox with choices in a config helper window. Only skimp on this if you are in control over the machine configuration, that's pretty rare.

Related

Try Catch exception in a locked block of code

I have an issue when there is an exception in a block of code that uses a lock. I am reading and writing to a serial port and there are several different threads that need access to the same serial port. This is managed by a lock. I have no issues except if the serial port stops working. This can happen since the software controls an RF transmitter and occasionally the rf can cause usb to serial ports to stop functioning. If you then attempt to write to the port you will get a write timeout. I tried handling this from a try - catch exception handler. However, the program locks hard at that point and has to have the task killed. I am not sure if this is coming from the exception or the message box I am trying to display since it could result from a background thread. Here is the code:
lock (_object)
{
try
{
if (portOpened)
{
port.Write(data);
}
else
{
MessageBox.Show("The radio is not connected. Please select a ComPort in the settings dialog");
}
}
catch (Exception x) //this will capture a write exception.
{
MessageBox.Show("The program is unable to write to the serial port. Select OK to close the program";
Application.Exit();
}
finally
{
}
}
Thanks for any help
If you want to force to exit the app, Application.Exit is not a good candidate, as it simply pushes the Close request on message queues of all threads, but does not force anything. If you want to stop app at any cost, use Environment.Exit(exitCode), or may be even better Environment.FailFast, with specified exception so it will be logged into the system's log: convenient for future investigations.

No idea why tcpClient isn't working for me

I've tried checking the server:port with telnet and I'm getting the expected results. So either writer.Write() or reader.ReadLine() isn't working cause I get nothing from the server.
TcpClient socket = new TcpClient(hostname, port);
if (!socket.Connected) {
Console.WriteLine("Failed to connect!");
return;
}
TextReader reader = new StreamReader(socket.GetStream());
TextWriter writer = new StreamWriter(socket.GetStream());
writer.Write("PING");
writer.Flush();
String line = null;
while ((line = reader.ReadLine()) != null) {
Console.WriteLine(line);
}
Console.WriteLine("done");
EDIT: I might have found the issue. This code was based off examples I found on the web. I tried another irc server: open.ircnet.net:6669 and I got a response:
:openirc.snt.utwente.nl 020 * :Please wait while we process your connection.
It seems as if I probably need to run the reader in a Thread so it can just constantly wait for a response. However it does seem weird that the program got caught on the while loop without ever printing done to the console.
I think you need to provide further details. I'm just going to assume that because you can easily telnet to the server using the same port your problem lies in the evaluation of the Connected property...
if (!socket.Connected) {
Console.WriteLine("Failed to connect!");
return;
}
this is wrong because Microsoft clearly specifies in the documentation that the Connected property is not reliable
Because the Connected property only reflects the state of the connection as of the most recent operation, you should attempt to send or receive a message to determine the current state. After the message send fails, this property no longer returns true. Note that this behavior is by design. You cannot reliably test the state of the connection because, in the time between the test and a send/receive, the connection could have been lost. Your code should assume the socket is connected, and gracefully handle failed transmissions.
That said, you should not use this property to determine the state of the connection. Needless to say that using this property to control the flow of your console app will result in unexpected results.
Suggestion
Remove the evaluation of the Connected property
Wrap your GetStream and Write method calls in a try/catch block to handle network communication errors
reader.ReadLine() will just wait for any data to arrive. If no data arrive, it seems to hang. That's a feature of tcp (I don't like it either). You need to find out how the end of the message is defined and stop based on that end criterion. Be careful, the end of message identifier may be split into two or more lines...
RFC for ping says that the server may not respond to it & such connections has to be closed after a time. Please check the RFC: https://www.rfc-editor.org/rfc/rfc1459#section-4.6.2

Is it good practice to put try-catch in a loop until all statements in the try block is executed without any exceptions?

I was trying to develop a multicast receiver program and socket initialization was done as shown below:
public void initializeThread()
{
statuscheckthread = new Thread(SetSocketOptions);
statuscheckthread.IsBackground = true;
}
private void Form1_Load(object sender, EventArgs e)
{
rxsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
iep = new IPEndPoint(IPAddress.Any, 9191);
rxsock.Bind(iep);
ep = (EndPoint)iep;
initializeThread();
statuscheckthread.Start();
}
public void SetSocketOptions()
{
initializeThread(); //re-initializes thread thus making it not alive
while (true)
{
if (NetworkInterface.GetIsNetworkAvailable())
{
bool sockOptnSet = false;
while (!sockOptnSet)
{
try
{
rxsock.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("224.50.50.50")));
rxsock.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 64);
sockOptnSet = true;
}
catch
{
//Catch exception here
}
}
}
break; // Break out from loop once socket options are set
}
}
When my PC is not connected to a network, SetSocketOption method was throwing exception and even after network is connected,
I was unable to receive data because socket options are not set.
To avoid this I used a thread which runs in the background checking
for network availability and once network is available, it sets the socket options.
It works properly in some PC's but in some others, NetworkInterface.GetIsNetworkAvailable()
returned true before network got connected
(while network was being identified) .
So, to make sure Socket options are set, I used a bool variable sockOptnSet
which is set as
true if all the statements in the try block is executed as shown inside the method public void SetSocketOptions()
This program works fine in all PC's I tried, but I am doubtful about how much I can rely on this to work.
My questions are:
1) Is this good practice?
2) If not, what are the possible errors or problems it may cause? And how can I implement it in a better way?
Is this a good practice?
No, not a good practice. The vast majority of exceptions, including your first one, fall in the category of vexing exceptions. Software is supposed to work, worked well when you tested it, but doesn't on the user's machine. Something went wrong but you do not know what and there isn't anything meaningful that you can do about it. Trying to keep your program going is not useful, it cannot do the job it is supposed to do. In your case, there's no hope that the socket is ever going to receive data when there is no network. And, as you found out, trying to work around the problem just begets more problems. That's normal.
If this is bad practice, how can I implement it in a better way?
You need help from a human. The user is going to have to setup the machine to provide a working network connection. This requires a user interface, you must have a way to tell a human what he needs to do to solve your problem. You can make that as intricate or as simple as you desire. Just an error message, a verbatim copy of the Exception.Message can be enough. Writing an event handler for the AppDomain.CurrentDomain.UnhandledException event is a very good (and required) strategy. Microsoft spent an enormous amount of effort to make exception messages as clear and helpful as possible, even localizing them for you in the user's native language, you want to take advantage of that. Even if the exception message is mystifying, a quick Google query on the message text returns hundreds of hits. With this event handler in place, you don't have to do anything special. Your program automatically terminates and your user knows what to do about it.
You can certainly make it more intricate, you discovered that SetSocketOption() is liable to fail right after the network becomes available but works when you wait long enough. So this is actually an error condition that you can work around, just by waiting long enough. Whether you should write the code to handle this is something that you have to decide for yourself. It is something you write when you have enough experience with the way your program behaves, you never write it up front. Usually as a result from feedback from the users of your program.
Some good advice in the comments, lets' expand on it.
Firstly, I would put all this socket code in to its' own class, outside of the form. This makes it its' own entity and semantically easier to understand. This class could have a property Initialised, which is initially set to false. The first thing you do in your form is call an Initialise method on this class which attempts to set socket options and catches the relevant exceptions if the network is not available. If it is available, we set our Initialised property to true.
If not available, we set a single timeout (see System.Threading.Timer) that calls this same function (potentially with a retry count) after 'x' seconds. Once again we'll find ourselves back in this Initialise function, perhaps with a retry count mentioned at the beginning. Once again, if it is available, we're good - if not, set the timer again. Eventually, after 'x' retries if we're not initialised we can throw an exception or set some other failure property to indicate that we can't proceed.
Your Form class can periodically check (or hook in to an event) to determine whether the socket is now ready for communication. In case of failure you can gracefully quit out, or because our class is nice and abstracted, attempt to start the whole process again.

Detecting unexpected socket disconnect

This is not a question about how to do this, but a question about whether it's wrong what I'm doing. I've read that it's not possible to detect if a socket is closed unexpectedly (like killing the server/client process, pulling the network cable) while waiting for data (BeginReceive), without use of timers or regular sent messages, etc. But for quite a while I've been using the following setup to do this, and so far it has always worked perfectly.
public void OnReceive(IAsyncResult result)
{
try
{
var bytesReceived = this.Socket.EndReceive(result);
if (bytesReceived <= 0)
{
// normal disconnect
return;
}
// ...
this.Socket.BeginReceive...;
}
catch // SocketException
{
// abnormal disconnect
}
}
Now, since I've read it's not easily possible, I'm wondering if there's something wrong with my method. Is there? Or is there a difference between killing processes and pulling cables and similar?
It's perfectly possible and OK to do this. The general idea is:
If EndReceive returns anything other than zero, you have incoming data to process.
If EndReceive returns zero, the remote host has closed its end of the connection. That means it can still receive data you send if it's programmed to do so, but cannot send any more of its own under any circumstances. Usually when this happens you will also close your end the connection thus completing an orderly shutdown, but that's not mandatory.
If EndReceive throws, there has been an abnormal termination of the connection (process killed, network cable cut, power lost, etc).
A couple of points you have to pay attention to:
EndReceive can never return less than zero (the test in your code is misleading).
If it throws it can throw other types of exception in addition to SocketException.
If it returns zero you must be careful to stop calling BeginReceive; otherwise you will begin an infinite and meaningless ping-pong game between BeginReceive and EndReceive (it will show in your CPU usage). Your code already does this, so no need to change anything.

SerialPort UnauthorizedAccessException

Occasionally some of my integration tests are failing with the above message. I'm using the code below to ready the port.
for(int i = 0; i < 5; i++)
{
try
{
port.Open();
if (port.IsOpen)
break;
}
catch (Exception e)
{
try
{
port.Close();
}
catch (Exception)
{}
Thread.Sleep(300);
}
}
My assumption is that because it can't be the current thread blocking the port (because it will try to close it), it must be another thread or process that has died without cleaning up properly (one of the other tests - nothing else accesses this port). Is there a way to reset the state of the SerialPort so that the new thread / process can access it again?
Thanks,
Richard
This is a flaw in the SerialPort class, it uses an internal helper thread to wait for events on the port. The source of the DataReceived, PinChanged and ErrorReceived events. The flaw is in the Close() method implementation, it doesn't wait for this helper thread to terminate. That takes time, the exact amount of time is not predictable and could be many seconds when the machine is particularly busy. The physical port doesn't get closed until this happens, opening the port before the thread exits bombs with a 'port already in use' exception. The one you get. Sleeping for 300 msec is thus not good enough.
This is not normally an issue, serial ports are not sharable devices. Closing a serial port and not exiting your program is dangerous, another process could steal the port. Also giving you this exception when you try to open it again. The normal practice is to open the port when your app starts and not close it until it terminates.
I routinely verify that the port is closed just before I instantiate a serial port. This helps if you stop debugging code without closing the serial port. Also you should wait 250 msec after opening or closing the port before you continue with your code.
try
{
if (m_SerialPort != null)
{
if (m_SerialPort.IsOpen)
{
m_SerialPort.Close();
}
}
m_SerialPort = new SerialPort(portName, dataRate, parity, databits, stopBits.One);
m_SerialPort.Open();
if (!m_SerialPort.IsOpen)
{
MessageBox.Show(string.Concat(portName, " failed to open"));
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
I can't see where you close the port.
The issue for me is not here (even if you should refactor a bit the code) but probably you are calling port.Open(); when the port is still open
From MSDN
Only one open connection can exist per SerialPort object.
(I can't tell you why because I don't have enough information) Bear also in mind the the close method takes some time to actually close the port in fact you should block the main thread until the port has been close (perhaps using Thread.Join)
From MSDN
The best practice for any application is to wait for some amount of time after calling the Close method before attempting to call the Open method, as the port may not be closed instantly.
for more info
http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.open.aspx
GC.SuppressFinalize should be called passing the SerialPort instance BaseStream property as the parameter and not just the SerialPort instance.
public class SerialConnection : SerialPort
{
public new void Dispose()
{
if (_isDisposed)
return;
_isDisposed = true;
BaseStream.Dispose();
GC.SuppressFinalize(BaseStream);
base.Dispose();
GC.SuppressFinalize(this);
}
The new void Dispose() of course is not the recommended way to implement IDisposable. It's merely a fix for the odd behaviour of the SerialPort class.

Categories

Resources