Asynchronous WCF service timing out - c#

We have an asynchronous WCF service operation that gets log files from all of the different components of our system and sends them to the client. Since this could take a while if one of the components isn't working right, it would be nice if this functionality won't time out, but it shouldn't cause the client to hang either.
My understanding of asynchronous WCF services is that when the client asks the server for something, the server immediately responds with a message saying, "I'm on it. Keep on doing your own stuff, and I'll let you know when I'm finished." Then the connection is freed up for the client to make other requests while the server spins up a new thread to do the bulk of its work. When the server is finished, it sends a message back to the client with the results. Because of this, the connection between the server and client is free, and regardless of how long the server takes, the connection should never time out. Is this correct?
If that's the case, then our service isn't working as expected. When I test the service, it works as expected as long as it takes less than a minute. If I force it to take longer than that, though, the client throws a TimeoutException. Since the service is asynchronous, shouldn't it never time out? If so, what am I missing?
We wrote our asynchronous service using this page as a guide:
http://code.msdn.microsoft.com/windowsdesktop/How-to-Implement-a-WCF-2090bec8
Here is my code. This is the service contract:
[ServiceContract(CallbackContract = typeof(IInformationServiceCallBack), SessionMode = SessionMode.Required)]
public interface IInformationService
{
//snip...
[OperationContract(AsyncPattern=true)]
[FaultContract(typeof(LogFileFault))]
IAsyncResult BeginGetLogFiles(LogFileRequest[] logfileRequests,
AsyncCallback callback, object state);
LogFile[] EndGetLogFiles(IAsyncResult result);
//snip...
}
This is the service implementation:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerSession, UseSynchronizationContext=false)]
public class InformationServiceImpl : IInformationService, IDisposable
{
//snip...
public IAsyncResult BeginGetLogFiles(LogFileRequest[] logfileRequests,
AsyncCallback callback, object state)
{
var task = Task<LogFile[]>.Factory.StartNew((x) =>
{
return GetLogFilesHelper(logfileRequests);
}, state);
return task.ContinueWith(res => callback(task));
}
public LogFile[] EndGetLogFiles(IAsyncResult result)
{
var castResult = result as Task<LogFile[]>;
return castResult.Result;
}
private LogFile[] GetLogFilesHelper(LogFileRequest[] logfileRequests)
{
//Long-running method that gets the log files
}
//snip...
}
Here is the client-side code:
public class InformationServiceConnection : WcfDurableConnection<IInformationService> //WcfDurableConnection is one of our internal classes
{
//snip...
public void GetServiceLogFiles(Action<LogFile[], WcfCommandResult> callback)
{
var logfileRequests = new LogFileRequest[]
{
new LogFileRequest(/* snip */),
new LogFileRequest(/* snip */),
new LogFileRequest(/* snip */),
new LogFileRequest(/* snip */)
};
ExecuteTask(x =>
{
LogFile[] logfile = null;
WcfCommandResult wcfResult = null;
var asyncCallback = new AsyncCallback((result) =>
{
logfile = Channel.EndGetLogFiles(result);
callback(logfile, wcfResult);
});
wcfResult = RunCommand(y =>
{
Channel.BeginGetLogFiles(logfileRequests, asyncCallback, null);
}, x);
});
}
/* ExecuteTask and RunCommand are both methods that take care of
* multithreading issues for us. I included their code below in
* case they make a difference, but the code I'm most interested
* in is the GetServiceLogFiles method above. */
//snip...
protected CancellationTokenSource ExecuteTask(Action<CancellationToken> action)
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
ManualResetEvent lastTask;
ManualResetEvent thisTask;
lock (_objectLock)
{
lastTask = _syncTask;
thisTask = new ManualResetEvent(false);
_syncTask = thisTask;
}
tokenSource.Token.Register(x => ((ManualResetEvent)x).Set(), thisTask);
var task = Task.Factory.StartNew((x) =>
{
try
{
lastTask.WaitOne();
action((CancellationToken)x);
}
catch (Exception e)
{
LogUtility.Error(e);
}
finally
{
thisTask.Set();
}
}, tokenSource.Token, tokenSource.Token).HandleExceptions();
return tokenSource;
}
//snip...
protected WcfCommandResult RunCommand(Action<CancellationToken> action, CancellationToken token, bool isRestarting = false)
{
return RunCommand(x => { action(x); return true; }, token, isRestarting);
}
protected WcfCommandResult RunCommand(Func<CancellationToken, bool> action, CancellationToken token, bool isRestarting = false)
{
WcfCommandResult result = new WcfCommandResult();
lock (_reconnectionLock)
{
if (_reconnecting && !isRestarting)
{
result.Completed = false;
return result;
}
}
lock (_channelLock)
{
if (Channel == null && !_closing)
{
token.ThrowIfCancellationRequested();
Channel = GetNewChannel();
var iChannel = (IClientChannel)Channel;
var initResult = Initialize(token, false);
if (initResult.Completed)
{
Connected = true;
LogUtility.Info(string.Format("Connected to {0} at {1}", ServiceName, iChannel.RemoteAddress));
}
else
LogUtility.Info(string.Format("Failed to connect to {0} at {1}", ServiceName, iChannel.RemoteAddress));
}
}
try
{
var channel = Channel;
token.ThrowIfCancellationRequested();
if (channel != null)
result.Completed = action(token);
}
catch (FaultException e)
{
result.Exception = e;
result.Detail = e.GetDetail<DurableFault>();
LogUtility.Error(result.Exception);
}
catch (CommunicationException e)
{
Connected = false;
result.Exception = e;
IClientChannel channel = ((IClientChannel)Channel);
if (channel != null)
channel.Abort();
Channel = null;
if (!_reconnecting)
LogUtility.Error(result.Exception);
}
catch (TimeoutException e)
{
Connected = false;
result.Exception = e;
IClientChannel channel = ((IClientChannel)Channel);
if (channel != null)
channel.Abort();
Channel = null;
if (!_reconnecting)
LogUtility.Error(result.Exception);
}
catch (NullReferenceException e)
{
Connected = false;
result.Exception = e;
IClientChannel channel = ((IClientChannel)Channel);
if (channel != null)
channel.Abort();
Channel = null;
if (!_reconnecting)
LogUtility.WriteException("Channel is null, it has either been disposed or not setup, call BeginSetupUser to create a new channel", e);
}
catch (ObjectDisposedException e)
{
Connected = false;
result.Exception = e;
IClientChannel channel = ((IClientChannel)Channel);
if (channel != null)
channel.Abort();
Channel = null;
if (!_reconnecting)
LogUtility.Error(result.Exception);
}
catch (InvalidOperationException e)
{
Connected = false;
result.Exception = e;
IClientChannel channel = ((IClientChannel)Channel);
if (channel != null)
channel.Abort();
Channel = null;
if (!_reconnecting)
LogUtility.Error(result.Exception);
}
return result;
}
//snip...
}

There is a timeout set in your config file even for asynchronous calls. You should probably increase it if it will take a long time to respond. I think default is 1 minute. In visual studio, go to tools-> WCF Service Configuration Editor to easily change the value.
This may also help you if you want to see what the configuration looks like: Increasing the timeout value in a WCF service
You can set it in that config file, or in the code behind.

Related

Async function freezes UI thread

I have an async function which still freezes / lags the UI thread for me when I execute it. This is my function calling it.
private void TcpListenerLogic(object sender, string e)
{
Application.Current.Dispatcher.BeginInvoke((Action)async delegate {
try
{
dynamic results = JsonConvert.DeserializeObject<dynamic>(e);
if (results.test_id != null)
{
// Get properties for new anchor
string testInformation = await CommunicationCommands.getJsonFromURL(
"http://" + ServerIP + ":" + ServerPort + "/api/" + results.test_id);
}
}
catch (Exception exception)
{
// Writing some Trace.WriteLine()'s
}
});
}
And this is the async function that freezes my UI Thread
public static async Task<string> getJsonFromURL(string url)
{
try
{
string returnString = null;
using (System.Net.WebClient client = new System.Net.WebClient())
{
returnString = await client.DownloadStringTaskAsync(url);
}
return returnString;
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
return null;
}
}
I already tried to make everything in TcpListenerLogic run in a new Thread:
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
}).Start();
Which resulted in the whole UI completely freezing. And I tried to make TcpListenerLogic async and await the dispatcher, which also made everything freeze permanently. I also tried to make TcpListenerLogic async and leave the dispatcher. The dispatcher is only there because I normally have some UI code in there, which I left out for my tests.
I have ventured far through the internet, but no BackgroundWorker, ThreadPool or other methods helped me in my endeavour.
If anyone has help for this particular problem, or a resource that would improve my understanding of async functions in C#, I would much appreciate it.
Edit
As requested a deeper insight in how this event handler is called.
I have System.Net.Websocket, which is connected to the Backend API I am working with and triggers an event, everytime he receives new Data. To guarantee the socket listens as longs as it is open, there is a while loop which checks for the client state:
public event EventHandler<string> TcpReceived;
public async void StartListener(string ip, int port, string path)
{
try
{
using (client = new ClientWebSocket())
{
try
{ // Connect to backend
Uri serverUri = new Uri("ws://" + ip + ":" + port.ToString() + path );
await client.ConnectAsync(serverUri, CancellationToken.None);
}
catch (Exception ex)
{
BackendSettings.IsConnected = false;
Debug.WriteLine("Error connecting TCP Socket: " + ex.ToString());
}
state = client.State;
// Grab packages send in backend
while (client.State == WebSocketState.Open || client.State == WebSocketState.CloseSent)
{
try
{
// **Just formatting the received data until here and writing it into the "message" variable**//
TcpReceived(this, message);
// Close connection on command
if (result.MessageType == WebSocketMessageType.Close)
{
Debug.WriteLine("Closing TCP Socket.");
shouldstayclosed = true;
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
break;
}
state = client.State;
}
catch
{
BackendSettings.IsConnected = false;
state = client.State;
}
}
state = client.State;
}
}
catch (Exception ex)
{
// Some error messages and settings handling
}
}
The Event has a handler attached:
TcpReceived += TcpListener_TcpReceived;
And this is the Handler, which calls the previously seen "TcpListenereLogic".
private void TcpListener_TcpReceived(object sender, string e)
{
TcpListenerLogic(sender, e);
//App.Current.Dispatcher.BeginInvoke(new Action(() => {
// TcpListenerLogic(sender, e);
//}));
//new Thread(() =>
//{
// Thread.CurrentThread.IsBackground = true;
// TcpListenerLogic(sender, e);
//}).Start();
}
I previously had the "TcpListenereLogic" as the handler, but I wanted to try different methods to call it. I also left in the commented out part, to show how the call of "TcpListenereLogic" looked already. All my attempts were with all mentioned setups and sadly lead to nothing.
Thank you very much #TheodorZoulias for helping me to find the solution to my problem.
It turns out it wasn't the async function itself, but rather how often it gets called. It got called roughly ~120 times every second.
My solution starts by calling the Listener method over a new Thread:
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
MainWindow.tcpListener.StartListener(ip, portNumber, "/api/");
}).Start();
To limit the amount of calls that happen every second I added a dispatcher timer, that resets a bool after it has been used for a call, by my Event.
readonly System.Windows.Threading.DispatcherTimer packageIntervallTimer =
new System.Windows.Threading.DispatcherTimer();
bool readyForNewPackage = true;
private void ReadyForPackage(object sender, EventArgs e)
{
readyForNewPackage = true;
}
public async void StartListener(string ip, int port, string path)
{
packageIntervallTimer.Interval = TimeSpan.FromMilliseconds(50);
packageIntervallTimer.Tick += (s, e) => { Task.Run(() => ReadyForPackage(s, e)); };
packageIntervallTimer.Start();
Then I wrapped everything inside the while loop into an if condition based on the bool, the most important part was to have my "event EventHandler TcpReceived" in there:
// Grab packages sent in backend
while (client.State == WebSocketState.Open || client.State == WebSocketState.CloseSent)
{
if (readyForNewPackage == true)
{
readyForNewPackage = false;
try
{
....
TcpReceived(this, message);
....
}
catch
{
...
}
}
}
I added my TcpListenerLogic to the Eventhandler:
TcpReceived += TcpListenerLogic;
And my TcpListenerLogic now looked like this (names have been changed):
private async void TcpListenerLogic(object sender, string e)
{
try
{
dynamic results = JsonConvert.DeserializeObject<dynamic>(e);
if (results.test_id != null)
{
string testID = "";
if (results.test_id is JValue jValueTestId)
{
testID = jValueTestId.Value.ToString();
}
else if (results.test_id is string)
{
testID = results.test_id;
}
// Get properties for new object
string information = await CommunicationCommands.getJsonFromURL(
"http://" + ServerIP + ":" + ServerPort + "/api/" + testID );
if (information != null)
{
await App.Current.Dispatcher.BeginInvoke(new Action(() =>
{
// Create object out of the json string
TestStatus testStatus = new TestStatus();
testStatus.Deserialize(information);
if (CommunicationCommands.isNameAlreadyInCollection(testStatus.name) == false)
{
// Add new object to the list
CommunicationCommands.allFoundTests.Add(testStatus);
}
}));
{
}
catch (Exception exception)
{
....
}
}
Adding a new Thread to execute any step results in problems, so keep in mind that all this uses the thread created at the beginning for "StartListener"

Signal R reconnect seems to create a new thread

We have a siglnalR hub hosted in IIS, and a WPF .net core application that connects. Everything is working perfectly on first run. However, when IIS recycles the application pool, the WPF client re-reconnects successfully, but, (so it seems) on another thread, as when the user attempts to perform an action (open a new WPF window) - the following error is thrown when creating a new instance of the window to open :-
"The calling thread must be STA, because many UI components require this"
This is how we connect to the hub :-
private async void Connect()
{
try
{
_signalRConnection.On<Notification>(NotificationMessageStr, (message) =>
{
if (message != null && _signalRConnection != null)
{
OnProcessMessage(message);
}
}
);
_signalRConnection.Reconnecting += error =>
{
OnReconnecting("Connection lost - Attempting to reconnect.");
return Task.CompletedTask;
};
_signalRConnection.Reconnected += connectionId =>
{
OnReconnected("Reconnected");
return Task.CompletedTask;
};
_signalRConnection.Closed += error =>
{
OnLostConnection("Failed to connect");
// Notify users the connection has been closed or manually try to restart the connection.
return Task.CompletedTask;
};
try
{
//Connect to the server
await _signalRConnection.StartAsync();
}
catch (Exception ex)
{
}
}
catch (Exception ex)
{
}
}
When a message is received from the hub, we call :-
private void SubscriveToNewNotification()
{
vm.NewNotification += (sender, e) => {
ShowNotificationAlert(e.NotificationMessage); };
}
private void ShowNotificationAlert(Notification notification) {
NotificationAlert notificationAlert = new NotificationAlert();
notificationAlert.notification = notification;
notificationAlert.Show();
}
And it is this:-
NotificationAlert notificationAlert = new NotificationAlert();
That is failing.
This is how the connection is built up :-
private void InitializeViewModel()
{
try
{
string serviceAddress = "xxxx/notificationHub";
connectHub = NotificationHubManager.CreateNotificationHub(serviceAddress, userInfo);
}
catch (System.Exception ex)
{
System.Windows.MessageBox.Show(ex.Message + "--");
}
connectHub.ProcessMessage += (sender, e) =>
{
// THIS IS WHERE IT FALLS OVER
NotificationAlert n = new NotificationAlert();
OnNotificationReceived(e.NotificationMessage);
};
-- This is the notification hub
public static NotificationHubConnect CreateNotificationHub(string address, ISwiftUser userInfo = null)
{
HubConnection hubConnection = new HubConnectionBuilder()
.WithUrl(address)
.WithAutomaticReconnect()
.Build();
try
{
var result = new NotificationHubConnect(hubConnection, userInfo);
return result;
}
catch (Exception ex)
{
throw ex;
}
}
Is there a way to have the reconnect run on the same thread?

Issue with local connection between 2 programs over TCP

So I have a really really weird issue. This is going to take a tiny bit of a wall to explain fully..
I'm running some game servers for a Unity game called SCP: Secret Laboratory, and have a custom-made C# Discord Bot that helps my staff moderate both in-game and on Discord, by controlling bans, logging player warnings, etc, etc.
I'm trying to make the two platforms communicate with each other over a TCP port over the local machine address (127.0.0.1).
I've setup the listener on the bot, to whom the individual servers (there are 7) will connect to when they are ready.
I've also set it up so that if the connection is broken from either side, the servers will close the TcpClient, start a new one, and attempt to connect, while the listener just resumes listening on that port for a connection again, after closing the client on it's side.
I've tested it several times by closing the client, the socket, or just rebooting the program on either end, and they seem to without fail reconnect flawlessly.
Here's my issue..
While the game servers remain empty, everything is fine. After players start to connect, anywhere from 5mins to an hour will go by, then suddenly, seemingly at random, the server no longer 'hears' the bot when it talks across the connection, however, the bot itself does not hit an error when trying to send data, the server just never receives any. What's stranger, is the server will continue to send it's own data over the connection, and the bot does receive that data.
To attempt to 'reset' the connection with a new client when this happens, I make the servers send an initial heartbeat to the bot when they first connect. This tells the bot that the connection is established on the server's end, and begins a looping thread that will send an AYT message to the server every 10s, to which the server must reply.
If the bot sends 3 AYT messages without getting a response back, it will close the TcpClient, and start listening for a new one.
At this time, the server detects the connection was closed, disposes of it's client, instantiates a new one, and tries to connect successfully. The bot then receives it's initial heartbeat, and starts the AYT timer for that client again, but the server continues to not receive them. Or any data whatsoever sent from the bot, even though the bot still receives data from the server during this time.
The only solution to fix the problem at that point, is to fully restart the game server, after which it will connect to the bot and work perfectly fine, until it.. just doesn't anymore.
For reference, pastebins of the code used are below.
The bot "listener" side
public class ProcessSTT
{
private static ConcurrentDictionary<int, TcpClient> bag = new ConcurrentDictionary<int, TcpClient>();
private static ConcurrentDictionary<int, int> heartbeats = new ConcurrentDictionary<int, int>();
public static void SendData(string data, int port, ulong channel = 0)
{
try
{
BinaryFormatter formatter = new BinaryFormatter();
SerializedData.SerializedData serializedData =
new SerializedData.SerializedData { Data = data, Port = port, Channel = channel };
//Console.WriteLine($"Sending {serializedData.Data}");
if (!bag.ContainsKey(port))
{
Console.WriteLine($"STT: Bag does not contain {port}");
return;
}
if (bag[port] != null && bag[port].Connected)
formatter.Serialize(bag[port].GetStream(), serializedData);
else
{
Console.WriteLine($"Error - Bag {port} is null or not connected.");
if (bag.TryRemove(port, out TcpClient client))
client.Dispose();
}
}
catch (IOException s)
{
Console.WriteLine($"STT: Socket exception, removing..");
KeyValuePair<int, TcpClient> thingything = default;
foreach (var thing in bag)
if (thing.Key == port)
thingything = thing;
if (bag.TryRemove(thingything.Key, out TcpClient _client))
{
_client.Close();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private static List<TcpListener> listener = new List<TcpListener>();
public static void Init()
{
for (int i = 1; i < 8; i++)
{
TcpListener list = new TcpListener(IPAddress.Loopback, 11900 + i);
Console.WriteLine($"STT: Listener started for port {11900 + i}");
listener.Add(list);
list.Start();
ThreadPool.QueueUserWorkItem(ListenForConn, list);
}
}
public static async Task Heartbeat(int port)
{
await Task.Delay(10000);
for (;;)
{
Console.WriteLine("STT: Starting Heartbeat");
if (heartbeats[port] > 3)
{
Console.WriteLine($"STT: Removing {port} due to heartbeat timeout.");
if (bag.TryRemove(port, out TcpClient client))
client.Close();
heartbeats.TryRemove(port, out int _);
return;
}
heartbeats[port]++;
Console.WriteLine($"STT: Sending heartbeat to: {port}");
if (!bag[port].Connected)
{
Console.WriteLine($"STT: {port} is null, removing.");
if (bag.TryRemove(port, out TcpClient client))
client.Close();
return;
}
SendData("ping", port, 653737934150959115);
await Task.Delay(10000);
}
}
public static void ListenForConn(object token)
{
Console.WriteLine("STT: Listener started.");
TcpListener listen = token as TcpListener;
for (;;)
{
try
{
TcpClient thing = listen.AcceptTcpClient();
ThreadPool.QueueUserWorkItem(ListenOn, thing);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
public static async Task ReceiveData(SerializedData.SerializedData data, TcpClient client)
{
try
{
if (data == null)
{
Console.WriteLine("STT: Received data null");
return;
}
if (data.Data == "ping")
{
if (!bag.ContainsKey(data.Port))
{
Console.WriteLine($"STT: Adding {data.Port}");
bag.TryAdd(data.Port, client);
}
if (!bag[data.Port].Connected || bag[data.Port] == null)
{
Console.WriteLine($"STT: Bag {data.Port} not connected or null, removing.");
if (bag.TryRemove(data.Port, out TcpClient cli))
{
cli?.Close();
}
}
Console.WriteLine($"STT: Received heartbeat for: {data.Port}");
if (!heartbeats.ContainsKey(data.Port))
{
Heartbeat(data.Port);
heartbeats.TryAdd(data.Port, 0);
}
else
heartbeats[data.Port]--;
return;
}
Console.WriteLine(data.Data);
data.Data = data.Data.Substring(data.Data.IndexOf('#') + 1);
//Console.WriteLine("Getting guild.");
SocketGuild guild = Bot.Discord.GetGuild(478381106798788639);
//Console.WriteLine("Getting channel");
SocketTextChannel chan = guild.GetTextChannel(data.Channel);
//Console.WriteLine("Sending message.");
await chan.SendMessageAsync($"Server {data.Port -= 7770}: {data.Data}");
if (data.Port == 7771)
{
DiscordWebhookClient webhook = new DiscordWebhookClient(
"");
await webhook.SendMessageAsync($"{data.Data}");
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
public static void ListenOn(object token)
{
TcpClient client = token as TcpClient;
try
{
BinaryFormatter formatter = new BinaryFormatter();
for (;;)
{
SerializedData.SerializedData serializedData;
if (!client.Connected)
{
Console.WriteLine($"Client not connected..");
client.Close();
continue;
}
serializedData = formatter.Deserialize(client.GetStream()) as SerializedData.SerializedData;
new Thread(() => ReceiveData(serializedData, client)).Start()
}
}
catch (SerializationException s)
{
Console.WriteLine($"STT: Serialization exception, removing..");
KeyValuePair<int, TcpClient> thingything = default;
foreach (var thing in bag)
if (thing.Value == client)
thingything = thing;
if (bag.TryRemove(thingything.Key, out TcpClient _client))
{
_client.Close();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
The server "speaker" side
public class ProcessSTT
{
private static TcpClient tcpClient;
public static readonly ConcurrentQueue<SerializedData.SerializedData> dataQueue = new ConcurrentQueue<SerializedData.SerializedData>();
private static Thread _init;
private static bool _locked;
public static void Init()
{
if (_locked)
return;
_locked = true;
Thread.Sleep(1000);
try
{
Plugin.Log($"STT: Starting INIT.");
tcpClient?.Close();
tcpClient = new TcpClient();
while (!tcpClient.Connected)
{
Plugin.Log($"STT: While loop start");
Thread.Sleep(2000);
try
{
tcpClient.Connect("127.0.0.1", ServerConsole.Port + 4130);
}
catch (SocketException)
{
tcpClient.Client.Disconnect(false);
}
catch (Exception e)
{
Plugin.Log($"STT: {e}");
}
}
Thread thread = new Thread(ReceiveData);
thread.Start();
SendData("ping", 0);
_locked = false;
}
catch (IOException i)
{
_init = new Thread(Init);
_init.Start();
}
catch (Exception e)
{
ServerConsole.AddLog(e.ToString());
}
}
public static void SendData(string data, ulong channel)
{
try
{
if (!tcpClient.Connected)
throw new InvalidOperationException("Tcp Client not connected!");
SerializedData.SerializedData serializedData = new SerializedData.SerializedData
{
Data = data, Port = ServerConsole.Port, Channel = channel
};
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(tcpClient.GetStream(), serializedData);
ServerConsole.AddLog($"Sent {data}");
}
catch (IOException i)
{
_init = new Thread(Init);
_init.Start();
}
catch (Exception e)
{
ServerConsole.AddLog(e.ToString());
}
}
public static void ReceiveData()
{
try
{
if (!tcpClient.Connected)
throw new InvalidOperationException("Tcp Client not connected!");
BinaryFormatter formatter = new BinaryFormatter();
for (;;)
{
SerializedData.SerializedData deserialize =
formatter.Deserialize(tcpClient.GetStream()) as SerializedData.SerializedData;
if (deserialize == null)
continue;
dataQueue.Enqueue(deserialize);
}
}
catch (SerializationException s)
{
_init = new Thread(Init);
_init.Start();
}
catch (IOException e)
{
_init = new Thread(Init);
_init.Start();
}
catch (Exception e)
{
ServerConsole.AddLog(e.ToString());
}
}
}
public class HandleQueue
{
public static ulong channelid;
public static void HandleQueuedItems()
{
while (ProcessSTT.dataQueue.TryDequeue(out SerializedData.SerializedData result))
{
string command = result.Data;
Plugin.Log($"STT: Received {result.Data} for {result.Port}");
if (result.Port != ServerConsole.Port)
return;
if (result.Data == "ping")
{
Plugin.Log("STT: BLART Heartbeat received.");
ProcessSTT.SendData("ping", 0);
return;
}
channelid = result.Channel;
try
{
GameCore.Console.singleton.TypeCommand($"/{command}", new BlartSender());
}
catch (Exception e)
{
ServerConsole.AddLog(e.ToString());
}
}
}
public static IEnumerator<float> Handle()
{
for (;;)
{
HandleQueuedItems();
yield return Timing.WaitForSeconds(1f);
}
}
}
public class BlartSender : CommandSender
{
public override void RaReply(string text, bool success, bool logToConsole, string overrideDisplay)
{
ProcessSTT.SendData($"{text}", HandleQueue.channelid);
}
public override void Print(string text)
{
ProcessSTT.SendData($"{text}", HandleQueue.channelid);
}
public override string SenderId => "BLART";
public override string Nickname => "BLART";
public override ulong Permissions => ServerStatic.GetPermissionsHandler().FullPerm;
public override byte KickPower => Byte.MaxValue;
public override bool FullPermissions => true;
}`

Pinging a server using C#

I was trying to ping a server using the Ping class, but after like 10 times that the method returns true, I keep getting false(which means the server is down[?] and it isn't) Here's the method:
public bool IsConnectedToInternet()
{
Ping p = new Ping();
try
{
PingReply reply = p.Send("www.uic.co.il", 1000);
if (reply.Status == IPStatus.Success)
return true;
}
catch(Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
return false;
}
private void start_Click(object sender, EventArgs e)
{
for (; ; )
{
Console.WriteLine(IsConnectedToInternet);
}
}
Why am I keep getting false after a while?
Thank you.
You're flooding the server with requests:
for (; ; )
{
Console.WriteLine(IsConnectedToInternet);
}
will loop as fast as possible sending request after request.
If you are just coding a keep alive service or service status control then using a timer that pings every minute or even every 10 minutes should be good enough.
Additionally, as others have pointed out in their comments, you are abusing properties by doing the ping in the getter as the call can potentially take some time and property getters should really return, if not instantly then very quickly. A CheckConnection() method would have clearer intent.
I rewrote your code.
It will trigger an event called ConnectionLost if connection is lost and an event called Connected when it gets connected again.
public class NetworkStateMonitor
{
private System.Threading.Timer _timer;
bool _wasConnected = false;
public NetworkStateMonitor()
{
_timer = new System.Threading.Timer(OnPing, null, TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10));
}
public bool CheckInternetConnection()
{
bool result = false;
Ping p = new Ping();
try
{
PingReply reply = p.Send("www.uic.co.il", 1000);
if (reply.Status == IPStatus.Success)
return true;
catch (PingException)
{
return false;
}
}
private void OnPing(object state)
{
var newState = CheckInternetConnection();
if (!newState && _wasConnected)
ConnectionLost(this, EventArgs.Empty);
else if (newState && !_wasConnected)
Connected(this, EventArgs.Empty);
_wasConnected = newState;
}
public event EventHandler ConnectionLost = delegate{};
public event EventHandler Connected = delegate{};
}
For other stumbling across this page, this function would be better if it were rewritten to:
public bool CheckInternetConnection(string HostName)
{
bool result = false; // assume error
try {
Ping oPing = new Ping();
PingReply reply = oPing.Send(HostName, 3000);
if (reply.Status == IPStatus.Success){
result = true;
}
} catch (Exception E) {
// Uncomment next line to see errors
// MessageBox.Show(E.ToString());
}
return result;
}
Now call using:
bool IsSuccessful = CheckInternetConnection("www.uic.co.il");

Async operation completes, but result is not send to browser

I want to implement a webchat.
The backend is a dual WCF channel. Dual channel works in the console or winforms,
and it actually works on the web. I can at least send and receive messages.
As a base I used this blog post
so, the async operation completes.
When i debug the result, I see that the messages are all ready to send to the browser.
[AsyncTimeout(ChatServer.MaxWaitSeconds * 1020)] // timeout is a bit longer than the internal wait
public void IndexAsync()
{
ChatSession chatSession = this.GetChatSession();
if (chatSession != null)
{
this.AsyncManager.OutstandingOperations.Increment();
try
{
chatSession.CheckForMessagesAsync(msgs =>
{
this.AsyncManager.Parameters["response"] = new ChatResponse { Messages = msgs };
this.AsyncManager.OutstandingOperations.Decrement();
});
}
catch (Exception ex)
{
Logger.ErrorException("Failed to check for messages.", ex);
}
}
}
public ActionResult IndexCompleted(ChatResponse response)
{
try
{
if (response != null)
{
Logger.Debug("Async request completed. Number of messages: {0}", response.Messages.Count);
}
JsonResult retval = this.Json(response);
Logger.Debug("Rendered response: {0}", retval.);
return retval;
}
catch (Exception ex)
{
Logger.ErrorException("Failed rendering the response.", ex);
return this.Json(null);
}
}
But nothing is actually sent.
Checking Fiddler, I see the request but I never get a response.
[SessionState(SessionStateBehavior.ReadOnly)]
public class ChatController : AsyncController
I also had to set the SessionStateBehaviour to Readonly, otherwise the async operation would block the whole page.
EDIT:
Here is the CheckForMessagesAsync:
public void CheckForMessagesAsync(Action<List<ChatMessage>> onMessages)
{
if (onMessages == null)
throw new ArgumentNullException("onMessages");
Task task = Task.Factory.StartNew(state =>
{
List<ChatMessage> msgs = new List<ChatMessage>();
ManualResetEventSlim wait = new ManualResetEventSlim(false);
Action<List<ChatMessage>> callback = state as Action<List<ChatMessage>>;
if (callback != null)
{
IDisposable subscriber = m_messages.Subscribe(chatMessage =>
{
msgs.Add(chatMessage);
wait.Set();
});
bool success;
using (subscriber)
{
// Wait for the max seconds for a new msg
success = wait.Wait(TimeSpan.FromSeconds(ChatServer.MaxWaitSeconds));
}
if (success) this.SafeCallOnMessages(callback, msgs);
else this.SafeCallOnMessages(callback, null);
}
}, onMessages);
}
private void SafeCallOnMessages(Action<List<ChatMessage>> onMessages, List<ChatMessage> messages)
{
if (onMessages != null)
{
if (messages == null)
messages = new List<ChatMessage>();
try
{
onMessages(messages);
}
catch (Exception ex)
{
this.Logger.ErrorException("Failed to call OnMessages callback.", ex);
}
}
}
it`s the same idea as the in the refered blog post
EDIT2:
Btw, when nothing is received, so the wait timeout comes into play, the reponse returns. so it seems to crash somewhere. any idea how to log this?
I changed the jQUERY request (see original blog post) from POST to GET. That fixes it.

Categories

Resources