Currently I try to read and write Async to/from a network stream. My software is the Client part and the server can send informations on its own or respond to commands I send him.
So I need a socket which
reads all the time (in case the server sends status informations)
stops reading when I want to send commands (commands can be sequences of data with multible Write and Read operations)
So I thought it would be a good approach to create a Semaphore and a Background Task which handles the server sent messages and in case I want to send a command I block the semaphore and have full access to read/write operations to the socket.
Here is what I do currently.
private TcpClient _tcpClient = new TcpClient();
protected SemaphoreSlim ClientSemaphore { get; } = new SemaphoreSlim(1, 1);
public async Task ConnectAsync()
{
if (_tcpClient.Connected)
{
await DisconnectAsync();
}
await _tcpClient.ConnectAsync(Hostname, RemotePort);
//here the background Task is started
_ = AutoReceiveMessages();
}
private async Task AutoReceiveMessages()
{
while (_tcpClient.Connected)
{
//enter and lock semaphore
await ClientSemaphore.WaitAsync();
try
{
//read from socket until timeout (ms)
var msg = await ReadFromSocket(2000);
foreach (var cmd in SplitMessageInTelegrams(msg))
{
Console.WriteLine("MESSAGE --> " + cmd);
}
}
catch (Exception ex)
{
}
finally
{
//release semaphore
ClientSemaphore.Release();
}
}
}
private async Task<string> ReadFromSocket(double timeout = 0)
{
var buf = new byte[4096];
var stream = _tcpClient.GetStream();
//read from stream or timeout
var amountReadTask = stream.ReadAsync(buf, 0, buf.Length);
var timeoutTask = Task.Delay(TimeSpan.FromMilliseconds(timeout));
await Task.WhenAny(timeoutTask, amountReadTask)
.ConfigureAwait(false);
//timeout
if (!amountReadTask.IsCompleted)
{
throw new TimeoutException("Timeout");
}
//no timeout
return Encoding.ASCII.GetString(buf, 0, amountReadTask.Result);
}
But this do not work as I expected...
I use this methode to send a message to the server and in WireShark I see the server resonds with the same message
protected async Task SendTelegramAsync(ITelegram telegram)
{
await ClientSemaphore.WaitAsync();
try
{
_ = telegram ?? throw new ArgumentException($"{nameof(telegram)}");
if (!_tcpClient.Connected) throw new InvalidOperationException("Socket not connected!");
var buf = new byte[4096];
var stream = _tcpClient.GetStream();
var msg = Encoding.ASCII.GetBytes("\x02" + telegram.GetCommandMessage() + "\x03");
Console.WriteLine("WRITE --> " + msg);
await stream.WriteAsync(msg, 0, msg.Length);
//comment AutoReceiveMessage and remove comment from this
//and I get responses from the server
//var test = await ReadFromSocket(2000);
}
finally
{
ClientSemaphore.Release();
}
}
I know in this case I do not need the semaphore but later I want to create sequences so one command consists of multible writes and reads and as long as the command is executed I do not want to use the AutoReceiveMessages method.
The problem now is
If I use it like this I never get a response the ReadFromSocket method always get the timeout even when wireshark tell me the server has responded
But even better if I disable AutoReceiveMessages (just comment _ = AutoReceiveMessages()) and use ReadFromSocket directly in SendTelegramAsync() everything work as expected.
So I think the problem is something related to the background task and the ReadAsync but I couldnt figure it out...
Got It!
stream.DataAvailable is your friend (or my friend :)).
If I check before the ReadAsync if DataIsAvailable then I have no problem anymore.
if (_tcpClient.GetStream().DataAvailable)
var msg = await ReadFromSocket(DEFAULT_TIMEOUT);
Related
I was trying to accept some data from client, send it to the server and also send feedback from server to the client again. When server only gets some data, everething works fine. While trying to implement two-way communication, server just hungs.
Server's code:
var tcpListener = new TcpListener(IPAddress.Any, 8888);
try
{
tcpListener.Start();
Console.WriteLine("Server is currently run, waiting for incoming connections");
while (true)
{
using var tcpClient = await tcpListener.AcceptTcpClientAsync();
await using var stream = tcpClient.GetStream();
var incomeData = await DeserializeHelper.ConverteToBytes(stream);
var incomePerson = MessagePackSerializer.Deserialize<Person>(incomeData);
Console.WriteLine($"Income message from {incomePerson.Name}: '{incomePerson.Message}'");
var responseMessage = $"Thank you, {incomePerson.Name} for the message";
var responceData = MessagePackSerializer.Serialize(responseMessage);
await stream.WriteAsync(responceData);
}
}
finally
{
tcpListener.Stop();
}
Client's code:
using TcpClient tcpClient = new TcpClient();
await tcpClient.ConnectAsync("127.1.199.250",8888);
Console.WriteLine("Hello, client!");
try
{
var stream = tcpClient.GetStream();
Console.WriteLine("Enter your name");
var name = Console.ReadLine();
Console.WriteLine("Enter message");
var message = Console.ReadLine();
Person tom = new Person(name, message);
var sentData = MessagePackSerializer.Serialize(tom);
await stream.WriteAsync(sentData);
Console.WriteLine("Message was sent to the server");
var incomeResponce = await DeserializeHelper.ConverteToBytes(stream);
var incomeResponceMessage = MessagePackSerializer.Deserialize<string>(incomeResponce);
Console.WriteLine($"Server's message: {incomeResponce}");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Also all of them using my own static method ConverteToBytes, here it's code:
public static async Task<byte[]> ConverteToBytes(NetworkStream stream)
{
await using var ms = new MemoryStream();
byte[] bytesData = null;
int count = 0;
do
{
byte[] buffer = new byte[1024];
count = await stream.ReadAsync(buffer,0,1024);
ms.Write(buffer,0,count);
} while (stream.CanRead && count > 0);
return bytesData = ms.ToArray();
}
I guess the problem is with not correct stream using, but don't know what exactly is wrong. Thank's everyone!
I strongly recommend not using TCP/IP in the first place. If possible, use a well-established protocol with good library support, e.g., HTTP or WebSockets. Writing a correct TCP/IP application is extremely difficult.
If you must use TCP/IP, then I recommend watching my video series on the subject and reading my TCP/IP .NET Sockets FAQ.
Of particular note is that network streams are streams of bytes, not streams of messages. So there's no built-in way to know when a message completes. So you'll have to add that to your protocol yourself, i.e., using message framing. I describe message framing on my blog, and the video series linked above shows a more modern way to do it.
Without message framing, your server is likely just waiting for more bytes to arrive from the client connection.
I'm building a TCP/IP connection for my application with a warehouse system. The communication goes like this.
I send a message to the TCP/IP(Socket) server of the warehouse system.
The warehouse system responds with a message the my local TCP/IP server.
So there are no direct response messages. Instead each application as it's own server.
Yet I want my application to wait for the response coming from the other server.
So basicly I have the following code.
public string ControllerFunction() {
startLocalTcpIpServer();
sendMessage("a message");
return clientMessage;
}
This is my own server started with the start() function
public void Start() {
// Start TcpServer background thread
tcpListenerThread = new Thread(new ThreadStart(ListenForIncommingRequests)) {
IsBackground = true
};
tcpListenerThread.Start();
}
private void ListenForIncommingRequests() {
try {
tcpListener = new TcpListener(IPAddress.Parse(serverIp), port);
tcpListener.Start();
byte[] bytes = new byte[1024];
Console.WriteLine("Server Started");
while(true) {
// Get a stream object for reading
using(NetworkStream stream = tcpListener.AcceptTcpClient().GetStream()) {
int length;
// Read incomming stream into byte arrary.
while((length = stream.Read(bytes, 0, bytes.Length)) != 0) {
byte[] incommingData = new byte[length];
Array.Copy(bytes, 0, incommingData, 0, length);
// Convert byte array to string message.
string clientMessage = Encoding.ASCII.GetString(incommingData);
}
}
}
}
catch(SocketException socketException) {
Console.WriteLine("SocketException " + socketException.ToString());
}
}
So I want to use the result string clientMessage again as a return for my ControllerFunction. But how do I get the data there in a proper way?
So what you need is to be able to wait for response coming from another place in your application (local server). Response will be fired there first. Local server should have an event you can subscribe to (OnMessage in my example). This event will forward result message to you.
Synchronization can be handled using TaskCompletionSource. You will create Task that you can use to obtain result synchronously or asynchronously.
Something like this:
public string ControllerFunction()
{
return ControllerFunctionTask().Result;
}
public Task<string> ControllerFunctionTask()
{
sendMessage("a message");
var task = new TaskCompletionSource<string>();
localServer.OnMessage = (message) =>
{
task.SetResult(message);
};
return task.Task;
}
As stated in comments, synchronous waiting for asynchronous Task may lead to deadlocks. This may happen when caller thread is context thread (UI, ASP). Therefore this should be better approach:
public async Task<string> ControllerFunction()
{
return await ControllerFunctionTask();
}
public Task<string> ControllerFunctionTask()
{
sendMessage("a message");
var task = new TaskCompletionSource<string>();
localServer.OnMessage = (message) =>
{
task.SetResult(message);
};
return task.Task;
}
OnMessage can be defined this way:
public event Action<string> OnMessage;
Then it will be called right after line where you get clientMessage string:
string clientMessage = Encoding.ASCII.GetString(incommingData);
if (OnMessage != null)
OnMessage(clientMessage);
I've been making a server. I am using TcpListener.AcceptTcpClientAsync() in an async method, but I have no idea how to actually make it work. My code right now is:
private static async void StartServer()
{
Console.WriteLine("S: Server started on port {0}", WebVars.ServerPort);
var listener = new TcpListener(WebVars.LocalIp, WebVars.ServerPort);
listener.Start();
var client = await listener.AcceptTcpClientAsync();
}
How do I process the client? Do I just continue coding and it will automagically make new threads of the same method or do I need to do some magic method that will do it for me?
Edit: current code:
private static Task HandleClientAsync(TcpClient client)
{
var stream = client.GetStream();
// do stuff
}
/// <summary>
/// Method to be used on seperate thread.
/// </summary>
private static async void RunServerAsync()
{
while (true)
{
Console.WriteLine("S: Server started on port {0}", WebVars.ServerPort);
var listener = new TcpListener(WebVars.LocalIp, WebVars.ServerPort);
listener.Start();
var client = await listener.AcceptTcpClientAsync();
await HandleClientAsync(client);
}
}
// all credit should go to c# 7.0 in a nutshell (Joseph Albahari & Ben Albahari)
async void RunServerAsync()
{
var listner = new TcpListener(IPAddress.Any, 9999);
listner.Start();
try
{
while (true)
await Accept(await listner.AcceptTcpClientAsync());
}
finally { listner.Stop(); }
}
const int packet_length = 2; // user defined packet length
async Task Accept(TcpClient client)
{
await Task.Yield();
try
{
using(client)
using(NetworkStream n = client.GetStream())
{
byte[] data = new byte[packet_length];
int bytesRead = 0;
int chunkSize = 1;
while (bytesRead < data.Length && chunkSize > 0)
bytesRead += chunkSize =
await n.ReadAsync(data, bytesRead, data.Length - bytesRead);
// get data
string str = Encoding.Default.GetString(data);
Console.WriteLine("[server] received : {0}", str);
// To do
// ...
// send the result to client
string send_str = "server_send_test";
byte[] send_data = Encoding.ASCII.GetBytes(send_str);
await n.WriteAsync(send_data, 0, send_data.Length);
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Nothing will magically create dedicated threads for you, although there are some threads used for IO completion which can come into play, particularly if you don't have a synchronization context that you need to return to.
You should decide whether you want your StartServer method to actually complete when it's accepted a single connection, or keep looping until you've been told to shut down.
Either way, you clearly need to decide how to handle the client too. Either you could start a new thread and use synchronous methods, or you could just use asynchronous IO to handle everything in the same thread. For example, to dump the incoming data to a file:
private Task HandleClientAsync(TcpClient client)
{
// Note: this uses a *synchronous* call to create the file; not ideal.
using (var output = File.Create("client.data"))
{
using (var input = client.GetStream())
{
// Could use CopyToAsync... this is just demo code really.
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = await input.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await output.WriteAsync(buffer, 0, bytesRead);
}
}
}
}
(That's assuming the client will just terminate the connection when it's finished writing the data.) Aside from the File.Create call, this is all asynchronous - so there's no need to create a separate thread for it.
This is just an example, of course - real connection handling would usually be more complicated. If your real handling needs anything more compute-intensive, you may well want to consider using Task.Run to use the thread pool... that way it won't interfere with accepting more connections.
Im building a simple TCP client and server as a basis for my networking project. Im planning to use the async await technique for future proof and scaleable server.
If I put wrong ip address, the client cant connect to my server and throw an exception. I can catch the exception using try/catch but is that the recommended way to do?
What do you guys think of the implementation. Any comments for me to improve?
My server
private void startServer_Click(object sender, RoutedEventArgs e)
{
if (anyIP.IsChecked == true)
{
listener = new TcpListener(IPAddress.Any, Int32.Parse(serverPort.Text));
Logger.Info("Ip Address : " + IPAddress.Any + " Port : " + serverPort.Text);
}
else
{
listener = new TcpListener(IPAddress.Parse(serverIP.Text), Int32.Parse(serverPort.Text));
Logger.Info("Ip Address : " + serverIP.Text + " Port : " + serverPort.Text);
}
try
{
listener.Start();
Logger.Info("Listening");
HandleConnectionAsync(listener, cts.Token);
}
//finally
//{
//cts.Cancel();
//listener.Stop();
//Logger.Info("Stop listening");
//}
//cts.Cancel();
}
async Task HandleConnectionAsync(TcpListener listener, CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
Logger.Info("Accepting client");
//TcpClient client = await listener.AcceptTcpClientAsync();
TcpClient client = await listener.AcceptTcpClientAsync();
Logger.Info("Client accepted");
EchoAsync(client, ct);
}
}
async Task EchoAsync(TcpClient client, CancellationToken ct)
{
var buf = new byte[4096];
var stream = client.GetStream();
while (!ct.IsCancellationRequested)
{
var amountRead = await stream.ReadAsync(buf, 0, buf.Length, ct);
Logger.Info("Receive " + stream.ToString());
if (amountRead == 0) break; //end of stream.
await stream.WriteAsync(buf, 0, amountRead, ct);
Logger.Info("Echo to client");
}
}
private void stopServer_Click(object sender, RoutedEventArgs e)
{
cts.Cancel();
listener.Stop();
Logger.Info("Stop listening");
}
My client
private void connect_Click(object sender, System.Windows.RoutedEventArgs e)
{
IPAddress ipAddress;
int port;
//TODO Check if ip address is valid
ipAddress = IPAddress.Parse(serverIP.Text);
//TODO port range is 0-65000
port = int.Parse(serverPort.Text);
StartClient(ipAddress, port);
}
private static async void StartClient(IPAddress serverIpAddress, int port)
{
var client = new TcpClient();
//can i try/catch to catch await exception?
try
{
await client.ConnectAsync(serverIpAddress, port);
}
catch (Exception e)
{
Logger.Info(e);
}
Logger.Info("Connected to server");
using (var networkStream = client.GetStream())
using (var writer = new StreamWriter(networkStream))
using (var reader = new StreamReader(networkStream))
{
writer.AutoFlush = true;
for (int i = 0; i < 10; i++)
{
Logger.Info("Writing to server");
await writer.WriteLineAsync(DateTime.Now.ToLongDateString());
Logger.Info("Reading from server");
var dataFromServer = await reader.ReadLineAsync();
if (!string.IsNullOrEmpty(dataFromServer))
{
Logger.Info(dataFromServer);
}
}
}
if (client != null)
{
client.Close();
Logger.Info("Connection closed");
}
}
I have a .NET TCP/IP FAQ that I recommend to get some of the basics down.
After just a brief look at your code, these points stood out to me:
Both your client and server have times when they're only reading (not writing). This means you're subject to the half-open scenario (as I describe in my FAQ). A robust server should be writing periodically even if it has nothing to say.
Both your client and server have times when they're only writing (not reading). This means that you're subject to a deadlock (as I describe in my FAQ) if the other end is not behaving well (e.g., sending lots of data). However, you can't just read indefinitely or you'll open yourself up to a DoS; so you should decide where your limit is and establish read buffer sizes (and write timeouts) that make sense for your application.
Using ReadLineAsync leaves you open to a trivial DoS attack, since you can't specify the maximum allowed size of the line.
Your code must be prepared for an exception at any time (as I describe in my FAQ). Obviously, ReadAsync and WriteAsync may throw. What's less obvious is that any socket method may throw, including AcceptTcpClientAsync.
Your code uses a mixture of exception handling types. The async Task methods are never awaited, so exceptions there just silently end that method. The StartClient method is more problematic, since it is async void. You'll need to think through your application needs for error detection and retry strategies, and apply proper handling at every level.
In conclusion, I reiterate my comment: I strongly recommend just self-hosting SignalR. Sockets should only be used if you have no choice.
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);