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);
}
Related
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.
I'm facing asynchronous methods for the first time and I cannot understand the advantages of the following code on a synchronous version:
private void SendCallback(IAsyncResult ar)
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
// Signal that all bytes have been sent.
SendCompleted.Set();
}
private AutoResetEvent SendCompleted = new AutoResetEvent(false);
public bool Send(byte[] byteData)
{
try
{
RfTcpClient.Client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(this.SendCallback), RfTcpClient.Client);
}
catch (Exception)
{
return false;
}
return SendCompleted.WaitOne(1000, true);
}
I simplified it a bit so don't care about trivial error !
The Send method starts sending the message and then waits till the operation is completed without wasting cpu time. A new thread executes SendCallback which writes on the communication channel and it actively wait for the operation to complete, then signal the thread of the Send method.
I cannot see any advantages on synchronous method.
My assumption is that client.EndSend costs cpu time till it is completed, so the synchronous method is only moved to another thread. Am I missing something ?
I need a serial port program to read data coming in at 4800 baud. Right now I have a simulator sending 15 lines of data every second. The output of it seems to get "behind" and can't keep up with the speed/amount of data coming in.
I have tried using ReadLine() with a DataReceieved event, which did not seem to be reliable, and now I am using an async method with serialPort.BaseStream.ReadAsync:
okToReadPort = true;
Task readTask = new Task(startAsyncRead);
readTask.Start();
//this method starts the async read process and the "nmeaList" is what
// is used by the other thread to display data
public async void startAsyncRead()
{
while (okToReadPort)
{
Task<string> task = ReadLineAsync(serialPort);
string line = await task;
NMEAMsg tempMsg = new NMEAMsg(line);
if (tempMsg.sentenceType != null)
{
nmeaList[tempMsg.sentenceType] = tempMsg;
}
}
public static async Task<string> ReadLineAsync(
this SerialPort serialPort)
{
// Console.WriteLine("Entering ReadLineAsync()...");
byte[] buffer = new byte[1];
string ret = string.Empty;
while (true)
{
await serialPort.BaseStream.ReadAsync(buffer, 0, 1);
ret += serialPort.Encoding.GetString(buffer);
if (ret.EndsWith(serialPort.NewLine))
return ret.Substring(0, ret.Length - serialPort.NewLine.Length);
}
}
This still seems inefficient, does anyone know of a better way to ensure that every piece of data is read from the port and accounted for?
Generally speaking, your issue is that you are performing IO synchronously with data processing. It doesn't help that your data processing is relatively expensive (string concatenation).
To fix the general problem, when you read a byte put it into a processing buffer (BlockingCollection works great here as it solves Producer/Consumer) and have another thread read from the buffer. That way the serial port can immediately begin reading again instead of waiting for your processing to finish.
As a side note, you would likely see a benefit by using StringBuilder in your code instead of string concatenation. You should still process via queue though.
My understanding is that async await is for IO (network, db, etc) and parallel task is for cpu.
Note: This code is a little harsh to make it concise for this post.
I have a windows service created in c# that has the following code
while (true)
{
var socket = await tcpListener.AcceptSocketAsync();
if (socket == null) { break; }
var client = new RemoteClient(socket);
Task.Run(() => client.ProcessMessage());
}
In the RemoteClient class the ProcessMessage method does this
byte[] buffer = new byte[4096];
rawMessage = string.Empty;
while (true)
{
Array.Clear(buffer, 0, buffer.Length);
int bytesRead = await networkStream.ReadAsync(buffer, 0, buffer.Length);
rawMessage += (System.Text.Encoding.ASCII.GetString(buffer).Replace("\0", string.Empty));
if (bytesRead == 0 || buffer[buffer.Length - 1] == 0)
{
StoreMessage();
return;
}
}
So I have the I/O work happening asynchronously. But my concern and my question is in using Task.Run to kick off the work am I still creating a block?
I'm trying to take a TCP connection and release it as quickly as possible in order to scale to a large number of connections.
I feel like I'm mixing paradigms here.
Thanks
My understanding is that async await is for IO (network, db, etc) and parallel task is for cpu.
I would say that understand is incorrect. async/await is for any asynchronous operation, whether I/O or CPU bound.
…my concern and my question is in using Task.Run to kick off the work am I still creating a block?
"A block"? What kind of block do you think you would be creating otherwise?
Personally, I would not write the code that way. The accept operation will already complete in a thread pool thread (or synchronously in the same thread), i.e. one from the IOCP thread pool. It would be perfectly fine to set up some initial conditions for the connection on that thread, and then initiate the I/O from there. There's no reason to queue up the work on yet another thread.
So the way I'd write the code is like this:
async Task ProcessMessage()
{
byte[] buffer = new byte[4096];
rawMessage = string.Empty;
while (true)
{
Array.Clear(buffer, 0, buffer.Length);
int bytesRead = await networkStream.ReadAsync(buffer, 0, buffer.Length);
rawMessage += (System.Text.Encoding.ASCII.GetString(buffer).Replace("\0", string.Empty));
if (bytesRead == 0 || buffer[buffer.Length - 1] == 0)
{
StoreMessage();
return;
}
}
}
Then in your service:
while (true)
{
var socket = await tcpListener.AcceptSocketAsync();
if (socket == null) { break; }
var client = new RemoteClient(socket);
var _ = client.ProcessMessage();
}
Notes:
The dummy _ variable is just there to keep the compiler from warning you about the ignored, non-awaited async return)
Since you are ignoring the returned Task object, you won't receive thrown exceptions. So in lieu of that, you should add appropriate exception handling to the ProcessMessage() method itself.
I agree with commenter shr regarding cleanup. You didn't provide a complete code example, so we don't know what e.g. the StoreMessage() method does. But presumably/hopefully you have logic in there somewhere that correctly and gracefully shuts down the connection and closes the socket.
I have a windows service which is getting started using Thread.After installation as windows service i am able to start the service properly but as soon as i try to stop the service it is taking too much time and and not getting stopped.I am using ManualResetEvent to stop the windows service Here is my code.
protected override void OnStart(string[] args)
{
_thread = new Thread(DoWork);
_thread.Start();
}
private void DoWork()
{
while (!_shutdownEvent.WaitOne(0))
{
data = new byte[1024];
int recv = sock.Receive(data);
stringData = Encoding.ASCII.GetString(data, 0, recv);
}
sock.Shutdown(SocketShutdown.Both);
sock.Close();
}
catch (Exception DFGFD)
{
}
}
protected override void OnStop()
{
_shutdownEvent.Set();
_thread.Join(); // wait for thread to stop
}
}
}
Please help me to resolve this.
You socket is blocking on the receive code. I would suggest issuing:
sock.Shutdown(SocketShutdown.Both);
sock.Close();
in a method called from the OnStop() handler (so it is called from another thread to the blocking Receive). This will cause the blocking sock.Receive to fail with an exception that you can handle by quitting the loop.
Maybe the code in the while is blocking ( ie waiting data from the conenction synchronously )
Typically you shoul use asynchronous I/O operation with the socket, that generally allow you to avoid starting a new thread.
The problem is in the call to Receive in the loop. From MSDN:
If no data is available for reading, the Receive method will block
until data is available, unless a time-out value was set by using
Socket.ReceiveTimeout. If the time-out value was exceeded, the Receive
call will throw a SocketException. If you are in non-blocking mode,
and there is no data available in the in the protocol stack buffer,
the Receive method will complete immediately and throw a
SocketException. You can use the Available property to determine if
data is available for reading. When Available is non-zero, retry the
receive operation.
Therefore, I'd suggest to change the code in the loop to this:
if (sock.Available > 0)
{
data = new byte[1024];
int recv = sock.Receive(data);
stringData = Encoding.ASCII.GetString(data, 0, recv);
}
Thread.Sleep(200);
This code checks whether data is available for reading and only calls Receive if there is data to read. In order to avoid busy waiting, it issues a call the Thread.Sleep.
If you want to avoid the call to Thread.Sleep, you can specify a ReceiveTimeout on the socket:
sock.ReceiveTimeout = 200;
The code in the while loop would then look like this:
try
{
data = new byte[1024];
int recv = sock.Receive(data);
stringData = Encoding.ASCII.GetString(data, 0, recv);
}
catch(SocketException ex)
{
if (ex.SocketErrorCode != SocketError.TimedOut)
throw;
// In case of Timeout, do nothing, continue loop
}
Problem: Receive() is waiting for additional input.
The Thread.Join() function is waiting for the thread to finish.
Possible Solution:
When I've been in similar situations, I've used a timeout on the Join function thread.Join(3000); This gives the thread an opportunity to do a clean shutdown, then continue.