Is this Tcp async/await code considered thread safe? - c#

I wrote some code using TcpListener and async/await which will wait for 3 clients to connect to the server, add them to a list, then do something with the clients:
private async void button1_Click(object sender, EventArgs e)
{
var listener = new TcpListener(IPAddress.Any, 6015);
listener.Start();
var clients = await Task.Run(() => GetTcpClients(listener));
// Do something with clients
listener.Stop();
}
private static async Task<List<TcpClient>> GetTcpClients(TcpListener listener)
{
var clients = new List<TcpClient>();
var clientsMaxReached = false;
while (!clientsMaxReached)
{
var client = await listener.AcceptTcpClientAsync();
clients.Add(client);
if (clients.Count == 3)
clientsMaxReached = true;
}
return clients;
}
What I am concerned about is whether it's okay to use a List for the clients instead of a thread-safe collection, since the await may resume on a different thread. Also, just wondering if my TcpListener usage is thread safe too.
I don't see a problem since no two threads will ever access the list or TcpListener at the same time, but just thought I'd check.

Both the accesses to list and listener are thread safe.
The clients list is not accessed and accessible by anybody else until the list is completely built. Any code after await will not execute until the awaited task/method is completed.
For the listener it's pretty much the same but in opposite direction. The instance is passed to getTcpClients() method but any other access is blocked until this method stops working with the instance.

Related

Multiple connections with TcpClient, second connection always hangs/does nothing

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.

Maximize Task Concurrency - using TCP/IP with specific Port Filtering

This is a follow-up question to this question. On the next level, I now want to use maximal task concurrency to connect to expected hosts on a large set of IP addresses, using TCP/IP on a specific port.
My own research, as well as community reference, has lead me to key articles, for example:
How to check TCP/IP port availability using C# (Socket Communication)
Checking if ip with port is available?
How to set the timeout for a TcpClient?
A very impressive solution for large-scale pinging: Multithreading C# GUI ping example
And of course the precursor to this question: C#, Maximize Thread Concurrency
This allowed me to set up my own code, which works fine, but currently takes a full 30 seconds to finish scanning 255 IPs, using only one specific port. Given the test, machine has 8 logical cores this observation suggests that my construct actually spawns at maximum 8 concurrent tasks (255/8=31.85).
The function I wrote returns a list of responding IPs {IPs} which is a subset of the List of all IPs {IP_Ports} to be checked. This is my current code, working fine but not yet suitable for use on larger networks due to what I suspect is lack of efficient task concurrency:
// Check remote host connectivity
public static class CheckRemoteHost
{
// Private Class members
private static bool AllDone = false;
private static object lockObj = new object();
private static List<string> IPs;
// Wrapper: manage async method <TCP_check>
public static List<string> TCP(Dictionary<string, int> IP_Ports, int TimeoutInMS = 100)
{
// Locals
IPs = new List<string>();
// Perform remote host check
AllDone = false;
TCP_check(IP_Ports, TimeoutInMS);
while (!AllDone) { Thread.Sleep(50); }
// Finish
return IPs;
}
private static async void TCP_check(Dictionary<string, int> IP_Ports, int timeout)
{// async worker method: check remote host via TCP-IP
// Build task-set for parallel IP queries
var tasks = IP_Ports.Select(host => TCP_IPAndUpdateAsync(host.Key, host.Value, timeout));
// Start execution queue
await Task.WhenAll(tasks).ContinueWith(t =>
{
AllDone = true;
});
}
private static async Task TCP_IPAndUpdateAsync(string ip, int port, int timeout)
{// method to call IP-check
// Run method asynchronously
await Task.Run(() =>
{
// Locals
TcpClient client;
IAsyncResult result;
bool success;
try
{
client = new TcpClient();
result = client.BeginConnect(ip, port, null, null);
success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(timeout));
if (success)
{
lock (lockObj)
{
IPs.Add(ip);
}
}
}
catch (Exception e)
{
// do nothing
}
});
}
}// end public static class CheckRemoteHost
So my question is: how can I maximize the task concurrency of requesting a response using TCP/IP at Port X such that I can obtain very fast IP-Port network scans on large internal networks?
Details
The default task scheduler is usually the ThreadPool scheduler. That means the number of concurrent tasks will be limited by the available threads in the pool.
Remarks
The thread pool provides new worker threads or I/O completion threads on demand until it reaches the minimum for each category. By default, the minimum number of threads is set to the number of processors on a system. When the minimum is reached, the thread pool can create additional threads in that category or wait until some tasks complete. Beginning with the .NET Framework 4, the thread pool creates and destroys threads in order to optimize throughput, which is defined as the number of tasks that complete per unit of time. Too few threads might not make optimal use of available resources, whereas too many threads could increase resource contention.
(Source: https://msdn.microsoft.com/en-us/library/system.threading.threadpool.getminthreads(v=vs.110).aspx)
You are likely just under the threshold where the threadpool would spin up new threads since tasks are being completed. Hence why you only have 8 concurrent tasks running at once.
Solutions
1. Use ConnectAsync with a timeout.
Instead of creating a separate task which blocks waiting for the connect. You can call ConnectAsync and join it with a delay to create a timeout. ConnectAsync doesn't seem to block the threadpool threads.
public static async Task<bool> ConnectAsyncWithTimeout(this Socket socket, string host, int port, int timeout = 0)
{
if (timeout < 0)
throw new ArgumentOutOfRangeException("timeout");
try
{
var connectTask = socket.ConnectAsync(host, port);
var res = await Task.WhenAny(connectTask, Task.Delay(timeout));
await res;
return connectTask == res && connectTask.IsCompleted && !connectTask.IsFaulted;
}
catch(SocketException se)
{
return false;
}
}
Example usage
private static async Task TCP_IPAndUpdateAsync(string ip, int port, int timeout)
{// method to call IP-check
client = new TcpClient();
var success = await client.Client.ConnectAsyncWithTimeout(ip, port, timeout);
if (success)
{
lock (lockObj)
{
IPs.Add(ip);
}
}
}
2. Use long running tasks.
Using Task.Factor.StartNew you can specify that the task is LongRunning. The threadpool task scheduler specifically will create a new thread for the task instead of using the threadpool. This will get around the 8 thread limit you are hitting. However, it should be noted that this is not a good solution if you plan to naively create thousands of tasks. Since at that point, the bottle neck will be thread context switches. You could however split all of the work between, for example, 100 tasks.
3. Use non-blocking connect
This method doesn't require creating multiple tasks. Instead you can call multiple connects on a single thread and check the status of multiple sockets at once. This method is a bit more involved though. If you rather go with this approach and want a more complete example then comment letting me know. Here is a quick snippet on how to use the API.
var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
socket.Blocking = false;
try
{
socket.Connect("127.0.0.1", 12345);
}
catch(SocketException se)
{
//Ignore the "A non-blocking socket operation could not be completed immediately" error
if (se.ErrorCode != 10035)
throw;
}
//Check the connection status of the socket.
var writeCheck = new List<Socket>() { socket };
var errorCheck = new List<Socket>() { socket };
Socket.Select(null, writeCheck, errorCheck, 0);
if (writeCheck.Contains(socket))
{
//Connection opened successfully.
}
else if (errorCheck.Contains(socket))
{
//Connection refused
}
else
{
//Connect still pending
}

Right approach for asynchronous TcpListener using async/await

I have been thinking about what is the right way of set up a TCP server by using asynchronous programming.
Usually I would spawn a thread per incoming request, but I would like to do the most of the ThreadPool, so when connections are idle, there are no blocked threads.
First I would create the listener and start to accept clients, in this case, in a Console app:
static void Main(string[] args)
{
CancellationTokenSource cancellation = new CancellationTokenSource();
var endpoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8001);
TcpListener server = new TcpListener(endpoint);
server.Start();
var task = AcceptTcpClients(server, cancellation.Token);
Console.ReadKey(true);
cancellation.Cancel();
await task;
Console.ReadKey(true);
}
In that method, I would loop accepting incoming requests and spawning a new Task to handle the connection, so the loop can get back to accept more clients:
static async Task AcceptTcpClients(TcpListener server, CancellationToken token)
{
while (!token.IsCancellationRequested)
{
var ws = await server.AcceptTcpClientAsync();
Task.Factory.StartNew(async () =>
{
while (ws.IsConnected && !token.IsCancellationRequested)
{
String msg = await ws.ReadAsync();
if (msg != null)
await ws.WriteAsync(ProcessResponse(msg));
}
}, token);
}
}
Creating new Task does not necessarily mean new thread, but is this the right way? Am I taking advantage of the ThreadPool or is there anything else I can do?
Is there any potential pitfall in this approach?
The await task; in your Main won't compile; you'll have to use task.Wait(); if you want to block on it.
Also, you should use Task.Run instead of Task.Factory.StartNew in asynchronous programming.
Creating new Task does not necessarily mean new thread, but is this the right way?
You certainly can start up separate tasks (using Task.Run). Though you don't have to. You could just as easily call a separate async method to handle the individual socket connections.
There are a few problems with your actual socket handling, though. The Connected property is practically useless. You should always be continuously reading from a connected socket, even while you're writing to it. Also, you should be writing "keepalive" messages or have a timeout on your reads, so that you can detect half-open situations. I maintain a TCP/IP .NET FAQ that explains these common problems.
I really, strongly recommend that people do not write TCP/IP servers or clients. There are tons of pitfalls. It would be far better to self-host WebAPI and/or SignalR, if possible.
In order to stop a server accept loop gracefully, I register a callback that stops listening when the cancellationToken is cancelled (cancellationToken.Register(listener.Stop);).
This will throw a SocketException on await listener.AcceptTcpClientAsync(); that is easy to capture.
There is no need for Task.Run(HandleClient()), because calling an async method returns a task that is running in parallel.
public async Task Run(CancellationToken cancellationToken)
{
TcpListener listener = new TcpListener(address, port);
listener.Start();
cancellationToken.Register(listener.Stop);
while (!cancellationToken.IsCancellationRequested)
{
try
{
TcpClient client = await listener.AcceptTcpClientAsync();
var clientTask = protocol.HandleClient(client, cancellationToken)
.ContinueWith(antecedent => client.Dispose())
.ContinueWith(antecedent => logger.LogInformation("Client disposed."));
}
catch (SocketException) when (cancellationToken.IsCancellationRequested)
{
logger.LogInformation("TcpListener stopped listening because cancellation was requested.");
}
catch (Exception ex)
{
logger.LogError(new EventId(), ex, $"Error handling client: {ex.Message}");
}
}
}

async TcpClient.Connect() how to use it?

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

Changing TCPClient port dynamically

Lets say you having the following code.
this._tcpListener.Start();
while (true)
{
//blocks until a client has connected to the server
TcpClient client = this._tcpListener.AcceptTcpClient();
//create a thread to handle communication
//with connected client
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientCommunication));
clientThread.Start(client);
}
private void HandleClientCommunication(object client)
{
using (TcpClient tcpClient = (TcpClient) client)
{
//Do my work
}
}
The problem with such an implementation is that whatever port I have used in the initial connection is then used in the client communication and therefor (despite the fact that the tcpListener is still listening it will be unable to accept other connections till the port is freed).
So is there someway to tell the tcpClient to change the port it is working on or is the only way to implement such functionality by sending back the client a new port number and telling it to reconnect?
IE:
TcpListener1.AcceptTcpClient(); //Wait
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientCommunication));
clientThread.Start(client);
private void HandleClientCommunication(object client)
{
using (TcpClient tcpClient = (TcpClient) client)
{
//Generate random port number send back to client and create another thread with a new tcpListener and wait again?
}
}
Looking at the code it appears that
the localendpoint for the client and
the remoteendpoint for the server
appears to change to a different port
yet the inverse in each case stays the
same.
IE:
Started server tcpListener on 121
ClientLocalEndPoint: {127.0.0.1:1380}
ClientRemoteEndPoint: {127.0.0.1:121}
ServerLocalEndPoint: {127.0.0.1:121}
ServerRemoteEndPoint: {127.0.0.1:1380}
You are incorrect about the problem here; Spawning a new thread to handle the TcpClient and then looping back to TcpListener.AcceptTcpClient() does not require you to change ports; A single server can get multiple connections in to the same socket.
Otherwise, how would web servers handle multiple users at once?
Something else is going wrong with your code somewhere. Your "thread-per-connection" code is not ideal (a thread per connection is much more than is needed), but it is a quick-and-dirty way that works just fine.
How are you constructing the Listener? What are you doing with the client? And what exactly is happening to subsequent connections?
I agree with others that you might want to look at asynchronous methods instead of using separate threads for each connection, or at least BackgroundWorker... As for what is happening, did you try debugging making sure that you had one thread stuck on the AcceptTcpClient call? You could try specifying a high backlog in your Start() call. Could it be something with scope and maybe garbage collection of your thread because you don't maintain a reference for it? If you put a Thread.Sleep(10000) at the end of your loop, can you connect after 10 seconds? If you try using command line telnet to connect to the port (i.e. "telnet localhost 9999") does the screen blank showing that it connected?
You could try something like adding a hashtable to store your threads and remove them on exit which adds the benefit of having a list of them and being able to close the connections and kill them...
Dictionary<TcpClient, Thread> _threads = new Dictionary<TcpClient, Thread>();
object _lockObject = new object();
void AddThread(TcpClient client, Thread thread)
{
lock (_lockObject)
{
_threads.Add(client, thread);
}
}
void RemoveThread(TcpClient client)
{
lock (_lockObject)
{
_threads.Remove(client);
}
}
void YourMainMethod()
{
this._tcpListener.Start();
while (true)
{
//blocks until a client has connected to the server
TcpClient client = this._tcpListener.AcceptTcpClient();
//create a thread to handle communication
//with connected client
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientCommunication));
AddThread(client, clientThread);
clientThread.Start(client);
}
}
private void HandleClientCommunication(object client)
{
try
{
using (TcpClient tcpClient = (TcpClient) client)
{
//Do my work
}
} catch (Exception)
{
// so program doesn't crash
}
finally
{
RemoveThread((TcpClient)client);
}
}

Categories