Currently I am connecting my client to the server synchronously.
However, the server is unavailable at times, so I figured using async would be better so I can already gather and format the data I want to send later. I found that the method I am looking for is probably TcpClient.BeginConnect
Unfortunately I am completely new to async operation, so the parameters weird me out.
For one: Is there a specific reason I have to use IP and port seperately, i.e. not use an IPEndPoint?
The more important problem however: AsyncCallback and Object, what are these?
Do I need to change anything on my server?
I think I understood that snyc or asnyc is a local choice and doesn't affect the other side, at least not in compatibility.
Lastly: Here I read about keywords asnyc and await: Where to use them and how?
Here's a little pseudo-code to demonstrate what I have and what I want
private static void send(string msg) {
TcpClient cli = new TcpClient(localEP);
bool loop = true;
while(loop) { //trying over and over till it works
try { //is bad practice and inefficient
cli.Connect(remoteEP);
} catch (Exception) {
;
}
if(cli.Connected)
break;
}
var blah = doOtherThings(msg);
useTheClient(blah);
}
And now how I wished it to work
private static void send(string msg) {
TcpClient cli = new TcpClient(localEP);
cli.BeginConnect(remoteEP); //thread doesn't block here anymore + no exceptions
var blah = doOtherThings(msg); //this now gets done while cli is connecting
await(cli.connect == done) //point to wait for connection to be established
useTheClient(blah);
}
You'll need to create a new AsyncCallback and set it to a specific void where something will be done once the TcpClient is finished connecting to the host. You might also want to check whether the TcpClient connection succeeded or not by checking the value of the TcpClient.Connected
Example
The following example illustrates the asynchronous TcpClient connection to google.com on port 80
static TcpClient cli = new TcpClient(); //Initialize a new TcpClient
static void Main(string[] args)
{
send("Something");
Console.ReadLine();
}
private static void doSomething(IAsyncResult result)
{
if (cli.Connected) //Connected to host, do something
{
Console.WriteLine("Connected"); //Write 'Connected'
}
else
{
//Not connected, do something
}
Console.ReadLine(); //Wait for user input
}
private static void send(string msg)
{
AsyncCallback callBack = new AsyncCallback(doSomething); //Set the callback to the doSomething void
cli.BeginConnect("google.com", 80, callBack, cli); //Try to connect to Google on port 80
}
Here's a better way to do what you described in the comment
System.Threading.Thread newThread = new System.Threading.Thread((System.Threading.ThreadStart)delegate {
//Do whatever you want in a new thread
while (!cli.Connected)
{
//Do something
}
});
newThread.Start(); //Start executing the code inside the thread
//This code will still run while the newThread is running
Console.ReadLine(); //Wait for user input
newThread.Abort(); //Stop the thread when the user inserts any thing
Related
I have an async socket server written in C#, running on a Lightsail server running Amazon Linux. It consists of a TcpListener that accepts connections, starts up a new thread to listen when someone connects, initiates an SSL connection, and then acts as a server for an online game.
This server works fine for about a day, until suddenly all networking stops working on the server. The crash takes anywhere from 22 hours to one week to occur. The symptoms are as follows:
Anyone already connected to the server will suddenly stop receiving/sending data. I can see in the logs that my inactivity checking code will eventually kick them for not sending heartbeat packets.
The server will also be unable to connect to its MySQL database (which is running on the same system, so it's unable to connect to localhost? I can still access it through PHPMyAdmin during this time).
It is, however, still able to write both to files and to console, as my logger is still able to write to both.
The code looks like everyone else's (I did try the changes suggested for this question, but it still crashed after ~24 hours). None of the errors get logged, so it looks like it never encounters an exception. No exceptions precede the crash, which is why I've been having problems figuring this one out.
For completeness, here is my main loop:
public void ListenLoop()
{
TcpListener listener = new TcpListener(IPAddress.Any, 26000);
listener.Start();
while (true)
{
try
{
if (listener.Pending())
{
listener.BeginAcceptTcpClient(new AsyncCallback(AcceptConnection), listener);
Logger.Write(Logger.Level.INFO, "continuing the main loop");
}
// Yield so we're not stuck in a busy-loop
Thread.Sleep(5);
}
catch (Exception e)
{
Logger.Write(Logger.Level.ERROR, $"Error while waiting for listeners: {e.Message}\n{e.StackTrace}");
}
}
}
and here are the accept parts:
/// <summary>
/// Finish an async callback but spawn a new thread to handle it if necessary
/// </summary>
/// <param name="ar"></param>
private void AcceptConnection(IAsyncResult ar)
{
if (ar.CompletedSynchronously)
{
// Force the accept logic to run async, to keep our listening
// thread free.
Action accept = () => AcceptCallback(ar);
accept.BeginInvoke(accept.EndInvoke, null);
} else
{
AcceptCallback(ar);
}
}
private void AcceptCallback(IAsyncResult ar)
{
try
{
TcpListener listener = (TcpListener) ar.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(ar);
// If the SSL connection takes longer than 5s we have a problem, and should stop
client.Client.ReceiveTimeout = 5000;
// Attempt to get the IP address of the client we're connecting to
IPEndPoint ipep = (IPEndPoint)client.Client.RemoteEndPoint;
string ip = ipep.Address.ToString();
Logger.Write(Logger.Level.INFO, $"Connection begun to {ip}");
// Authenticate and begin communicating with the client
SslStream stream = new SslStream(client.GetStream(), false);
try
{
stream.AuthenticateAsServer(
serverCertificate,
enabledSslProtocols: System.Security.Authentication.SslProtocols.Tls12,
clientCertificateRequired: false,
checkCertificateRevocation: true
);
stream.ReadTimeout = 3600000;
stream.WriteTimeout = 3600000;
NetworkPlayer player = new NetworkPlayer();
player.Name = ip;
player.Connection.Stream = stream;
player.Connection.Connected = true;
player.Connection.Client = client;
stream.BeginRead(player.Connection.Buffer, 0, 1024, new AsyncCallback(ReadCallback), player);
}
catch (Exception e)
{
Logger.Write(Logger.Level.ERROR, $"Error while starting the connection to {ip}: {e.Message}");
// The following code just calls stream.Close(); and client.Close(); but sends exceptions to my logger.
CloseConnectionSafely(client, stream);
}
}
catch (Exception e)
{
Logger.Write(Logger.Level.ERROR, $"Error while starting a connection to an unknown user: {e.Message}");
}
}
I'm guessing that your primary issue is that you are not disposing the stream and therefore you are getting socket exhaustion.
Apart from that I would advise you to move to fully async code using Task.
public async Task ListenLoop(CancellationToken cancel) // use a cancellation token to shutdown the loop
{
using (var TcpListener listener = new TcpListener(IPAddress.Any, 26000))
{
listener.Start();
while (!cancel.IsCancellationRequested)
{
try
{
var client = await listener.AcceptTcpClientAsync(cancel);
Task.Run(async () => await AcceptConnection(client, cancel));
Logger.Write(Logger.Level.INFO, "continuing the main loop");
// no need to yield due to async
}
catch (OperationCanceledException) { }
catch (Exception e)
{
Logger.Write(Logger.Level.ERROR, $"Error while waiting for listeners: {e.Message}\n{e.StackTrace}");
}
}
listener.Stop();
}
}
private async Task AcceptConnection(TcpClient client, CancellationToken cancel)
{
try
{
using (client)
{
// If the SSL connection takes longer than 5s we have a problem, and should stop
client.Client.ReceiveTimeout = 5000;
await AcceptConnectionImpl(client, cancel);
}
}
catch (OperationCanceledException) { }
catch (Exception e)
{
Logger.Write(Logger.Level.ERROR, $"Error while starting a connection to an unknown user: {e.Message}");
}
}
private async Task AcceptConnectionImpl(TcpClient client, CancellationToken cancel)
{
// Attempt to get the IP address of the client we're connecting to
IPEndPoint ipep = client.Client.RemoteEndPoint;
Logger.Write(Logger.Level.INFO, $"Connection begun to {ipep.Address}");
// Authenticate and begin communicating with the client
using (SslStream stream = new SslStream(client.GetStream(), false))
{
try
{
await stream.AuthenticateAsServerAsync(
serverCertificate,
enabledSslProtocols: System.Security.Authentication.SslProtocols.Tls12,
clientCertificateRequired: false,
checkCertificateRevocation: true
);
stream.ReadTimeout = 3600000;
stream.WriteTimeout = 3600000;
NetworkPlayer player = new NetworkPlayer();
player.Name = ip;
player.Connection.Stream = stream;
player.Connection.Connected = true;
player.Connection.Client = client;
player.Cancellation = cancel;
await player.YourReadLoopAsync();
}
catch (OperationCanceledException) { }
catch (Exception e)
{
Logger.Write(Logger.Level.ERROR, $"Error while starting the connection to {ip}: {e.Message}");
// The following code just calls stream.Close(); and client.Close(); but sends exceptions to my logger.
CloseConnectionSafely(client, stream);
}
}
}
The function YourReadLoopAsync should read data from the stream using ReadAsync, or using classes like StreamReader which also has async functions.
You don't need to use CancellationToken, but it does make it easier to deal with shutting everything down cleanly. Make sure to catch OperationCanceledException on every try.
See also this link for further tips.
The solution I found after consulting some people more familiar with C# than me is that I was running into Thread Pool Exhaustion. Essentially, I had a bunch of other async tasks (not shown in the code in the question, as they didn't look like they could cause what I was seeing) that were stuck executing some extremely-long-IOs (talking to users that had either disconnected improperly or were behind very high latency), which prevented the async AcceptCallback in my post from being picked up by the Thread Pool. This had a myriad of other side-effects which I outlined in the question:
Creating a new connection to a MySQL database involves an async task behind-the-scenes, which was being starved out due to exhaustion.
Completing the EndAcceptTcpClient required my async task to run, which requires an available thread.
Tasks which did not involve the async keyword, such as Timer() bound tasks (like my logger I/O) were unaffected and could still run.
My solution involved reducing the number of synchronization steps elsewhere in my program, and restructuring any tasks that could take a long time to execute so that they didn't block threads. Thank you to everyone who looked/commented.
So I have a TcpClient in a console app that is listening on port 9096. I want the client to be able to handle multiple connections (simultaneous or not). I also do not want to use Threads. I want to use async/await. I also need to be able to gracefully close the app during certain events, being careful not to lose any data. So I need a cancellation token. I have the code mostly working but there are two issues.
First, when the app starts listening and I send it data; everything works correctly as long as the sender is using the same initial connection to the app. Once a new connection (or socket I guess? not clear on the terminology) is established the app does not process the new data.
Second, when the terminate signal is given to the app and the token is canceled the app does not close. I am not getting any exceptions and I cannot figure out what I an doing wrong.
I have looked all over and cannot find an example of a TcpClient that uses async/await with a cancellation token. I also cannot find an example that I have been able to get working that correctly processes multiple connections, without using Threads or other complicated designs. I want the design as simple as possible with as little code as possible while still meeting my requirements. If using threads is the only way to do it I will, but I am soo close to getting it right I feel like I am just missing a little thing.
I am trying to figure this out at my wits end and have exhausted all my ideas.
EDIT: I moved the AcceptTcpClientAsync into the loop as suggested below and it did not change anything. The app functions the same as before.
Program.cs
class Program
{
private static List<Task> _listeners = new List<Task>();
private static readonly CancellationTokenSource cancelSource = new CancellationTokenSource();
static void Main(string[] args)
{
Console.TreatControlCAsInput = false;
Console.CancelKeyPress += (o, e) => {
Console.WriteLine("Shutting down.");
cancelSource.Cancel();
};
Console.WriteLine("Started, press ctrl + c to terminate.");
_listeners.Add(Listen(cancelSource.Token));
cancelSource.Token.WaitHandle.WaitOne();
Task.WaitAll(_listeners.ToArray(), cancelSource.Token);
}
}
Listen
public async Task Listen(CancellationToken token){
var listener = new TcpListener(IPAddress.Parse("0.0.0.0"), 9096);
listener.Start();
Console.WriteLine("Listening on port 9096");
while (!token.IsCancellationRequested) {
// Also tried putting AcceptTcpClientAsync here.
await Task.Run(async () => {
var client = await listener.AcceptTcpClientAsync();
using (var stream = client.GetStream())
using (var streamReader = new StreamReader(stream, Encoding.UTF8))
using (var streamWriter = new StreamWriter(stream, Encoding.UTF8)) {
while (!token.IsCancellationRequested) {
// DO WORK WITH DATA RECEIVED
vat data = await streamReader.ReadAsync();
await streamWriter.WriteLineAsync("Request received.");
}
}
});
}
Console.WriteLine("Stopped Accepting Requests.");
listener.Server.Close();
listener.Stop();
}
This is actually working the way you designed it, however you have only built to receive one connection. I am not going to write a full socket implementation for you (as this can get fairly in-depth). However, as for your main problem, you need to put the AcceptTcpClientAsync in the loop otherwise you won't get any more connections:
var cancellation = new CancellationTokenSource();
...
var listener = new TcpListener(...);
listener.Start();
try
{
while (!token.IsCancellationRequested)
{
var client = await listener.AcceptTcpClientAsync()
...
}
}
finally
{
listener.Stop();
}
// somewhere in another thread
cancellation.Cancel();
Update
I tried that and no behavior changed. Still does not pick up any
connection after the first.
await ...
while (!token.IsCancellationRequested) {
// DO WORK WITH DATA RECEIVED
It's obvious that AcceptTcpClientAsync will never get called again because you are awaiting the task. This method is what accepts the client, if you can't call it, you don't get any more clients.
You cannot block here, which is what you are doing. Please see some socket server examples to get a better idea of how to write a listener.
I am implementing a piece of software that reads a list of ids from a message queue. Once some come through, I would like to pass each one through a socket to a third party application, that will then process it and return a value back once it's done.
If the third party app takes too long to reply, I want to report this and maybe even close the connection.
Furthermore, this should run asynchronously, that is, once the messages are read from the queue, a separate task is started to handle it being sent to the socket and any subsequent communication.
Following this I have created a class that spawns a task and sends an exception after a timeout threshold.
public async Task Run(Action action, int timeoutInSeconds)
{
try
{
await Task.Run(action).TimeoutAfter(timeoutInSeconds);
}
catch (TimeoutException te)
{
//add error capture here or retry
}
}
public static async Task TimeoutAfter(this Task task, int timeoutInSeconds)
{
if (task == await Task.WhenAny(task, Task.Delay(timeoutInSeconds*1000)))
{
await task;
}
else
{
throw new TimeoutException(string.Format("Task {0} timed out after {1} seconds", task.Id, timeoutInSeconds));
}
}
Next I created another class to asynchronously listen to connections.
public class SocketListener
{
...
public async void Listen(Action action)
{
//initialization code
var listener = new TcpListener(ipAddress, Port);
listener.Start(numberOfConnections);
while (true)
{
try
{
//wait for client to connect
var client = await listener.AcceptTcpClientAsync();
//do something once client is connected
var task = new TaskWithTimeout();
await task.Run(() => action, 10);
}
catch (Exception ex)
{
// Log error
throw;
}
}
}
...
}
Here, after the client connects successfully, I want to call a method that will handle communication between server and client. If the client takes too long to respond, the TaskWithTimeout should throw an exception and move on.
My thought process was to call SocketListener once I read from the queue
public void ProcessQueue() {
//initialize SocketListener
listener.Listen(MethodToHandleCommunication)
...
}
Now I am a bit stuck. Preferably, SocketListener should be able to handle any type of communication, and that's why I thought I'd pass the Action as a parameter, so that I can determine what method I want to run from outside (by this I mean that if in the future I need to pass different data to the client, I would be able to reuse this code). However with this approach, I cannot even pass the client object back to the action.
In general I feel like I'm taking the wrong approach, and I am sure there's a better and more efficient way of doing what I want. As you can see I'm fairly new to parallel programming in general. I am a bit frustrated with this and would greatly appreciate any help or insight from SO
I have been writing a command line program in C# that uses multiple tcp clients that all connect to the same server. Each client resides in it's own thread. At the moment I am trying to work out an effective method of spreading say 5 requests a second efficiently between let's say 4 threads.
My code currently looks like the following but I still end up with requests overlapping each other. Does anyone have any idea how to prevent these overlaps effectively?
// Max connections is 4, interval is 200
// Loop once to give tcp clients chance to connect
var tcpClients = new TcpClient[_maxConnections];
for(int i = 0; i < _maxConnections; i++)
{
tcpClients[i] = new TcpClient();
tcpClients[i].Connect(host, port);
}
// Loop again to setup tasks
for(int i = 0; i < _maxConnections; i++)
{
Task.Factory.StartNew(TcpHandler, tcpClients[i]);
// Sleep so every task starts separate from each other.
Thread.Sleep(_interval);
}
And then the TcpHandler code looks like:
public static void TcpHandler(Object o)
{
// active is already declared
while(_active)
{
var tcpClient = (TcpClient) o;
// .. do some send and receive...
Console.WriteLine("Something here..");
Thread.Sleep(_interval * _maxConnections);
}
}
So as you can see I am sleeping to provide sufficient space between each thread executing yet now and then they still overlap.
How can I make this threads run parallel without any overlap and limit to 5 times a second spread across all 4?
Or am I going about this all wrong?
Presuming each client requires a separate thread, and that only one thread may be communicating with the server at a given time (no overlap), a lock in the TcpHandler method should suffice:
// Max connections is 4, interval is 200
// Loop once to give tcp clients chance to connect
var tcpClients = new TcpClient[_maxConnections];
// dedicated lock object
static readonly object lockObject = new object();
And then in your TcpHandler method
public static void TcpHandler(Object o)
{
// active is already declared
while(_active)
{
//DO NON-SOCKET RELATED STUFF HERE
// ... code ...
//
//DO SOCKET RELATED STUFF HERE
lock(lockObject)
{
var tcpClient = (TcpClient) o;
// .. do some send and receive...
Console.WriteLine("Something here..");
Thread.Sleep(_interval * _maxConnections);
}
}
}
I am not quite sure why you are doing this but I have used System.Timers (actually an array of timers) in windows services and have staggered the start (intervals).
In the Elapse event maybe you could use a lock(myobject) { } so they don't overlap?
Gina
I think you are using sleep to manage connection times.. Why not instead setup a "Maximum connection delay" then use BeginConnect and a Timer to look after the connection.
eg.
//setup a timer variable
TCPClient connectionOpening;
_connecting = true;
_connected = false;
connectionOpening = tcpClient;
timer.change(5000, Infinite)
tcpClient.BeginConnect(ClientConnectCallback, tcpClient)
void ClientConnectCallback(iasyncresult ar)
{
_timer.change(infinite, infinite);
TCPClient tcp = (TCPClient)ar.AsyncState;
try
{
//if we have timed out because our time will abort the connect
tcp.EndConnect(ar);
_connected = true;
_connecting = false;
//we are now connected... do the rest you want to do.
//get the stream and BeginRead etc.
}
catch (Exception ex) // use the proper exceptions IOException , socketException etc
{
if (!_connecting)
{
//We terminated the connection because our timer ticked.
}
else
{
//some other problem that we weren't expecting
}
}
void TimerTick(object state)
{
_connecting = false;
_connected = false;
connectionOpening.Close();
}
I have the following code, I want to abort a thread if it is unfinished in 2 secs.
You can see from the first code that i create a new myThread evertyime in the while loop and do not abort it. Well, I dont want to it to be like this, but if i take the myThread outside the loop and use the abort() function as the second code does. there will be error in aborting.
while (true)
{
try
{
m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//reset up socket
myThread = new System.Threading.Thread(new System.Threading.ThreadStart(socket_connect));
myThread.Start();
if (!myThread.Join(2000))
{
throw new SocketException(SocketError.AccessDenied);
}
}
catch (Exception ex)
{
m_socket.Close();
}
}
}
private static void socket_connect()
{
m_socket.Connect(remoteEndPoint);//Connect to remote device
}
I was trying the following code at first, however it give threadabortexceptions.
myThread = new System.Threading.Thread(new System.Threading.ThreadStart(socket_connect));
while (true)
{ try
{
m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//reset up socket
myThread.Start();
if (!myThread.Join(2000))
{
myThread.Abort();
throw new SocketException(SocketError.AccessDenied);
}
}
catch (Exception ex)
{
m_socket.Close();
}
}
}
private static void socket_connect()
{
m_socket.Connect(remoteEndPoint);//Connect to remote device
}
I know abort() is not a good idea so I turn to let the threads stay and let C#(.Net? I dont know who actually does that) dealing with the garbage collection. And can anyone tell if it is a good idea since this program will be run on a board which does not have lots of memory for holding bunches of threads. And can someone tell me how exactly the garbage collection is done in C#? for example the threads.
Another thing need to be mention is that i dont have the Task class, or the socket.beginconnect() method, I guess its because i'm building a program that is going to run on a small board, not a pc. The board is a netduido plus and I'm building my project on a netduino plus platform.
I have the following code, I want to abort a thread if it is unfinished in 2 secs.
Reading into the code, it looks like you actually want to attempt to connect a socket to a resource within two seconds. If more than two seconds elapses, you want to move on.
I'm mostly reproducing the code in this answer, I believe this is approximately what you should be doing to achieve your goal, rather than spinning up a thread and aborting it:
Socket socket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
// Connect using a timeout (2 seconds)
IAsyncResult result = socket.BeginConnect( sIP, iPort, null, null );
bool success = result.AsyncWaitHandle.WaitOne( 2000, true );
if ( !success )
{
// NOTE, MUST CLOSE THE SOCKET
socket.Close();
throw new ApplicationException("Failed to connect server.");
}
// Success
//...
[edit: massive copypasta fail when untabbing/spacing]
Oh, please use the Task library, it's way easier to deal with these situations:
(LINQPad-friendly blob)
void Main()
{
var canceller = new CancellationTokenSource();
var task = Task.Factory.StartNew(() => DoStuff(canceller.Token), canceller.Token);
if(!task.Wait(2000, canceller.Token))
{
canceller.Cancel();
task.Wait(2);
}
sw.Elapsed.Dump();
}
private Stopwatch sw;
private void DoStuff(CancellationToken token)
{
try
{
sw = Stopwatch.StartNew();
while(!token.IsCancellationRequested)
{
}
}
// no catch - rethrown exceptions must be checked on Task
finally
{
sw.Stop();
}
}
Alternatively, you can use some "exit flag" condition - a bool that both your thread-starter and your thread-runner can see/alter, and use that in your while condition.