Checking connection state with WinSCP .NET assembly in C# - c#

I have a method for retrying the connection of WinSCP in C#.
How would I know if the state of my connection is opened or closed? Are there methods for this in WinSCP .Net?
using (Session session = new Session())
{
try
{
session.Open(sessionOptions);
}
catch(Exception ex)
{
//I want to reconnect here if my connection
//is timed out
//I want to reconnect here 3 times
}
// Upload file
session.PutFiles(#"C:\Test\files.dat", "var/test/files.dat");
// I want also to reconnect here if my upload failed
// reconnect to the server then upload the files that
// did not upload because of the connection errors
}

In your code, you already know, if the connection succeeded or not. Anyway, you can check Session.Opened, if you want to test explicitly for some reason.
using (Session session = new Session())
{
int attempts = 3;
do
{
try
{
session.Open(sessionOptions);
}
catch (Exception e)
{
Console.WriteLine("Failed to connect - {0}", e);
if (attempts == 0)
{
// give up
throw;
}
}
attempts--;
}
while (!session.Opened);
Console.WriteLine("Connected");
}
The file transfer operations, like the Session.PutFiles, reconnect automatically, if a connection is lost during transfer.
How long it will keep reconnecting is specified by Session.ReconnectTime.

Related

Azure Function SSH.Net Socket read operation has timed out

I'm attempting to connect from a timed Azure function to a 3rd party SFTP server that I have access to, but do not control. My function runs successfully locally when using the azure functions emulator, however I receive an exception ("Socket read operation has timed out after 30000 milliseconds.") when attempting to run in Azure.
Is there anything from a networking perspective I need to do to allow/set up outbound SFTP connections, or does anyone see anything wrong with my code below?
var ftpHost = Environment.GetEnvironmentVariable("SFTP:Server");
var ftpUser = Environment.GetEnvironmentVariable("SFTP:User");
var ftpPass = Environment.GetEnvironmentVariable("SFTP:Password");
var ftpDirectory = Environment.GetEnvironmentVariable("SFTP:WorkingDirectory");
log.Info($"Connecting to {ftpHost}"); //This outputs the correct values I would expect from my app settings
using (var sftp = new SftpClient(ftpHost, ftpUser, ftpPass))
{
sftp.Connect(); //This throws the exception
log.Info("Connected");
var files = sftp.ListDirectory(ftpDirectory);
log.Info("Directory listing successful");
var exceptions = new List<Exception>();
foreach (var file in files.Where(f => f.IsRegularFile))
{
try
{
log.Info($"{file.FullName} - {file.LastWriteTimeUtc}");
var records = Process(sftp, file);
log.Info($"Parsed {records.Count} records");
sftp.DeleteFile(file.FullName);
log.Info($"Deleted {file.FullName}");
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
if (exceptions.Any())
{
throw new AggregateException(exceptions);
}
}
Edit
I did leave my failing code out there and the failures appear to be intermittent. Running every 15 minutes, I have a roughly 50% success rate. In the last 20 attempts, 10 have succeeded.

TLsharp CHANNELS_TOO_MUCH Exception

I have been using TLSharp library for a week but recently I am encountering the Exception:
CHANNELS_TOO_MUCH
My code can't get pass the await client.connect() function even. I haven't found any documentation on the GitHub repository of the library that describes why this exception occurs. I seems it's not a Exception that occurs because of telegram limitation because it gives me this exception at connect function.
Here is my code:
public static async Task<TelegramClient> connectTelegram()
{
store = new FileSessionStore();
client = new TelegramClient(store, numberToAuthenticate, apiId, apiHash);
try
{
await client.Connect();
}
catch (InvalidOperationException e)
{
Debug.WriteLine("Invalid Operation Exception");
if (e.Message.Contains("Couldn't read the packet length"))
{
Debug.WriteLine("Couldn't read the packet length");
Debug.WriteLine("Retying to Connect ...");
}
await connectTelegram();
}
catch (System.IO.IOException)
{
Debug.WriteLine("IO Exception while Connecting");
Debug.WriteLine("Retrying to Connect ...");
await connectTelegram();
}
catch(Exception e)
{
Debug.WriteLine(e.Message):
}
return client;
}
This exception is not documented yet. I encountered this exception when I tried to use the same session file for connecting to telegram and calling requests. It seems when a session file is used by different and multiple clients the session file becomes corrupted. All you have to do is deleting the session file and recreate it as you have created it before.
Here is an example of doing that:
FileSessionStore store;
TelegramClient client;
store = new FileSessionStore();
client = new TelegramClient(store, numberToAuthenticate, apiId, apiHash);
await client.Connect();

How to reconnect to a socket gracefully

I have a following method that connects to an end point when my program starts
ChannelSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var remoteIpAddress = IPAddress.Parse(ChannelIp);
ChannelEndPoint = new IPEndPoint(remoteIpAddress, ChannelPort);
ChannelSocket.Connect(ChannelEndPoint);
I also have a timer that is set to trigger every 60 seconds to call CheckConnectivity, that attempts to send an arbitrary byte array to the end point to make sure that the connection is still alive, and if the send fails, it will attempt to reconnect.
public bool CheckConnectivity(bool isReconnect)
{
if (ChannelSocket != null)
{
var blockingState = ChannelSocket.Blocking;
try
{
var tmp = new byte[] { 0 };
ChannelSocket.Blocking = false;
ChannelSocket.Send(tmp);
}
catch (SocketException e)
{
try
{
ReconnectChannel();
}
catch (Exception ex)
{
return false;
}
}
}
else
{
ConnectivityLog.Warn(string.Format("{0}:{1} is null!", ChannelIp, ChannelPort));
return false;
}
return true;
}
private void ReconnectChannel()
{
try
{
ChannelSocket.Shutdown(SocketShutdown.Both);
ChannelSocket.Disconnect(true);
ChannelSocket.Close();
}
catch (Exception ex)
{
ConnectivityLog.Error(ex);
}
ChannelSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var remoteIpAddress = IPAddress.Parse(ChannelIp);
ChannelEndPoint = new IPEndPoint(remoteIpAddress, ChannelPort);
ChannelSocket.Connect(ChannelEndPoint);
Thread.Sleep(1000);
if (ChannelSocket.Connected)
{
ConnectivityLog.Info(string.Format("{0}:{1} is reconnected!", ChannelIp, ChannelPort));
}
else
{
ConnectivityLog.Warn(string.Format("{0}:{1} failed to reconnect!", ChannelIp, ChannelPort));
}
}
So how I'd test the above, is to physically unplug the LAN cable from my ethernet device, allowing my code to attempt to reconnect (which fails obviously) and reconnect back the LAN cable.
However, even after reconnecting the LAN cable (able to ping), ChannelSocket.Connect(ChannelEndPoint) in my Reconnect method always throws this error
No connection could be made because the target machine actively refused it 192.168.168.160:4001
If I were to restart my whole application, it connects successfully. How can I tweak my reconnect method such that I don't have to restart my application to reconnect back to my Ethernet device?
If an application closes a TCP/IP port, the protocol dictates that the port stays in TIME_WAIT state for a certain duration (default of 240 seconds on a windows machine).
See following for references -
http://en.wikipedia.org/wiki/Transmission_Control_Protocol
http://support.microsoft.com/kb/137984
http://www.pctools.com/guides/registry/detail/878/
What this means for your scenario - is that you cannot expect to close (willingly or unwillingly) and re-open a port within a short period of time (even several seconds). Despite some registry tweaks which you'd find on internet.. the port will be un-available for any app on windows, for a minimum of 30 seconds. (Again, default is 240 seconds)
Your options - here are limited...
From the documentation at http://msdn.microsoft.com/en-us/library/4xzx2d41(v=vs.110).aspx -
"If the socket has been previously disconnected, then you cannot use this (Connect) method to restore the connection. Use one of the asynchronous BeginConnect methods to reconnect. This is a limitation of the underlying provider."
The reason why documentation suggests that BeginConnect must be used is what I mentioned above.. It simply doesn't expect to be able to establish the connection right away.. and hence the only option is to make the call asynchronously, and while you wait for the connection to get established in several minutes, do expect and plan for it to fail. Essentially, likely not an ideal option.
If the long wait and uncertainty is not acceptable, then your other option is to somehow negotiate a different port between the client and server. (For example, in theory you could use UDP, which is connectionless, to negotiate the new TCP port you'd re-establish the connection on). Communication using UDP, in theory of course, itself is not guaranteed by design. But should work most of the times (Today, networking in typical org is not that flaky / unreliable). Subjective to scenario / opinion, perhaps better than option 1, but more work and smaller but finite chance of not working.
As suggested in one of the comments, this is where application layer protocols like http and http services have an advantage. Use them, instead of low level sockets, if you can.
If acceptable, this is the best option to go with.
(PS - FYI - For HTTP, there is a lot of special handling built into OS, including windows - For example, there is a dedicated driver Http.sys, specially for dealing with multiple apps trying to listen on same port 80 etc.. The details here are a topic for another time.. point is, there is lots of goodness and hard work done for you, when it comes to HTTP)
Maybe you should switch to a higher abstraction class, which better deals with all these nifty little details?
I'm going to use for these network connections the TcpListener and TcpClient classes. The usage of these classes is quite easy:
The client side:
public void GetInformationAsync(IPAddress ipAddress)
{
_Log.Info("Start retrieving informations from address " + ipAddress + ".");
var tcpClient = new TcpClient();
tcpClient.BeginConnect(ipAddress, _PortNumber, OnTcpClientConnected, tcpClient);
}
private void OnTcpClientConnected(IAsyncResult asyncResult)
{
try
{
using (var tcpClient = (TcpClient)asyncResult.AsyncState)
{
tcpClient.EndConnect(asyncResult);
var ipAddress = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address;
var stream = tcpClient.GetStream();
stream.ReadTimeout = 5000;
_Log.Debug("Connection established to " + ipAddress + ".");
var formatter = new BinaryFormatter();
var information = (MyInformation)formatter.Deserialize(stream);
_Log.Info("Successfully retrieved information from address " + ipAddress + ".");
InformationAvailable.FireEvent(this, new InformationEventArgs(information));
}
}
catch (Exception ex)
{
_Log.Error("Error in retrieving informations.", ex);
return;
}
}
The server side:
public void Start()
{
ThrowIfDisposed();
if (_TcpServer != null;)
_TcpServer.Stop();
_TcpServer = new TcpListener(IPAddress.Any, _PortNumber);
_TcpServer.Start();
_TcpServer.BeginAcceptTcpClient(OnClientConnected, _TcpServer);
_Log.Info("Start listening for incoming connections on " + _TcpServer.LocalEndpoint + ".");
}
private void OnClientConnected(IAsyncResult asyncResult)
{
var tcpServer = (TcpListener)asyncResult.AsyncState;
IPAddress address = IPAddress.None;
try
{
if (tcpServer.Server != null
&& tcpServer.Server.IsBound)
tcpServer.BeginAcceptTcpClient(OnClientConnected, tcpServer);
using (var client = tcpServer.EndAcceptTcpClient(asyncResult))
{
address = ((IPEndPoint)client.Client.RemoteEndPoint).Address;
_Log.Debug("Client connected from address " + address + ".");
var formatter = new BinaryFormatter();
var informations = new MyInformation()
{
// Initialize properties with desired values.
};
var stream = client.GetStream();
formatter.Serialize(stream, description);
_Log.Debug("Sucessfully serialized information into network stream.");
}
}
catch (ObjectDisposedException)
{
// This normally happens, when the server will be stopped
// and their exists no other reliable way to check this state
// before calling EndAcceptTcpClient().
}
catch (Exception ex)
{
_Log.Error(String.Format("Cannot send instance information to {0}.", address), ex);
}
}
This code works and doesn't make any problems with a lost connection on the client side. If you have a lost connection on the server side you have to re-establish the listener, but that's another story.
In ReconnectChannel just dispose the ChannelSocket object.
try
{
`//ChannelSocket.Shutdown(SocketShutdown.Both);
//ChannelSocket.Disconnect(true);
//ChannelSocket.Close();
ChannelSocket.Dispose();`
}
This is working for me. Let me know if it doesn't work for you.

Continuously checking connection to MySQL Database

Is there an effective way to continuously check if a connection to the database is established? For example, if the network drops or the server computer is turned off, then there is no obvious database available. My idea was to create a Timer that would poll every 10 seconds or so to determine an available connection but that just seems like another "hack". Another idea is to check connectivity before any user input. Here is the sample code that would check for a connection:
public bool isDbAvail()
{
using (MySqlConnection conn = new MySqlConnection(TimeClock.Properties.Settings.Default.timeclockConnectionString))
{
try
{
conn.Open();
return true;
}
catch (MySqlException ex)
{
Console.WriteLine(ex.Message);
return false;
}
}
}
Any input would be great on what would be the best approach.

Ping to server for external connectivity

I have a c# application that has a module for checks the server connection. The relevant code is like the following:
private void PingCheck(string hostName)
{
using (var p = new Ping())
{
try
{
var pr = p.Send(hostName, 2000);
if (pr.Status != IPStatus.Success)
{
log.ErrorFormat("Ping error! Host = {0}, Ping status = {1}", hostName, pr.Status.ToString());
}
}
catch (Exception exc)
{
log.Error("Ping error!", exc);
}
}
}
We have deployed this application to our server that is inside the same network as the target machine. That's why this method checks internal connectivity. Is there any way to check server external connectivity? Because sometimes server connection is available in our network although connection from external network is down. How can I achieve this?
No, there is not, since you are on the server itself.
Either ping some resource outside to check connectivity, or use the NetworkInterface.GetIsNetworkAvailable() method to check whether there is an active connection.

Categories