ReadExactlyAsync not cancellable? - c#

I have a program that communicates with a device over a virtual serial port.
I am working on making it async. However, it seems, that the underlying buffer methods do not honor cancellation, as they should.
var _serialPort = new SerialPort("COM1", 19200, Parity.None);
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
cts.Token.Register(() => Console.WriteLine("Cancelled"));
var buffer = new byte[10];
_serialPort.Open();
await Task.Run(async () =>
{
while (!cts.IsCancellationRequested)
{
Console.WriteLine("Waiting for data");
await _serialPort.BaseStream.ReadExactlyAsync(buffer, cts.Token);
Console.WriteLine("Got data");
}
});
Console.WriteLine("Done");
I got - with no data actually on the stream:
And that's it. What am I missing?

Related

Multiple TCPServer/Client C#

I am trying to learn C# programming language. I have a very simple question here. I wanted to make TCPServer and TCPClient. but I couldn't quite do what I wanted to do. What I want is to have 4 clients on 1 server and they can talk to each other. I want the names of the people speaking to be determined individually. Please can anyone help with this? (Sorry for bad english)
/Server Code/
namespace SimpleTcpSrvr
{
class Program
{
//private static object mTcpClient;
static void Main(string[] args)
{
Console.OutputEncoding = Encoding.UTF8;
int asd;
byte[] data = new byte[1024];
IPEndPoint asd2 = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1302);
Socket newsocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
newsocket.Bind(asd2);
newsocket.Listen(100);
Console.WriteLine("waiting for connection...");
Socket client = newsocket.Accept();
IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint;
var socket = (Socket)client;
Console.WriteLine((string.Format("New connection: " + socket.RemoteEndPoint.ToString())));
string welcome = "Chat Server";
data = Encoding.UTF8.GetBytes(Welcome my chat server);
client.Send(data, data.Length, SocketFlags.None);
string input;
while (true)
{
data = new byte[1024];
asd = client.Receive(data);
if (asd == 0)
break;
Console.WriteLine("TCPClient: " + Encoding.UTF8.GetString(data, 0, asd));
input = Console.ReadLine();
Console.SetCursorPosition(0, Console.CursorTop - 1);
Console.WriteLine("You: " + input);
client.Send(Encoding.UTF8.GetBytes(input));
}
Console.WriteLine("{0}'sinin bağlantısı kesildi.", clientep.Address);
client.Close();
newsocket.Close();
Console.ReadLine();
}
/Client Code/
namespace TCPClient
{
public class TcpClient
{
static void Main(string[] args)
{
Console.OutputEncoding = Encoding.ASCII;
byte[] data = new byte[1024];
string input, stringData;
IPEndPoint asd2 = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1302);
Socket newsocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
newsocket.Connect(asd2);
}
catch (SocketException e)
{
Console.WriteLine("cant connect server");
Console.WriteLine(e.ToString());
return;
}
int asd = newsocket.Receive(data);
stringData = Encoding.UTF8.GetString(data, 0, asd);
Console.WriteLine(stringData);
while (true)
{
input = Console.ReadLine();
Console.SetCursorPosition(0, Console.CursorTop - 1);
Console.WriteLine("You: " + input);
newsocket.Send(Encoding.UTF8.GetBytes(input));
data = new byte[1024];
asd = newsocket.Receive(data);
stringData = Encoding.UTF8.GetString(data, 0, asd);
byte[] utf8string = Encoding.UTF8.GetBytes(stringData);
Console.WriteLine("TCPServer:" + stringData);
}
Console.WriteLine("connection lost from server...");
newsocket.Shutdown(SocketShutdown.Both);
newsocket.Close();
Console.WriteLine("disconnected!");
Console.ReadLine();
}
}
}
Firstly, it has to be said, there are a million-and-one edge cases when working with sockets - unless you are actually writing a new transport-protocol to run over TCP, you'd be much better off investing your time into learning an existing transport such as HTTP or gRPC.
With that disclaimer out of the way:
Use TcpListener() instead of Socket() in your server.
Use TcpClient() instead of Socket() in your client.
Your server needs to be able to simultaneously handle multiple clients, for this, use the Task-based Asyncronous Pattern (TAP).
So in your server, you need an instance of TcpClient() for every connection that your server accepts().
Start with a place to store all of your Tasks, e.g.,
static List<Task> Tasks = new();
Next you need a TcpListener which accepts incomming connections and spawns a Task to manage the TcpClient associated with that connection e.g.,
static async Task RunServerAsync()
{
TcpListener tcpListener = new(IPAddress.Loopback, 9999);
tcpListener.Start();
while (true)
{
var tcpClient = await tcpListener.AcceptTcpClientAsync();
Tasks.Add(Task.Run(() => RunClientAsync(tcpClient)));
}
}
A simple TcpClient Task would look something like this
static async Task RunClientAsync(TcpClient tcpClient)
{
Console.WriteLine($"Connection from: [{tcpClient.Client.RemoteEndPoint}]");
var reader = new StreamReader(tcpClient.GetStream());
while (true)
{
var line = await reader.ReadLineAsync();
Console.WriteLine($"{tcpClient.Client.RemoteEndPoint}: {line}");
}
}
You can tie this together in a Main() like this:
static async Task Main(string[] args)
{
await Task.Run(() => RunServerAsync());
}
Now you have a very simple echo-line server that will accept connections from as many clients as you can throw at it.
To wrap it up, you could use the TAP to create clients, for your testing, maybe something like:
static async Task RunClientAsync(string message)
{
var tcpClient = new TcpClient("127.0.0.1", 9999);
StreamWriter sw = new(tcpClient.GetStream());
tcpClient.NoDelay = true;
while(true)
{
await sw.WriteLineAsync(message);
await sw.FlushAsync();
await Task.Delay(1000);
}
}
And of course you'd need to update your Main to support this, e.g.,
static async Task Main(string[] args)
{
_ = Task.Run(() => RunServerAsync());
await Task.Delay(1000); // give the server a sec to start
_ = Task.Run(() => RunClientAsync("This is from client1"));
_ = Task.Run(() => RunClientAsync("Client2 is here!!"));
_ = Task.Run(() => RunClientAsync("And I am client3"));
await Task.Delay(int.MaxValue);
}
Of course in a real application you'd never fire and forget your Tasks like that.
Full code for the sake of completeness:
class Program
{
static List<Task> Tasks = new();
static async Task RunClientAsync(TcpClient tcpClient)
{
Console.WriteLine($"Connection from: [{tcpClient.Client.RemoteEndPoint}]");
var reader = new StreamReader(tcpClient.GetStream());
while (true)
{
var line = await reader.ReadLineAsync();
Console.WriteLine($"{tcpClient.Client.RemoteEndPoint}: {line}");
}
}
static async Task RunServerAsync()
{
TcpListener tcpListener = new(IPAddress.Loopback, 9999);
tcpListener.Start();
while (true)
{
var tcpClient = await tcpListener.AcceptTcpClientAsync();
Tasks.Add(Task.Run(() => RunClientAsync(tcpClient)));
}
}
static async Task Main(string[] args)
{
_ = Task.Run(() => RunServerAsync());
await Task.Delay(1000); // give the server a sec to start
_ = Task.Run(() => RunClientAsync("This is from client1"));
_ = Task.Run(() => RunClientAsync("Client2 is here!!"));
_ = Task.Run(() => RunClientAsync("And I am client3"));
await Task.Delay(int.MaxValue);
}
static async Task RunClientAsync(string message)
{
var tcpClient = new TcpClient("127.0.0.1", 9999);
StreamWriter sw = new(tcpClient.GetStream());
tcpClient.NoDelay = true;
while(true)
{
await sw.WriteLineAsync(message);
await sw.FlushAsync();
await Task.Delay(1000);
}
}
}

How to run async task in background?

I need to forward the port of running service in k8s cluster. So I have use the example provided in github repo to do port forward for specific service. Now I would like to use the example in my test but when I call port forward method then I always get web socket exception but if I written the same code in console application then I don't get any exception it run perfectly fine.
private async Task<bool> ForwardPortOperation(string podName, int[] originalPort, int forwardPort)
{
var pod = GetPodObject(podName);
var webSocket = await _config.GetK8SClient().WebSocketNamespacedPodPortForwardAsync(pod.Metadata.Name, pod.Metadata.NamespaceProperty, originalPort, WebSocketProtocol.V4BinaryWebsocketProtocol);
var demux = new StreamDemuxer(webSocket);
demux.Start();
var stream = demux.GetStream((byte?)0, (byte?)0);
IPAddress ipAddress = IPAddress.Loopback;
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, forwardPort);
Socket listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(localEndPoint);
listener.Listen(100);
Socket handler = null;
var accept = Task.Run(() =>
{
while (true)
{
handler = listener.Accept();
var bytes = new byte[4096];
while (true)
{
int bytesRec = handler.Receive(bytes);
stream.Write(bytes, 0, bytesRec);
if (bytesRec == 0 || Encoding.Default.GetString(bytes, 0, bytesRec).IndexOf("<EOF>", StringComparison.InvariantCultureIgnoreCase) > -1)
{
break;
}
listener.Close();
}
}
});
var copy = Task.Run(() =>
{
var buff = new byte[4096];
while (true)
{
var read = stream.Read(buff, 0, 4096);
handler.Send(buff, read, 0);
}
});
await accept;
await copy;
return true;
}
Port forward method:
public bool PortForward(string podName, int[] originalPort, int forwardPort)
{
return Task.Run(() => ForwardPortOperation(podName, originalPort, forwardPort)).Result;
}
Nunit test project:
[Test]
public void VerifyPortForward()
{
new Pod(_config).PortForward("web_service_application", new int[3100], 1615);
}
How can I run such async method in background? so that once port forward successful then I can continue to another test.
Exception:
https://gist.github.com/gittadesushil/bc3bf008b7a4fc62c33e97ab1fbf687f
I assume the method PortForward should be rewritten to be asynchronous in the following way:
public async Task<bool> PortForward(string podName, int[] originalPort, int forwardPort)
{
return await Task.Run(() => ForwardPortOperation(podName, originalPort, forwardPort));
}
After that you can write a NUnit test:
[Test]
public async Task VerifyPortForward()
{
var pod = new Pod(_config);
var result = await pod.PortForward("web_service_application", new int[3100], 1615);
// TODO: assert the result
}

I have a problem getting response on websocket

I use the websocket API for speech recognition.
I send audio to the server and receive a response.
With this websocket API, it is possible to obtain voice recognition results while streaming voice data.
I can get the final recognition result, but I can not get the recognition result on the way.
The following shows the referenced code.
https://gist.github.com/arjun-g/75961830d363cc9265e4ac2ca095168b
After various trials, I found that line:85 was a problem.
This part seems to be waiting for the final result.
I may not have received the result on the way at all.
Please tell me why I can't get result on the way.
private async Task Connect(ClientWebSocket clientWebSocket)
{
CancellationTokenSource cts = new CancellationTokenSource();
clientWebSocket.Options.SetRequestHeader("Authorization", $"Bearer {this.accessToken}");
Uri connection = new Uri($"wss://hogehoge");
await clientWebSocket.ConnectAsync(connection, cts.Token);
}
The above is the part of the connect method.
Here is my whole code.
private async Task Connect(ClientWebSocket clientWebSocket)
{
CancellationTokenSource cts = new CancellationTokenSource();
clientWebSocket.Options.SetRequestHeader("Authorization", $"Bearer {this.accessToken}");
Uri connection = new Uri($"wss://hogehoge");
await clientWebSocket.ConnectAsync(connection, cts.Token);
}
static async Task SendAudio(ClientWebSocket ws)
{
ArraySegment<byte> closingMessage = new ArraySegment<byte>(Encoding.UTF8.GetBytes(
"{\"command\": \"recog-break\"}"
));
using (FileStream fs = File.OpenRead("audio.raw"))
{
byte[] b = new byte[3200];
while (fs.Read(b, 0, b.Length) > 0)
{
await ws.SendAsync(new ArraySegment<byte>(b), WebSocketMessageType.Binary, true, CancellationToken.None);
}
await ws.SendAsync(closingMessage, WebSocketMessageType.Text, true, CancellationToken.None);
}
}
// prints results until the connection closes or a delimeterMessage is recieved
private async Task HandleResults(ClientWebSocket ws)
{
var buffer = new byte[60000];
while (true)
{
var segment = new ArraySegment<byte>(buffer);
var result = await ws.ReceiveAsync(segment, CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
return;
}
int count = result.Count;
while (!result.EndOfMessage)
{
if (count >= buffer.Length)
{
await ws.CloseOutputAsync(WebSocketCloseStatus.InvalidPayloadData, "That's too long", CancellationToken.None);
return;
}
segment = new ArraySegment<byte>(buffer, count, buffer.Length - count);
result = await ws.ReceiveAsync(segment, CancellationToken.None);
count += result.Count;
}
var message = Encoding.UTF8.GetString(buffer, 0, count);
this.textBox2.AppendText(message);
}
}
private async void button1_Click_1(object sender, EventArgs e)
{
ClientWebSocket cws = new ClientWebSocket();
await this.Connect(cws);
await Task.WhenAll(SendAudio(cws), HandleResults(cws));
}

C# how to use 'await' to wait for connection for an Asynchronous Client Socket [duplicate]

I've previously used BeginAccept() and BeginRead(), but with Visual Studio 2012 I want to make use of the new asynchronous (async, await) features in my socket server program.
How can I complete the AcceptAsync and ReceiveAsync functions?
using System.Net;
using System.Net.Sockets;
namespace OfficialServer.Core.Server
{
public abstract class CoreServer
{
private const int ListenLength = 500;
private const int ReceiveTimeOut = 30000;
private const int SendTimeOut = 30000;
private readonly Socket _socket;
protected CoreServer(int port, string ip = "0.0.0.0")
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.Bind(new IPEndPoint(IPAddress.Parse(ip), port));
_socket.Listen(ListenLength);
_socket.ReceiveTimeout = ReceiveTimeOut;
_socket.SendTimeout = SendTimeOut;
_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
}
public void Start()
{
}
}
}
...because you're so determined, I put together a very simple example of how to write an echo server to get you on your way. Anything received gets echoed back to the client. The server will stay running for 60s. Try telnetting to it on localhost port 6666. Take time to understand exactly what's going on here.
void Main()
{
CancellationTokenSource cts = new CancellationTokenSource();
TcpListener listener = new TcpListener(IPAddress.Any, 6666);
try
{
listener.Start();
//just fire and forget. We break from the "forgotten" async loops
//in AcceptClientsAsync using a CancellationToken from `cts`
AcceptClientsAsync(listener, cts.Token);
Thread.Sleep(60000); //block here to hold open the server
}
finally
{
cts.Cancel();
listener.Stop();
}
}
async Task AcceptClientsAsync(TcpListener listener, CancellationToken ct)
{
var clientCounter = 0;
while (!ct.IsCancellationRequested)
{
TcpClient client = await listener.AcceptTcpClientAsync()
.ConfigureAwait(false);
clientCounter++;
//once again, just fire and forget, and use the CancellationToken
//to signal to the "forgotten" async invocation.
EchoAsync(client, clientCounter, ct);
}
}
async Task EchoAsync(TcpClient client,
int clientIndex,
CancellationToken ct)
{
Console.WriteLine("New client ({0}) connected", clientIndex);
using (client)
{
var buf = new byte[4096];
var stream = client.GetStream();
while (!ct.IsCancellationRequested)
{
//under some circumstances, it's not possible to detect
//a client disconnecting if there's no data being sent
//so it's a good idea to give them a timeout to ensure that
//we clean them up.
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(15));
var amountReadTask = stream.ReadAsync(buf, 0, buf.Length, ct);
var completedTask = await Task.WhenAny(timeoutTask, amountReadTask)
.ConfigureAwait(false);
if (completedTask == timeoutTask)
{
var msg = Encoding.ASCII.GetBytes("Client timed out");
await stream.WriteAsync(msg, 0, msg.Length);
break;
}
//now we know that the amountTask is complete so
//we can ask for its Result without blocking
var amountRead = amountReadTask.Result;
if (amountRead == 0) break; //end of stream.
await stream.WriteAsync(buf, 0, amountRead, ct)
.ConfigureAwait(false);
}
}
Console.WriteLine("Client ({0}) disconnected", clientIndex);
}
You can use TaskFactory.FromAsync to wrap up Begin / End pairs into async-ready operations.
Stephen Toub has an awaitable Socket on his blog which wraps the more efficient *Async endpoints. I recommend combining this with TPL Dataflow to create a fully async-compatible Socket component.

How to set the timeout for a TcpClient?

I have a TcpClient which I use to send data to a listener on a remote computer. The remote computer will sometimes be on and sometimes off. Because of this, the TcpClient will fail to connect often. I want the TcpClient to timeout after one second, so it doesn't take much time when it can't connect to the remote computer. Currently, I use this code for the TcpClient:
try
{
TcpClient client = new TcpClient("remotehost", this.Port);
client.SendTimeout = 1000;
Byte[] data = System.Text.Encoding.Unicode.GetBytes(this.Message);
NetworkStream stream = client.GetStream();
stream.Write(data, 0, data.Length);
data = new Byte[512];
Int32 bytes = stream.Read(data, 0, data.Length);
this.Response = System.Text.Encoding.Unicode.GetString(data, 0, bytes);
stream.Close();
client.Close();
FireSentEvent(); //Notifies of success
}
catch (Exception ex)
{
FireFailedEvent(ex); //Notifies of failure
}
This works well enough for handling the task. It sends it if it can, and catches the exception if it can't connect to the remote computer. However, when it can't connect, it takes ten to fifteen seconds to throw the exception. I need it to time out in around one second? How would I change the time out time?
Starting with .NET 4.5, TcpClient has a cool ConnectAsync method that we can use like this, so it's now pretty easy:
var client = new TcpClient();
if (!client.ConnectAsync("remotehost", remotePort).Wait(1000))
{
// connection failure
}
You would need to use the async BeginConnect method of TcpClient instead of attempting to connect synchronously, which is what the constructor does. Something like this:
var client = new TcpClient();
var result = client.BeginConnect("remotehost", this.Port, null, null);
var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(1));
if (!success)
{
throw new Exception("Failed to connect.");
}
// we have connected
client.EndConnect(result);
Another alternative using https://stackoverflow.com/a/25684549/3975786:
var timeOut = TimeSpan.FromSeconds(5);
var cancellationCompletionSource = new TaskCompletionSource<bool>();
try
{
using (var cts = new CancellationTokenSource(timeOut))
{
using (var client = new TcpClient())
{
var task = client.ConnectAsync(hostUri, portNumber);
using (cts.Token.Register(() => cancellationCompletionSource.TrySetResult(true)))
{
if (task != await Task.WhenAny(task, cancellationCompletionSource.Task))
{
throw new OperationCanceledException(cts.Token);
}
}
...
}
}
}
catch(OperationCanceledException)
{
...
}
The answers above don't cover how to cleanly deal with a connection that has timed out. Calling TcpClient.EndConnect, closing a connection that succeeds but after the timeout, and disposing of the TcpClient.
It may be overkill but this works for me.
private class State
{
public TcpClient Client { get; set; }
public bool Success { get; set; }
}
public TcpClient Connect(string hostName, int port, int timeout)
{
var client = new TcpClient();
//when the connection completes before the timeout it will cause a race
//we want EndConnect to always treat the connection as successful if it wins
var state = new State { Client = client, Success = true };
IAsyncResult ar = client.BeginConnect(hostName, port, EndConnect, state);
state.Success = ar.AsyncWaitHandle.WaitOne(timeout, false);
if (!state.Success || !client.Connected)
throw new Exception("Failed to connect.");
return client;
}
void EndConnect(IAsyncResult ar)
{
var state = (State)ar.AsyncState;
TcpClient client = state.Client;
try
{
client.EndConnect(ar);
}
catch { }
if (client.Connected && state.Success)
return;
client.Close();
}
One thing to take note of is that it is possible for the BeginConnect call to fail before the timeout expires. This may happen if you are attempting a local connection. Here's a modified version of Jon's code...
var client = new TcpClient();
var result = client.BeginConnect("remotehost", Port, null, null);
result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(1));
if (!client.Connected)
{
throw new Exception("Failed to connect.");
}
// we have connected
client.EndConnect(result);
Here is a code improvement based on mcandal solution.
Added exception catching for any exception generated from the client.ConnectAsync task (e.g: SocketException when server is unreachable)
var timeOut = TimeSpan.FromSeconds(5);
var cancellationCompletionSource = new TaskCompletionSource<bool>();
try
{
using (var cts = new CancellationTokenSource(timeOut))
{
using (var client = new TcpClient())
{
var task = client.ConnectAsync(hostUri, portNumber);
using (cts.Token.Register(() => cancellationCompletionSource.TrySetResult(true)))
{
if (task != await Task.WhenAny(task, cancellationCompletionSource.Task))
{
throw new OperationCanceledException(cts.Token);
}
// throw exception inside 'task' (if any)
if (task.Exception?.InnerException != null)
{
throw task.Exception.InnerException;
}
}
...
}
}
}
catch (OperationCanceledException operationCanceledEx)
{
// connection timeout
...
}
catch (SocketException socketEx)
{
...
}
catch (Exception ex)
{
...
}
As Simon Mourier mentioned, it's possible to use ConnectAsync TcpClient's method with Task in addition and stop operation as soon as possible.
For example:
// ...
client = new TcpClient(); // Initialization of TcpClient
CancellationToken ct = new CancellationToken(); // Required for "*.Task()" method
if (client.ConnectAsync(this.ip, this.port).Wait(1000, ct)) // Connect with timeout of 1 second
{
// ... transfer
if (client != null) {
client.Close(); // Close the connection and dispose a TcpClient object
Console.WriteLine("Success");
ct.ThrowIfCancellationRequested(); // Stop asynchronous operation after successull connection(...and transfer(in needed))
}
}
else
{
Console.WriteLine("Connetion timed out");
}
// ...
Also, I would recommended checking out AsyncTcpClient C# library with some examples provided like Server <> Client.
If using async & await and desire to use a time out without blocking, then an alternative and simpler approach from the answer provide by mcandal is to execute the connect on a background thread and await the result. For example:
Task<bool> t = Task.Run(() => client.ConnectAsync(ipAddr, port).Wait(1000));
await t;
if (!t.Result)
{
Console.WriteLine("Connect timed out");
return; // Set/return an error code or throw here.
}
// Successful Connection - if we get to here.
See the Task.Wait MSDN article for more info and other examples.
I am using these generic methods; they can add timeout and cancellation tokens for any async task. Let me know if you see any problem so I can fix it accordingly.
public static async Task<T> RunTask<T>(Task<T> task, int timeout = 0, CancellationToken cancellationToken = default)
{
await RunTask((Task)task, timeout, cancellationToken);
return await task;
}
public static async Task RunTask(Task task, int timeout = 0, CancellationToken cancellationToken = default)
{
if (timeout == 0) timeout = -1;
var timeoutTask = Task.Delay(timeout, cancellationToken);
await Task.WhenAny(task, timeoutTask);
cancellationToken.ThrowIfCancellationRequested();
if (timeoutTask.IsCompleted)
throw new TimeoutException();
await task;
}
Usage
await RunTask(tcpClient.ConnectAsync("yourhost.com", 443), timeout: 1000);

Categories