I'm using .NET Core and want to send messages via TCP. For this I'm using the TcpClient class and created a custom service. This solution works for now, not sure if I can improve it
class MyTcpService : IMyTcpService
{
private readonly TcpClient tcpClient = new TcpClient();
public async Task Send(byte[] bytesToSend)
{
if (!tcpClient.Connected) // Check if client was closed before
{
await tcpClient.ConnectAsync("127.0.0.1", 5000); // Read values from config
}
NetworkStream networkStream = tcpClient.GetStream();
// Send the message
await networkStream.WriteAsync(bytesToSend, 0, bytesToSend.Length);
// Read the response
byte[] responseBuffer = new byte[1024]; // Read value from config
int amountOfResponseBytes = await networkStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
string responseMessage = Encoding.ASCII.GetString(responseBuffer, 0, amountOfResponseBytes);
// Close the connection with a timeout if true
if (true) // Read value from config
{
networkStream.Close(1000); // Read value from config
tcpClient.Close();
}
// Handle the response message here
// ...
}
}
I want to inject IMyTcpService as a transient service. I would like to know how to close the client with a timeout? The Socket class has a Close method accepting a timeout parameter
https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.close?view=netcore-3.1#System_Net_Sockets_Socket_Close_System_Int32_
but I'm not able to find an equivalent for the TcpClient just for its NetworkStream.
At the moment you are awaiting both the WriteAsync and ReadAsync calls. Because of this the timeout in your call to networkStream.Close(1000) should have no impact and the connection will always close immediately as no data is waiting to be sent/received. For neither the write or read you have specified a timeout, which means they won't return until data has finished being transferred.
I would like to know how to close the client with a timeout?
It's not clear why you want this or what you want to achieve with this. TcpClient is simply a wrapper around a NetworkStream which in turn wraps around a Socket. So handling timeouts both on the TcpClient and the NetworkStream doesn't make much sense.
Resource management:
In your current example I would first of all advise you to keep the TcpClient inside the Send method instead of a class field. If you don't need to use the TcpClient in other places (which expect you don't since you are closing it in the Send function) you should narrow it's scope for easier resource management. While doing that I'd suggest you make use of the using statement to avoid forgetting to properly dispose your resources. This applies to all types that implement the IDisposable interface (of course there are exceptions to this).
Handling timeout:
To handle timeouts in this snippet of code you have shared I suggest you configure the timeout on the write and read operations rather than the close operation, since your code is very sequential. An example of what that could look like:
class MyTcpService : IMyTcpService
{
public async Task Send(byte[] bytesToSend)
{
string responseMessage;
using (tcpClient = new TcpClient())
{
if (shouldUseTimeout) // From config
{
tcpClient.ReceiveTimeout = 1000; // From config
tcpClient.SendTimeout = 1000; // From config
}
await tcpClient.ConnectAsync("127.0.0.1", 5000); // Read values from config
NetworkStream networkStream = tcpClient.GetStream();
// Send the message
await networkStream.WriteAsync(bytesToSend, 0, bytesToSend.Length);
// Read the response
byte[] responseBuffer = new byte[1024]; // Read value from config
int amountOfResponseBytes = await networkStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
responseMessage = Encoding.ASCII.GetString(responseBuffer, 0, amountOfResponseBytes);
}
// The tcpClient is now properly closed and disposed of
// Handle the response message here
// responseMessage...
}
}
Update in response to the comment 13/10/2020:
hey, thanks for your reply :) I tried to improve your answer, what do you think about this snippet? pastebin.com/7kTvtTv2
From your https://pastebin.com/7kTvtTv2:
public async Task<string> Send(byte[] messageToSend)
{
string responseMessage;
using (TcpClient tcpClient = new TcpClient())
{
await tcpClient.ConnectAsync("127.0.0.1", 5000); // From config
NetworkStream networkStream = tcpClient.GetStream();
await networkStream.WriteAsync(messageToSend, 0, messageToSend.Length);
await networkStream.FlushAsync();
tcpClient.Client.Shutdown(SocketShutdown.Send); // shutdown gracefully
byte[] responseBuffer = new byte[256]; // This can be of any size
StringBuilder stringBuilder = new StringBuilder();
int amountOfResponseBytes;
do
{
amountOfResponseBytes = await networkStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
string responseData = Encoding.ASCII.GetString(responseBuffer, 0, amountOfResponseBytes);
stringBuilder.Append(responseData);
} while (amountOfResponseBytes > 0);
responseMessage = stringBuilder.ToString();
}
return responseMessage;
}
Looks pretty good to me. Only some minor comments:
await networkStream.FlushAsync() - it seems like this should be unnecessary when I look at the remarks for the Flush method but I haven't tested it:
The Flush method implements the Stream.Flush method; however, because NetworkStream is not buffered, it has no effect on network streams. Calling the Flush method does not throw an exception
tcpClient.Client.Shutdown(SocketShutdown.Send) - this method simply tells the Socket to no longer allow writing/sending data. Since your TcpClient only stays within the Send method and therefore not being shared anywhere it seems a little unnecessary too. I think the Shutdown method is mostly relevant if you don't have complete control over when the Socket is used.
do { ... } while (...) - looks good to me. Just remember the responseBuffer need to be a multiple of 8 if you are dealing with ASCII characters so you don't end up trying to decode a partial character.
Where did the timeout handling go? Did you forget to add timeout handling or is it not relevant anymore? Currently, if you have a lot of data to send or receive or the network is just slow, the WriteAsync and ReadAsync calls may potentially take a long time.
Related
I am working on client-server data receiving. I am able to receive data from the server which is my meter. The code is below
listner = new TcpListener(new IPEndPoint(IPAddress.Any, port));
Console.WriteLine("Listening...");
listner.Start();
while (true)
{
try
{
//---incoming client connected---
TcpClient client = listner.AcceptTcpClient();
//---get the incoming data through a network stream---
NetworkStream nwStream = client.GetStream();
byte[] buffer = new byte[client.ReceiveBufferSize];
//---read incoming stream---
int bytesRead = nwStream.Read(buffer, 0, client.ReceiveBufferSize);
//---convert the data received into a string---
string dataReceived = BitConverter.ToString(buffer, 0, bytesRead);
//Encoding.ASCII.GetString(buffer, 0, bytesRead);
//Console.WriteLine("Received : " + dataReceived);
//MessageBox.Show("Data Received", dataReceived);
//---write back the text to the client---
//Console.WriteLine("Sending back : " + dataReceived);
//nwStream.Write(buffer, 0, bytesRead);
client.Close();
}
catch (Exception ex)
{
}
Using the above code I am able to receive the data. But I want to perform the following
Keeping listening on the port
While listening I want to send some bytes to the server (meter)
Get a response from the server after sending some data.
I know there are lots of client-server tutorials but they have a separate implementation of server and client. I just want to handle the client(my software). I have also tried with Asynchronous Server Socket Example.
Any help would be highly appreciated.
Your server needs to continually listen for connections from clients. Each time it gets a connection you get from the OS a new TcpClient to handle that connection. the TcpClient that you get from the OS is sometimes called a Child TcpClient because it's created by the listening port. But it's functionally the same as any other socket.
This is very easy to achieve with the async/await pattern.
First you need a listener that waits for connections, everytime it gets a connection it passes this off to another task that processes the child TcpClient. e.g.,
static async Task StartTcpServerAsync()
{
var tcpListener = new TcpListener(new IPEndPoint(IPAddress.Any, 9999));
tcpListener.Start();
while (true)
{
var tcpClient = await tcpListener.AcceptTcpClientAsync();
// Fire and forget the Child connection
_ = StartChildTcpClientAsync(tcpClient);
}
}
In the above boiler plate code we're completely forgetting about the ChildTcpClient task. You might or might not want this. If you make the task completely self sufficient (as this demo is) then the main task may never need to know if it finished or not. But if you need to be able to provide feedback between the ChildTcpClient Task and the main task then you'll need to provide some additional code to manage this and you'd start by storing the Task returned from StartChildTcpClientAsync(tcpClient); somewhere so you can observe it. You could use Task.Run() here, but it is not necessary in a Console application.
Now that you've got your listener Task, you need a ChildTcpClient Task, eg:
static async Task StartChildTcpClientAsync(TcpClient tcpClient)
{
Console.WriteLine($"Connection received from: {tcpClient.Client.RemoteEndPoint.ToString()}");
using var stream = tcpClient.GetStream();
var buffer = new byte[1024];
while (true)
{
var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
await stream.WriteAsync(buffer, 0, bytesRead);
}
}
That's a super simple Tcp echo client. It's got no flow control and no error handling the task will just sit there forever waiting for the client to send some data even after the client has disconnected. This Task just "awaits" for infinity (or until an exception occurs) so you need to add some code in here to manage the client, check that the client is still connected, etc etc
To pull it all together you simply need a main like this:
static async Task Main(string[] args)
{
await StartTcpServerAsync();
// Will never exit unless tcpListener.AcceptTcpClientAsync();
// throws an exception
}
And that's it, you have a Console Application that waits for an infinite number of clients to connect, and each time one does it has it's own Task to deal with the IO.
I have a server application but am getting some intermittent timeouts. I want to start writing back to the client immediately and then defer the rest of my write until my payload is ready. However, nothing I've seen in TcpClient or NetworkStream supports active streaming. In my own test bed, any write to the stream is in isolation, even the Begin/End methods.
For example, in my client side write/read loop, the following server code only sends back a single time entry instead of two.
static void Main(string[] args)
{
var listener = TcpListener.Create(2345);
listener.Start();
while (true)
{
var client = listener.AcceptTcpClientAsync().Result;
ThreadPool.QueueUserWorkItem(async state =>
{
try
{
await HandleClient(state);
}
catch (SocketException error) when (error.Message.Contains("existing connection was forcibly closed"))
{
// do nothing
}
}, client);
}
}
private static async Task HandleClient(object state)
{
var client = (TcpClient) state;
var stream = client.GetStream();
while (true)
{
var readBuffer = new byte[4096];
var requestSize = await stream.ReadAsync(readBuffer, 0, readBuffer.Length);
var requestString = Encoding.ASCII.GetString(readBuffer, 0, requestSize);
Console.WriteLine($"[{DateTime.Now.ToLongTimeString()}] Received: {requestString}");
if (requestString == "quit")
{
break;
}
var responseString = DateTime.Now.ToLongTimeString();
var writeBuffer = Encoding.ASCII.GetBytes(responseString);
stream.BeginWrite(writeBuffer, 0, writeBuffer.Length, DelayedWriteCallback, stream);
}
client.Close();
client.Dispose();
}
private static void DelayedWriteCallback(IAsyncResult ar)
{
var stream = (NetworkStream) ar.AsyncState;
Thread.Sleep(5000);
var writeString = Encoding.ASCII.GetBytes(DateTime.Now.ToLongTimeString());
stream.Write(writeString, 0, writeString.Length);
stream.Flush();
stream.EndWrite(ar);
}
How do I do this?
TCP is a streaming protocol, which just means that it will transport any data you send through it and make sure it is received in the correct order on the other side.
That data might be a constant series of bytes, or many seconds of nothing followed by a single byte. TCP does not care, it´s just the pipe for.
This is made clear by the fact that every socket Send and Receive method takes a buffer with a specific size as a parameter (same goes for NetworkStream). The "streaming" of useful data is up to the application, as is the reception and interpretation of the data.
If you want to send data continuously, you need a send() loop that will pump bytes into the socket as soon as your server has them. If you need to read data continuously, you need a loop that will receive() chunks of bytes continuously and make sense of them.
I am new to asynchronous socket programming, and I am having problems with my asynchronous functions.
I am trying to create a chat program that uses Windows Forms for the client, and a console application for the server.
Here is the code for handling connections on my server:
public async void StartServer()
{
TcpListener listener = new TcpListener(_ip, _port);
listener.Start();
Console.WriteLine("Server is running on IP: {0} Port: {1}", _ip.ToString(), _port);
while (true)
{
try
{
TcpClient client = await listener.AcceptTcpClientAsync();
HandleConnections(client);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
private async void HandleConnections(TcpClient client)
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[256];
string message = null;
int x;
while(stream.DataAvailable)
{
x = await stream.ReadAsync(buffer, 0, buffer.Length);
message += Encoding.ASCII.GetString(buffer);
}
message = message.Replace('\0', ' ');
message = message.Trim();
Console.WriteLine("Message Recieved: " + message);
byte[] bytes = Encoding.ASCII.GetBytes(message);
await stream.WriteAsync(bytes, 0, bytes.Length);
stream.Close();
}
And here is the code for the client program connecting to the server:
private async void ConnectButton_Click(object sender, EventArgs e)
{
IPAddress address = IPAddress.Parse(IPInput.Text);
client = new TcpClient();
await client.ConnectAsync(address, 12345);
NetworkStream stream = client.GetStream();
string message = UsernameInput.Text + " Connected!";
Task<int> sendTask = SendMessage(stream, message);
int sendComp = await sendTask;
Task<string> recieveTask = RecieveMessage(stream);
string recieved = await recieveTask;
stream.Close();
ChatText.AppendText(recieved);
}
private async Task<int> SendMessage(NetworkStream stream, string message)
{
byte[] bytes = Encoding.ASCII.GetBytes(message + "\r\n");
await stream.WriteAsync(bytes, 0, bytes.Length);
return 1;
}
private async Task<string> RecieveMessage(NetworkStream stream)
{
byte[] buffer = new byte[256];
string message = null;
int x;
while (stream.DataAvailable)
{
x = await stream.ReadAsync(buffer, 0, buffer.Length);
message += Encoding.ASCII.GetString(buffer);
}
return message;
}
The first problem that I am having is when I run the client program and click the ConnectButton, the message gets sent to the server program which outputs Message Recieved: user Connected!, but then the client program encounters a null reference exception on the line ChatText.AppendText(recieved); saying that the recieved variable is null. It seems that the line string recieved = await recieveTask; is not waiting for the task to finish executing, and it jumps to the next line without assigning a value to recieved. If I put a breakpoint at the top of the private async Task<string> RecieveMessage(NetworkStream stream) function and step through it, then the recieved variable gets it's value and the code will complete successfully, but without the breakpoint I get the null reference exception.
The next issue that I am having, is if I leave the server running and open the client again and try connecting, the server gets a null reference exception on the line message = message.Replace('\0', ' ');. The first time I run with the client, the server receives the message successfully, but the second time, it doesn't get any data from the stream and leaves the variable null, resulting in a null reference exception.
I apologize if my code is garbage, I have been reading the MSDN documentation for hours and am unable to come up with a solution, and I feel like I am doing this completely wrong. So my questions are as follows:
What is causing these errors that I am encountering? And am I approaching this problem the right way?
Both of your issues are not related to asynchronous functions, and actually both issues are because of the same problem:
while (stream.DataAvailable)
{
// read stream here
}
If data is not yet available to read from the stream - both of your ReceiveMessage and HandleConnections functions just skip reading stream at all. What you should do instead (in your case) is:
do
{
// read your stream here
} while (stream.DataAvailable);
Then first Read (or ReadAsync) will wait until first data chunk arrives, and only after first chunk will check if more data is already available.
Also note that you use large buffer (256 bytes) while client\server send short messages (like "Client received: xxx"), which means most of the buffer is empty and when you convert it to string via Encoding.ASCII.GetString - you get a lot of whitespace at the end ("Client received: xxx ... ").
It doesn't look like a problem with async/await so much as an issue with your TCP streams.
You don't appear to be actually waiting for a response. SendMessage writes the server, then RecieveMessage expects a response to already be in the stream.
If stream.DataAvailable is false when you hit the while loop for the first time, message will remain null.
You need some way to wait for there to be data in the stream before you attempt to read from it.
I am trying to read/write bytes to/from a Bluetooth printer using Xamarin Android in C#. I am making use of System.IO.Stream to do this. Unfortunately, whenever I try to use ReadTimeout and WriteTimeout on those streams I get the following error:
Message = "Timeouts are not supported on this stream."
I don't want my Stream.Read() and Stream.Write() calls to block indefinitely. How can I solve this?
You probably would like to expose an method with cancellation token so your api can be easliy consumed.
One of the CancellationTokenSource constructors takes TimeSpan as a parameter. CancellationToken on other hand exposes Register method which allows you close the stream and the reading operation should stop with an exception being thrown.
Method call
var timeout = TimeSpan.Parse("00:01:00");
var cancellationTokenSource = new CancellationTokenSource(timeout);
var cancellationToken = cancellationTokenSource.Token;
await ReadAsync(stream, cancellationToken);
Method implementation
public async Task ReadAsync(Stream stream, CancellationToken cancellationToken)
{
using (cancellationToken.Register(stream.Dispose))
{
var buffer = new byte[1024];
var read = 0;
while ((read = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
// do stuff with read data
}
}
}
The following code will dispose stream only if it times out
More to can be found here.
Edit:
Changed .Close() to .Dispose() since it is no longer available in some PCLs .Close() vs .Dispose()
You must do the reads on another thread; in this case if you must stop reading you can close the stream from other thread and the read will finish with an exception.
Another easy way is to use a System.Threading.Timer to dispose the stream:
Stream str = //...
Timer tmr = new Timer((o) => str.Close());
tmr.Change(yourTimeout, Timeout.Infinite);
byte[] data = new byte(1024);
bool success = true;
try{ str.Read(data, 0, 1024); }
catch{ success = false, }
finally{ tmr.Change(Timeout.Inifinite, Timeout.Infinite); }
if(success)
//read ok
else
//read timeout
When running this code:
private async void StartChat(Object obj)
{
TcpClient me = (TcpClient)obj;
UpdateChatBox("Attempting read from server.");
myBuffer = new byte[BUFFER_SIZE];
while (true)
{
var myStream = me.GetStream();
myStream.BeginRead(myBuffer, 0, BUFFER_SIZE, new AsyncCallback(UpdateChatBoxAsync), myStream);
if (messageToSend)
{
await myStream.WriteAsync(myMessage, 0, myMessage.Length);
}
}
}
I am receiving the following IO Exception from BeginRead:
Unable to read data from the transport connection: An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full.
Here is the callback method for BeginRead:
private void UpdateChatBoxAsync(IAsyncResult result)
{
var stream = result.AsyncState as NetworkStream;
int bytesRead = stream.EndRead(result);
if (bytesRead > 0)
{
String newMessage = NetworkUtils.GetString(myBuffer);
UpdateChatBox(newMessage);
}
}
Can someone please shed some light as to the reason this exception is being raised? I tried recreating the buffer each time at the start of the while loop, but while that worked in not raising the exception, I would not receive messages from the server.
I also attempted to reset myBuffer to an empty array at the end of UpdateChatBoxAsync, this did not work either.
Any help would be appreciated.
You're running an infinite loop without any throttling. You are issuing async read calls as fast as the CPU can do it. This creates unlimited amounts of outstanding operations.
You are supposed to issue the next read when the last one has completed (most commonly done from the callback).
By just adding
if (myStream.DataAvailable)
you avoid the read but still burn one CPU core. This is just a mitigation for one of the symptoms. Fix the root cause instead.
It seems you should not be doing async IO at all because you don't have a problem with blocking. You're blocking a thread and burning 100% of a core. If that was acceptable, don't even bother with async IO.
Or, use await ReadAsync.
I have found my solution.
Before trying the BeginRead I check to see if the stream has any data available using NetworkStream.DataAvailable or in my case myStream.DataAvailable
So the new working block of code looks like
if (myStream.DataAvailable){
myStream.BeginRead(myBuffer, 0, BUFFER_SIZE, new AsyncCallback(UpdateChatBoxAsync), myStream);
}