As discussed in the previous post, disposing an SSH connection is very important to free the port and leave it usable to the next connection.
Unfortunately, implementing the connector as disposable is not enought, because if the application crash or stop unexpectedly, the dispose method will no be called and the port will remain busy and unusable.
This is the case when we need to check the state of the socket/port and free it before to use it; what we need to do is implement a code that is able to check if the connection is busy and close it before to open a new one.
It’s an operation that we have to execute carefully because we can drop an active connection used by someone but in my case it was a develop enviroment and I needed the port free for the automatic tests execution.
So what we need to do is implement an helper that drop the connection and use it later in the unit tests.
Helper
The C# language give us some classes in order to manage and stop local process, like Process and ProcessStartInfo.
With the first one we can execute some process and commands on the local machine and with the second one we define the command and the parameters.
In this case, we need to check if the connection port 2022 (SSH port) is busy and in order to do that on a windows environment, we have to use the command netstat -ano; the a option means all the connetions, with n we say that we want the address and port number in numerical format and with the o option we can see the process id tied to the connection.
The result of this command looks like this:
What we need to do is retrieve these connections, check if there is one with 2022 port and kill the associated process; we can implement a singleton class:
public static class ConnectionsHelper { public static void KillProcessByPortNumber(int port) { DropConnection(port); } }
Next we need to implement the DropConnection method:
private static void DropConnection(int port) { try { using (var process = new Process()) { var processStartInfo = new ProcessStartInfo(); processStartInfo.FileName = "netstat.exe"; processStartInfo.Arguments = " -ano"; processStartInfo.WindowStyle = ProcessWindowStyle.Hidden; processStartInfo.UseShellExecute = false; processStartInfo.RedirectStandardInput = true; processStartInfo.RedirectStandardOutput = true; processStartInfo.RedirectStandardError = true; process.StartInfo = processStartInfo; process.Start(); var netStatContent = process.StandardOutput.ReadToEnd(); foreach (string netStatRow in Regex.Split(netStatContent, "\r\n")) { var tokens = Regex.Split(netStatRow, "\\s+"); if (tokens.Length>4 && tokens[1].Equals("TCP") && Int32.TryParse(tokens[5], out int _) && Int32.TryParse(tokens[3].Split(':')[1], out int _) && Convert.ToInt32(tokens[3].Split(':')[1]) == port) { try { KillProcessById(tokens[5]); } catch (Exception) { throw; } } } } } catch (Exception) { throw; } }
You can see that you can define the command and the arguments with the ProcessStartInfo class and then retrieve the standard output as a string.
Then we need to parse the string result, split every single row and seek a connection (in my case TCP) with a specific port (2022) and with a valid process id associated.
If we find it, we can proceed by killing the process with this method:
private static void KillProcessById(string processId) { try { using (var process = new Process()) { ProcessStartInfo StartInfo = new ProcessStartInfo(); StartInfo.FileName = "taskkill.exe"; StartInfo.Arguments = $" /f /pid {processId}"; StartInfo.WindowStyle = ProcessWindowStyle.Hidden; StartInfo.UseShellExecute = false; process.StartInfo = StartInfo; process.Start(); } } catch (Exception) { throw; } }
The implementation is quite short, we use the taskkill command to kill a process by the id.
Now we can use this helper in a test unit.
Unit tests
I’m using NUnit as test framework, so I can leverage SetUpFixture and OneTimeSetup attributes to esecute my helper and close pending SSH connections:
[SetUpFixture] public class SetupFixture { [OneTimeSetUp] public void SetUp() { ConnectionsHelper.KillProcessByPortNumber(2022); ...... } [OneTimeTearDown] public void TearDown() { ...... } }
The setup class is valid for all the unit tests in the same namespace, so we need to consider this feature when we need a general setup for a bunch of tests.
With the OneTimeSetUp attribute we says to execute the method only one time before to execute any test in a fixture, so we’ll sure that the check will be executed prior any unit test execution.
You can find the source code here.
Leave a Reply