Answer the question
In order to leave comments, you need to log in
C#: Automatic reconnection of UdpClient after server restart?
I am writing a service application that requests status data from the server via UDP and displays alerts if necessary.
The problem is that the server lives its own life. It may or may not be running when the application starts. Or it may even crash / restart / etc, and the service needs to wait and periodically check if the server has risen and whether it is possible to connect again.
I implemented it this way.
public Server(string name, string host, int? queryPort = null, int? rConPort = null)
{
Name = name;
Host = Dns.GetHostAddresses(host)[0];
QueryPort = queryPort;
RConPort = rConPort;
if (QueryPort == null && RConPort == null)
{
throw new IncorrectServerEntryPoint(Host);
}
}
public async void Watch()
{
if (QueryPort == null) return;
_statusWatcherClient = new UdpClient(Host.ToString(), QueryPort.Value);
UpdateChallengeTokenTimer = new Timer(async obj =>
{
Console.WriteLine($"[INFO] [{Name}] Send handshake request");
Request handshakeRequest = Request.GetHandshakeRequest();
byte[] response = null;
try
{
response = await SendResponseService.SendReceive(_statusWatcherClient, handshakeRequest.Data, ReceiveAwaitIntervalSeconds);
IsOnline = true;
}
catch (SocketException)
{
WaitForServerAlive(QueryPort.Value);
}
if (response == null) return;
var challengeTokenRaw = Response.ParseHandshake(response);
lock (_challengeTokenLock)
{
SetChallengeToken(challengeTokenRaw);
}
Console.WriteLine($"[INFO] [{Name}] ChallengeToken is set up: " + BitConverter.ToString(challengeTokenRaw));
}, null, 0, GettingChallengeTokenInterval);
UpdateServerStatusTimer = new Timer(async obj =>
{
Console.WriteLine($"[INFO] [{Name}] Send full status request");
var challengeToken = new byte[4];
lock (_challengeTokenLock)
{
Buffer.BlockCopy(_challengeToken, 0, challengeToken, 0, 4);
}
var fullStatusRequest = Request.GetFullStatusRequest(challengeToken);
byte[] response = null;
try
{
response = await SendResponseService.SendReceive(_statusWatcherClient, fullStatusRequest.Data, ReceiveAwaitIntervalSeconds);
IsOnline = true;
}
catch (SocketException)
{
WaitForServerAlive(QueryPort.Value);
}
if (response == null) return;
ServerFullState fullState = Response.ParseFullState(response);
Console.WriteLine($"[INFO] [{Name}] Full status is received");
OnFullStatusUpdated?.Invoke(this, new ServerStateEventArgs(Name, fullState));
}, null, 500, GettingStatusInterval);
}
public async Task Unwatch()
{
await UpdateChallengeTokenTimer.DisposeAsync();
await UpdateServerStatusTimer.DisposeAsync();
_statusWatcherClient.Dispose();
_statusWatcherClient = null;
}
public async void WaitForServerAlive(int port)
{
Console.WriteLine($"[WARNING] [{Name}] Server is unavailable. Waiting for reconnection...");
IsOnline = false;
await Unwatch();
Timer waitTimer = null;
waitTimer = new Timer(async obj => {
try
{
using TcpClient tcpClient = new TcpClient();
await tcpClient.ConnectAsync(Host, port);
if (waitTimer == null) return;
await waitTimer.DisposeAsync();
Console.WriteLine($"[INFO] [{Name}] Server is available again");
Watch();
} catch (SocketException) { }
}, null, 500, 5000);
}
public static int ReceiveAwaitIntervalSeconds = 10;
public static int GettingChallengeTokenInterval = 30000;
public static int GettingStatusInterval = 5000;
public static async Task<byte[]> SendReceive(UdpClient client, byte[] data, int receiveAwaitIntervalSeconds)
{
if (client == null)
{
throw new NullReferenceException("UdpClient client is null");
}
IPEndPoint ipEndPoint = null;
byte[] response = null;
await client.SendAsync(data, data.Length);
var responseToken = client.BeginReceive(null, null);
responseToken.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(receiveAwaitIntervalSeconds));
if (responseToken.IsCompleted)
{
try
{
response = client.EndReceive(responseToken, ref ipEndPoint);
}
catch (Exception)
{
// can't end receive
}
}
if (response == null)
throw new SocketException();
return response;
}
[INFO] [ML_VDS] Send handshake request
[WARNING] [ML_VDS] Server is unavailable. Waiting for reconnection...
[INFO] [ML_VDS] Server is available again
[INFO] [ML_VDS] Send handshake request
[WARNING] [ML_VDS] Server is unavailable. Waiting for reconnection...
[INFO] [ML_VDS] Server is available again
...
[INFO] [ML_VDS] Send handshake request
[WARNING] [ML_VDS] Server is unavailable. Waiting for reconnection...
[INFO] [ML_VDS] Server is available again
[INFO] [ML_VDS] Send handshake request
[INFO] [ML_VDS] Send full status request
[WARNING] [ML_VDS] Server is unavailable. Waiting for reconnection...
[WARNING] [ML_VDS] Server is unavailable. Waiting for reconnection...
[INFO] [ML_VDS] Server is available again
[INFO] [ML_VDS] Send handshake request
[INFO] [ML_VDS] Server is available again
[INFO] [ML_VDS] Send handshake request
[ML_VDS] Server is online
[INFO] [ML_VDS] ChallengeToken is set up: 00-93-95-1E
[INFO] [ML_VDS] ChallengeToken is set up: 00-98-D8-8B
[INFO] [ML_VDS] Send full status request
[INFO] [ML_VDS] Send full status request
[INFO] [ML_VDS] Send full status request
[WARNING] [ML_VDS] Server is unavailable. Waiting for reconnection...
[ML_VDS] Server is offline
[WARNING] [ML_VDS] Server is unavailable. Waiting for reconnection...
[INFO] [ML_VDS] Send full status request
[WARNING] [ML_VDS] Server is unavailable. Waiting for reconnection...
[WARNING] [ML_VDS] Server is unavailable. Waiting for reconnection...
[WARNING] [ML_VDS] Server is unavailable. Waiting for reconnection...
Unhandled exception. [WARNING] [ML_VDS] Server is unavailable. Waiting for reconnection...
Unhandled exception. Unhandled exception. Unhandled exception. Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
at MCServerNotifier.Server.Unwatch() in /Users/maxlevs/RiderProjects/MCServerNotifier/MCServerNotifier/Server.cs:line 142
at MCServerNotifier.Server.WaitForServerAlive(Int32 port) in /Users/maxlevs/RiderProjects/MCServerNotifier/MCServerNotifier/Server.cs:line 150
at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__140_1(Object state)
...
at System.Threading.ThreadPoolWorkQueue.Dispatch()
System.NullReferenceException: Object reference not set to an instance of an object.
at MCServerNotifier.Server.Unwatch() in
...
at System.Threading.ThreadPoolWorkQueue.Dispatch()
System.NullReferenceException: Object reference not set to an instance of an object.
at MCServerNotifier.Server.Unwatch() in
...
at System.Threading.ThreadPoolWorkQueue.Dispatch()
System.NullReferenceException: Object reference not set to an instance of an object.
at MCServerNotifier.Server.Unwatch() in
...
at System.Threading.ThreadPoolWorkQueue.Dispatch()
System.NullReferenceException: Object reference not set to an instance of an object.
at MCServerNotifier.Server.Unwatch() in
...
at System.Threading.ThreadPoolWorkQueue.Dispatch()
Answer the question
In order to leave comments, you need to log in
Resolved the issue of refusing to use TCP connection as a test of server operation. (Who can tell if such a replacement could be viable at all?).
Replaced the WaitForServerAlive() code with the following:
public async void WaitForServerAlive()
{
if(Debug)
Console.WriteLine($"[WARNING] [{ServerName}] Server is unavailable. Waiting for reconnection...");
IsOnline = false;
await Unwatch();
_mcQuery.InitSocket();
Timer waitTimer = null;
waitTimer = new Timer(async obj => {
try
{
await _mcQuery.GetHandshake();
IsOnline = true;
Watch();
lock (_retryCounterLock)
{
RetryCounter = 0;
}
waitTimer.Dispose();
}
catch (SocketException)
{
if(Debug)
Console.WriteLine($"[WARNING] [{ServerName}] [WaitForServerAlive] Server doesn't response. Try to reconnect: {RetryCounter}");
lock (_retryCounterLock)
{
RetryCounter++;
if (RetryCounter >= RetryMaxCount)
{
if(Debug)
Console.WriteLine($"[WARNING] [{ServerName}] [WaitForServerAlive] Recreate socket");
RetryCounter = 0;
_mcQuery.InitSocket();
}
}
}
}, null, 500, 5000);
}
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question