AcceptTcpClientAsync doesn't wait for the response - c#

I'm trying to write a console application
_mainListener = new TcpListener(new IPEndPoint(IPAddress.Any, 5000));
_mainListener.Start();
try
{
while (true)
{
TcpClient client = await _mainListener.AcceptTcpClientAsync();
client.ReceiveTimeout = 60000;
WaitForData(client);
}
}
catch (Exception ex)
{
StartListening();
}
Application is being terminated immediately just after the
TcpClient client = await _mainListener.AcceptTcpClientAsync();
I know there is no problem with connecting to port 5000 cause AcceptTcpClient works unlike the Async method, it goes to WaitForData with a valid response. I need to use Async one cause the app'll connect to multiple ips. What am i missing?
Thanks in advance..

The await keyword causes execution of your method to stop, and the remainder of the method will continue once a client connects.
However, if this is the only code you're calling from your Main() method, then the program will terminate as soon as you get to that line as there's nothing left running.
I don't think you need to use async for what you're trying to do, provided WaitForData() is async internally then this code will just go round a loop continuing to accept more clients.

Related

SignalR , how to handle Startasync failing

I'm encountering some problems with a signalR hub , i need to invoke a certain method
from a signalR hub whenever i receive a request from an external service .
Whenever i do receive such "request" i connect to the signalRhub and then invoke one of its methods.
string hostname = Environment.GetEnvironmentVariable("FRONTEND_HOSTNAME");
HubConnection connection;
connection = new HubConnectionBuilder()
.WithUrl($"{hostname}/xxx")
.Build();
await connection.StartAsync();
var resp = new
{
xx:'xx',
};
await connection.InvokeAsync("SendReport", resp);
connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0, 5) * 1000);
await connection.StartAsync();
};
The whole implementation has already been done , although it seems that i'm able to connect & send messages ALMOST always without problems,
however from time to time the StartAsync() method fails and throws an exception "Name or service not known" , to me it looks like it's unable to connect due to network issues , if you had to implement a way to retry the StartAsync a few times to then invoke the hub method, how would you do it?
I'm not asking for a straight solution , an example of something / links to documentation would still be great, i did search for a way to handle this but with no luck
Sorry it appears i didn't look properly within the documentation ,
here's the answer i was looking for:
public static async Task<bool> ConnectWithRetryAsync(HubConnection connection, CancellationToken token){
// Keep trying to until we can start or the token is canceled.
while (true)
{
try
{
await connection.StartAsync(token);
Debug.Assert(connection.State == HubConnectionState.Connected);
return true;
}
catch when (token.IsCancellationRequested)
{
return false;
}
catch
{
// Failed to connect, trying again in 5000 ms.
Debug.Assert(connection.State == HubConnectionState.Disconnected);
await Task.Delay(5000);
}
}}
https://learn.microsoft.com/en-us/aspnet/core/signalr/dotnet-client?view=aspnetcore-6.0&tabs=visual-studio

How can I restart communication with an FTP server that sends back reset packets without restarting our process?

We have a (long-running) Windows service that among other things periodically communicates with an FTP server embedded on a third-party device using FtpWebRequest. This works great most of the time, but sometimes our service stops communicating with the device, but as soon as you restart our service everything starts working again.
I've spent some time debugging this with an MCVE (included below) and discovered via Wireshark that once communication starts failing there is no network traffic going to the external FTP server (no packets at all show up going to this IP in Wireshark). If I try to connect to the same FTP from another application on the same machine like Windows explorer everything works fine.
Looking at the packets just before everything stops working I see packets with the reset (RST) flag set coming from the device, so I suspect this may be the issue. Once some part of the network stack on the computer our service in running on receives the reset packet it does what's described in the TCP resets section of this article and blocks all further communication from our process to the device.
As far as I can tell there's nothing wrong with the way we're communicating with the device, and most of the time the exact same code works just fine. The easiest way to reproduce the issue (see MCVE below) seems to be to make a lot of separate connections to the FTP at the same time, so I suspect the issue may occur when there are a lot of connections being made to the FTP (not all by us) at the same time.
The thing is that if we do restart our process everything works fine, and we do need to re-establish communication with the device. Is there a way to re-establish communication (after a suitable amount of time has passed) without having to restart the entire process?
Unfortunately the FTP server is running embedded on a fairly old third-party device that's not likely to be updated to address this issue, and even if it were we'd still want/need to communicate with all the ones already out in the field without requiring our customers to update them if possible.
Options we are aware of:
Using a command line FTP client such as the one built into Windows.
One downside to this is that we need to list all the files in a directory and then download only some of them, so we'd have to write logic to parse the response to this.
We'd also have to download the files to a temp file instead of to a stream like we do now.
Creating another application that handles the FTP communication part that we tear down after each request completes.
The main downside here is that inter-process communication is a bit of a pain.
MCVE
This runs in LINQPad and reproduces the issue fairly reliably. Typically the first several tasks succeed and then the issue occurs, and after that all tasks start timing out. In Wireshark I can see that no communication between my computer and the device is happening.
If I run the script again then all tasks fail until I restart LINQPad or do "Cancel All Threads and Reset" which restarts the process LINQPad uses to run the query. If I do either of those things then we're back to the first several tasks succeeding.
async Task Main() {
var tasks = new List<Task>();
var numberOfBatches = 3;
var numberOfTasksPerBatch = 10;
foreach (var batchNumber in Enumerable.Range(1, numberOfBatches)) {
$"Starting tasks in batch {batchNumber}".Dump();
tasks.AddRange(Enumerable.Range(1, numberOfTasksPerBatch).Select(taskNumber => Connect(batchNumber, taskNumber)));
await Task.Delay(TimeSpan.FromSeconds(5));
}
await Task.WhenAll(tasks);
}
async Task Connect(int batchNumber, int taskNumber) {
try {
var client = new FtpClient();
var result = await client.GetFileAsync(new Uri("ftp://192.168.0.191/logging/20140620.csv"), TimeSpan.FromSeconds(10));
result.Count.Dump($"Task {taskNumber} in batch {batchNumber} succeeded");
} catch (Exception e) {
e.Dump($"Task {taskNumber} in batch {batchNumber} failed");
}
}
public class FtpClient {
public virtual async Task<ImmutableList<Byte>> GetFileAsync(Uri fileUri, TimeSpan timeout) {
if (fileUri == null) {
throw new ArgumentNullException(nameof(fileUri));
}
FtpWebRequest ftpWebRequest = (FtpWebRequest)WebRequest.Create(fileUri);
ftpWebRequest.Method = WebRequestMethods.Ftp.DownloadFile;
ftpWebRequest.UseBinary = true;
ftpWebRequest.KeepAlive = false;
using (var source = new CancellationTokenSource(timeout)) {
try {
using (var response = (FtpWebResponse)await ftpWebRequest.GetResponseAsync()
.WithWaitCancellation(source.Token)) {
using (Stream ftpStream = response.GetResponseStream()) {
if (ftpStream == null) {
throw new InvalidOperationException("No response stream");
}
using (var dataStream = new MemoryStream()) {
await ftpStream.CopyToAsync(dataStream, 4096, source.Token)
.WithWaitCancellation(source.Token);
return dataStream.ToArray().ToImmutableList();
}
}
}
} catch (OperationCanceledException) {
throw new WebException(
String.Format("Operation timed out after {0} seconds.", timeout.TotalSeconds),
WebExceptionStatus.Timeout);
} finally {
ftpWebRequest.Abort();
}
}
}
}
public static class TaskCancellationExtensions {
/// http://stackoverflow.com/a/14524565/1512
public static async Task<T> WithWaitCancellation<T>(
this Task<T> task,
CancellationToken cancellationToken) {
// The task completion source.
var tcs = new TaskCompletionSource<Boolean>();
// Register with the cancellation token.
using (cancellationToken.Register(
s => ((TaskCompletionSource<Boolean>)s).TrySetResult(true),
tcs)) {
// If the task waited on is the cancellation token...
if (task != await Task.WhenAny(task, tcs.Task)) {
throw new OperationCanceledException(cancellationToken);
}
}
// Wait for one or the other to complete.
return await task;
}
/// http://stackoverflow.com/a/14524565/1512
public static async Task WithWaitCancellation(
this Task task,
CancellationToken cancellationToken) {
// The task completion source.
var tcs = new TaskCompletionSource<Boolean>();
// Register with the cancellation token.
using (cancellationToken.Register(
s => ((TaskCompletionSource<Boolean>)s).TrySetResult(true),
tcs)) {
// If the task waited on is the cancellation token...
if (task != await Task.WhenAny(task, tcs.Task)) {
throw new OperationCanceledException(cancellationToken);
}
}
// Wait for one or the other to complete.
await task;
}
}
This reminds me of old(?) IE behaviour of no reload of pages even when the network came back after N unsuccessful tries.
You should try setting the FtpWebRequest's cache policy to BypassCache.
HttpRequestCachePolicy bypassPolicy = new HttpRequestCachePolicy(
HttpRequestCacheLevel.BypassCache
);
ftpWebRequest.CachePolicy = bypassPolicy;
after setting KeepAlive.
I had the same issue, when trying to connect to an ftps server without the EnableSsl = true. The connection would fail twice, Wireshark shows the RST command, and then no more requests would leave the network resulting in the timeout exception, even after setting the EnableSsl = true.
I found setting the ConnectionGroupName allows the connection to reset and use a new port.
eg:
request.ConnectionGroupName = Guid.NewGuid();
Beware of port exhaustion using this method however, see https://learn.microsoft.com/en-us/troubleshoot/dotnet/framework/ports-run-out-use-connectiongroupname

Proper use of async / await and Task

I'm trying to learn those "new" keywords and tried to implement a simple async udp server.
public class UdpServerSync
{
private CancellationTokenSource _cts;
private CancellationToken _token;
private UdpClient _client;
public void Start()
{
Console.WriteLine("Start server");
_cts = new CancellationTokenSource();
_token = _cts.Token;
var ipAddress = IPAddress.Parse("192.168.0.25");
var ip = new IPEndPoint(ipAddress, 7070);
try
{
Task.Run(async () =>
{
using (_client = new UdpClient(ip))
{
while (!_token.IsCancellationRequested)
{
var receivedData = await _client.ReceiveAsync();
var msg = Encoding.ASCII.GetString(receivedData.Buffer);
// Process request e.g ProcessRequest(msg);
Console.WriteLine(msg);
}
}
}, _token).ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
public void Stop()
{
Console.WriteLine("Stop server");
if (_cts != null) _cts.Cancel();
}
And then use it like this (for testing purpose):
var server = new UdpServerSync();
server.Start();
await Task.Delay(5000);
server.Stop();
The above code is just a proof of concept, its not about code review. By a simple udp server I mean a while loop with a udpclient listening for udp messages and writing them to the console - no processing or error handling.
The reason for the Task.Delay is just because its a proof of concept of calling the server's start() and stop() methods.
To narrow down my questions:
1) if i was going to call the Start() and Stop() methods from e.g a WPF applicationĀ“s start button, should I use server.Start() or Task.Run ? I don't want to await the call since there's no way to know how long the user is going to want the server started.
2) in the server code "ProcessRequest(msg), if that was a void method in another library, should I use Task.Run() to execute it to avoid the server thread being blocked or is there a better way ?
3) When we do async/await, does the code in the await statement execute in a new thread from the thread pool ?
4) Can I specify that the UdpServer is a long running process or it doesn't matter to the thread pool ?
Hope my question is more clear now, thanks guys :)
1) if i was going to call the Start() and Stop() methods from e.g a WPF applicationĀ“s start button, should I use server.Start() or Task.Run ?
With the code you have now, you can just call Start (which calls Task.Run). IMO, the call to Task.Run is unnecessary. And the call to ConfigureAwait is definitely unnecessary since there's no await to configure.
I don't want to await the call since there's no way to know how long the user is going to want the server started.
But you probably do want to know about any exceptions. So, think about how to handle those. One solution is to save the returned Task as a property. Or, you could just await it (remember, it doesn't matter "how long" it runs, because it's an asynchronous wait).
2) in the server code "ProcessRequest(msg), if that was a void method in another library, should I use Task.Run() to execute it to avoid the server thread being blocked or is there a better way ?
There's no "server thread". But if ProcessRequest takes a long time, then you might want to consider using Task.Run so that your code will accept the next request while processing that one.
3) When we do async/await, does the code in the await statement execute in a new thread from the thread pool ?
No. I have an async intro on my blog that goes into more detail.
4) Can I specify that the UdpServer is a long running process or it doesn't matter to the thread pool ?
It doesn't matter.

Server communication via async/await?

I want to create Socket message sending via TAP via async/await.
After reading this answer and this one - I decided to create a fully working sample :
So what have I tried :
I took the TAP extenstion methods from here (all ok) : and I test it in console cmd :
Reciever Code
public static class SocketExtensions
{
public static Task<int> ReceiveTaskAsync(this Socket socket, byte[] buffer, int offset, int count)
{
return Task.Factory.FromAsync<int>(
socket.BeginReceive(buffer, offset, count, SocketFlags.None, null, socket),
socket.EndReceive);
}
public static async Task<byte[]> ReceiveExactTaskAsync(this Socket socket, int len)
{
byte[] buf = new byte[len];
int totalRead = 0;
do{
int read = await ReceiveTaskAsync(socket, buf, totalRead, buf.Length - totalRead);
if (read <= 0) throw new SocketException();
totalRead += read;
}while (totalRead != buf.Length);
return buf;
}
public static Task ConnectTaskAsync(this Socket socket, string host, int port)
{
return Task.Factory.FromAsync(
socket.BeginConnect(host, port, null, null),
socket.EndConnect);
}
public static Task SendTaskAsync(this Socket socket, byte[] buffer)
{
return Task.Factory.FromAsync<int>(
socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, null, socket),
socket.EndSend);
}
}
static void Main()
{
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
s.ConnectTaskAsync("127.0.0.1", 443);
var buf1 = s.ReceiveExactTaskAsync(100); //read exactly 100 bytes
Console.Write(Encoding.UTF8.GetString(buf1.Result));
var buf2 = s.ReceiveExactTaskAsync(100); //read exactly 100 bytes
Console.Write(Encoding.UTF8.GetString(buf2.Result));
Console.ReadLine();
}
Sender Code :
// use same extension method class like above ....^
void Main()
{
Socket s = new Socket(SocketType.Stream , ProtocolType.Tcp);
s.ConnectTaskAsync( "127.0.0.1" , 443);
s.SendTaskAsync(Encoding.UTF8.GetBytes("hello"));
s.Close();
Console.ReadLine();
}
notice I removed the async from main since im testing it in console.
Question ,
According to link above , the code should work
However I'm getting no exception and it's just hangs on that line :
Console.Write(Encoding.UTF8.GetString(buf1.Result));
(First I run receiver , then I run sender)
What am I missing?
the problem comes from the "notice I removed the async from main since im testing it in console.".
You need to wait for the operation to complete before doing the next step. The code you used as an example pauses at each await for the operation to complete, your code just goes straight through.
You may be able to fix this by putting a .Wait() after each operation that would have had a await or by running this function inside a threadpool thread via Task.Run(, however I think it is better to know when you should use async and when you should not.
Async should be used when you have other work you could have the thread be doing, very commonly that "other work" will be things like processing UI messages on a WinForms project or accepting new connections on a ASP.NET site. In a console application there is no other work your program could be doing while it waits, so in that situation it would be more appropriate to use the synchronous version of the functions instead.
P.S. You made the comment after I posted "that's why I remove the async awaits and used Task.result", just so you know never ever1 combine code that uses await and code that blocks the synchronization contest (by using things like Task.Result or Task.Wait(), you will likely cause your code to deadlock and stop functioning!
It is not a issue for your current example because console applications do not have a synchronization context, but if you copied and pasted this code to something that did you could easily lock up your program.
1: Ok, you could combine await and blocking code but there are rules you need to follow, but if you know enough to dispute my what I am saying you know enough to safely do it. If you don't know how to safely do it just avoid doing it
since you do not wait for the threads to do their work and then call s.Close() the socket will be closed before any traffic is sent out.
You would have to either remove the s.Close() call or wait until the calls are complete, for instance via
Task connect = s.ConnectTaskAsync( "127.0.0.1" , 443);
connect.Wait(); // you have to connect before trying to send
Task sendData = s.SendTaskAsync(Encoding.UTF8.GetBytes("hello"));
sendData.Wait(); // wait for the data to be sent before calling s.Close()
s.Close();
or you could box it in a method and Wait for that method to complete. The end result is to not call Close before completing the previous calls.
void Main()
{
Socket s = new Socket(SocketType.Stream , ProtocolType.Tcp);
Task worker = DoWorkAsync(s);
worker.Wait();
s.Close();
Console.ReadLine();
}
private async Task DoWorkAsync(Socket s){
await s.ConnectTaskAsync( "127.0.0.1" , 443);
await s.SendTaskAsync(Encoding.UTF8.GetBytes("hello"));
}

How do I create a new thread each time a new connection is received in C#?

So, I'm kind of new to multithreading and socket programming, especially in C#.
Anyways, I'm trying to create a program that creates each new accepted TcpClient as a new thread.
This is what I made:
public static void Listen()
{
try
{
listener = new TcpListener(IPAddress.Any, port);
listener.Start();
while (true)
{
t = new Thread((client = listener.AcceptTcpClient));
}
}
catch { Listen(); }
}
I also have already declared listener as a TcpListener, t as a Thread, and client as a TcpClient.
The code is fine, except for where I create the thread, it gives me an error.
My ultimate goal is to create a new thread for each accepted connection, then be able to send data to a specific connection.
So, how do I create each connection/client in a new thread? How do I reference a certain thread/connection from another method called Send (to send the data over a stream to a specific thread/connection only)?
Since you mention that you are new to threading and sockets I will recommend a change in approach. Creating a new thread per incoming connection will not scale well. 1000 users results in 1000 threads and your server will spend most of it's time context switching. Instead you should consider using async I/O methods (e.g. TcpListener.BeginAcceptTcpClient). The callback that you provide to this method will be invoked on a .NET thread pool thread only when it is required to do something. Just be sure to synchronize access to instance variables (e.g. via a lock statement) since if two clients connect simultaneously the callbacks may run in parallel (which is, of course, the goal). Best of luck.
Here's my start method for a socket server sample I had. Note new thread takes new ThreadStart as an argument. I can send more of the sample if needed.
It uses ThreadPool.QueueUserWorkitem instead of using a thread per request. Look at the listener.
public void Start()
{
m_protocol = LoadProtocolPlugIn();
// Create a TcpListener to accept client connection requests
TcpListener tcpListener = new TcpListener(m_address, m_port);
tcpListener.Start();
//
// Create a protocol listener per thread
//
for (int i = 0; i < m_listeners; i++)
{
ProtocolListener listener = new ProtocolListener(tcpListener, m_protocol);
Thread thread = new Thread(new ThreadStart(listener.Start));
thread.Start();
Console.WriteLine("Listening on thread: {0}", thread.Name);
}
m_state = ServerState.Started;
}
and here's the protocol listener:
class ProtocolListener
{
TcpListener m_listener;
IProtocol m_protocol = null;
TcpClient m_client = null;
internal ProtocolListener(TcpListener listener, IProtocol protocol)
{
m_listener = listener;
m_protocol = protocol;
}
internal void Start()
{
//
// Block waiting for socket connection and then process. Repeat in endless loop.
//
while (true)
{
try
{
m_client = m_listener.AcceptTcpClient();
ThreadPool.QueueUserWorkItem (new WaitCallback(ProcessClientProtocol), m_protocol);
}
catch (SocketException se)
{
// TODO: replace with logging and event log
Console.WriteLine("Exception = " + se.Message);
}
}
}
private void ProcessClientProtocol (object protocol)
{
Debug.Assert(m_client != null);
Debug.Assert(protocol != null);
((IProtocol)protocol).Client = m_client;
((IProtocol)protocol).ProcessClient();
}
}
This is not a very good way to do things. You should look at the either the BeginXXX/EndXXX pattern or the Async pattern for sockets.
http://msdn.microsoft.com/en-us/library/bbx2eya8.aspx
The Thread constructor takes a delegate, while you are trying to give it an object.
When you create a thread, you need to provide a method to be executed in that thread. For example:
private void HandleClient(TcpClient client)
{
// ...
}
However, it is usually a bad idea to create your own threads. You should use ThreadPool or Task Parallel Library, if you are using .NET 4:
ThreadPool.QueueUserWorkItem(() => HandleClient(acceptedClient));
or
Task.Factory.StartNew(() => HandleClient(acceptedClient));

Categories

Resources