Please excuse my lack of knowledge regarding Tasks and Async.
Using the TcpClient class I am creating a connection with an available server:
void async RunClientAsync()
{
TcpClient client = new TcpClient();
try
{
await client.ConnectAsync(IPAddress.Parse("1.1.1.1"), 8889);
Task.Start(() => ReadClientAsync(client));
}
catch (Exception ex)
{
HandleException(ex);
}
}
// -----
void async ReadClientAsync(TcpClient client)
{
byte[] bf = new byte[2048];
try
{
while(true)
{
int br = await client.NetworkStream().ReadAsync();
if (br > 0)
{
HandleInboundData(bf, br);
}
}
}
catch (Exception ex)
{
HandleException(ex);
}
}
The helper methods HandleException(Exception ex) and HandleInboundData(byte[] buffer, int length) will perform the assumed tasks.
The connection to the server will be in perpetuity and the data received from the server will be of unknown length and frequency, the idea being to throw a task out there that receives and processes the inbound data only when data is available.
ReadClientAsync(TcpClient client) is an obvious fail because ReadAsync will always return 0 bytes if there is no data available.
How should I approach writing ReadClientAsync using async / task to prevent the busy-looping situation? I've used BeginRead / EndRead in these situations before, which has worked fine. Would that be the solution in this particular case?
Thank you,
No, that's not how TCP works.
NetworkStream is considered to be in an "end of stream" state when the other side has initiated (possible one-way) shutdown. That's when ReadAsync (or Read, for that matter) returns zero - not in any other case.
The MSDN documentation can be easily misunderstood - mainly because you're looking at the wrong piece of documentation. NetworkStream doesn't override ReadAsync (there's no reason to do so), so you're actually looking at the documentation for the generic Stream.ReadAsync. In contrast, the documentation for NetworkStream.Read says:
This method reads data into the buffer parameter and returns the number of bytes successfully read. If no data is available for reading, the Read method returns 0. The Read operation reads as much data as is available, up to the number of bytes specified by the size parameter. If the remote host shuts down the connection, and all available data has been received, the Read method completes immediately and return zero bytes.
Note the final sentence, which tells you what it actually means for a NetworkStream to be "end of stream". This is how TCP connections are closed.
Your response to this should usually be shutting down the connection from the other side as well - return out of your helper method and clean up the socket. In any case, do not repeat the while (true) again - you're just going to get an infinite loop that eats 100% of your CPU.
If you want a few pointers on how to handle C# asynchronous sockets with await, have a look at my sample at https://github.com/Luaancz/Networking/tree/master/Networking%20Part%202. Note the disclaimers - this is in no way production ready. But it does solve a few of the very common mistakes people make when implementing TCP communication.
Related
In C# you have 3 ways to try and receive TCP data on a Socket:
Socket.Receive is a synchronous, blocking method. It doesn't return until it succeeds, barring failure or [optionally] timeout.
Socket.BeginReceive is asynchronous, a supplied callback/delegate is called when there is data to receive, using the now-antiquated Begin/End pattern
Socket.ReceiveAsync begins an asynchronous request to receive data
However my understanding is none of these actually let you cancel the receive operation? The docs suggest EndReceive is used for completing a read, not something one could call to terminate the request?
You often see code like
while(socket.Available==0 && !cancel)Sleep(50); if(!cancel)socket.Receive(...);
But that's pretty terrible.
If I want to sit waiting for data but at some point cancel the receive, say the user hits "stop" button how can this neatly be done so I don't get a callback triggered later on when unexpected?
I had wondered about closing the socket, which would cause the Receive operation to fail, but it seems somewhat ugly. Am I thinking along the right lines or do the various API methods listed above allow a direct cancellation so I don't risk dangling async operations?
There is no known way to cancel it (AFAIK)
One thing you can do it set the Socket.Blocking = false. The receive will return immediately when there is no data. This way it will not hang.
You should check the Socket.Blocking property.
I advise you to use the BeginReceive(IList<ArraySegment<Byte>>, SocketFlags, SocketError, AsyncCallback, Object) overload to prevent it throwing exceptions.
Check the SocketError on "Would Block", meaning "there is not data". So you can try again.
Didn't tested it but ->
A nice idea is using the non-async version Receive to receive 0 bytes (use a static byte[] emptyBuffer = new byte[0]) , and if the sockerError returns with a 'would block', you can have a short delay and retry it. When it doesn't return a socketError there is probably data. So you can start an async version.
What you could do is get a NetworkStream from the socket being read and use it's ReadTimeout property, for example:
// Get stream from socket:
using NetworkStream ns = new NetworkStream(socket);
// Set timeout:
stream.ReadTimeout = 10 * 1000; // 10 sec
var buffer = new List<byte>();
try
{
do
{
buffer.Add((byte) stream.ReadByte());
}
while (stream.DataAvailable);
}
catch (IOException)
{
// Timeout
}
return buffer.ToArray();
I am experimenting in creating a small IRC server to learn some new programming concepts (and others I haven't used in forever). The first step is to get a basic client connecting via TCP to send plaintext commands to the server.
To listen for connection I have the following code:
public NetworkClient(Server server, TcpClient socket, int id)
{
_socket = socket;
_id = id;
_server = server;
}
private async void ListenForClients()
{
int numClients = 0;
while (IsRunning)
{
var tcpClient = await _listener.AcceptTcpClientAsync();
var netClient = new NetworkClient(this, tcpClient, numClients);
netClient.Start();
Console.WriteLine("Client Connected");
numClients++;
}
}
Then in my NetworkClient class my Start() method looks like:
public async void Start()
{
using (var reader = new StreamReader(_socket.GetStream()))
{
while (_server.IsRunning)
{
var line = await reader.ReadLineAsync();
Console.WriteLine("Client {0} wrote: {1}", _id, line);
}
}
}
This works well while a telnet client is connected, however once I close my telnet client reader.ReadLineAsync(); constantly returns null. I would add a check to see if line == null but I'm not sure that is the correct way to detect if a client has disconnected.
To make matters worse, _socket.Connected is constantly returning true all while nulls are getting "received" by reader.ReadLineAsync().
What is the proper way to detect when tcp clients have been disconnected?
A read on a TCP/IP socket will return 0 bytes when the connection has been gracefully closed. This situation causes ReadLineAsync to return null. So, yes, you should check for null and treat it as a graceful socket closure.
Sockets may be closed other ways, too; any socket operation may throw an exception if the socket is abortively closed. If the exception happens at an acceptable part of the protocol (where a close is not considered an error), then you should treat that exception as though it were a graceful closure as well.
Oh, and TcpClient.Connected (like Socket.Connected) is practically useless; it only tells you whether the socket was connected, not whether it is connected. Just pretend that property doesn't exist.
Finally, a couple of notes:
Avoid async void. If your methods return Task, then you have a "handle" to see when they complete (and whether they raised exceptions). My recent MSDN article explains why async void is not recommended.
It's best to periodically send data over the connection to determine if it is still viable. I wrote a TCP/IP .NET sockets FAQ that covers this in more detail.
I'm writing a server for a game, and I want to be able to handle thousands of concurrent users. For this reason, I went with non-blocking sockets and use the poll method. However, I do create multiple threads to handle database and web calls, and some of these threads will send a response to the user. In one of these threads, on send, I get the error "A non-blocking socket operation could not be completed immediately". What could cause this problem? I imagine it's because a poll is occurring at the same time as send is called. If I used beginAsync, would it take stop this error? I thought about locking the socket, but I don't want my main thread to be blocked for this.
I don't know what kind of non-blocking-polling socket calls are you using, but I would recommend that you use the Async socket calls (instead of the Begin). For more information on the difference between Async calls vs Begin see: What's the difference between BeginConnect and ConnectAsync?
The asynchronous calls automatically do "polling" on the OS level, which will be much more efficient than your polling. As a matter of fact, they use IO completion ports, which are probably the fastest and most efficient thing you can use on Windows to handle a large amount of client connections/requests.
As far as the error, I would consider this to be the normal operation of non-blocking sockets, so you just have to handle it gracefully.
Update
Your server should probably do something like this:
// Process the accept for the socket listener.
private void ProcessAccept(SocketAsyncEventArgs e)
{
Socket s = e.AcceptSocket;
if (s.Connected)
{
try
{
SocketAsyncEventArgs readEventArgs = this.readWritePool.Pop();
if (readEventArgs != null)
{
// Get the socket for the accepted client connection and put it into the
// ReadEventArg object user token.
readEventArgs.UserToken = new Token(s, this.bufferSize);
Interlocked.Increment(ref this.numConnectedSockets);
Console.WriteLine("Client connection accepted.
There are {0} clients connected to the server",
this.numConnectedSockets);
if (!s.ReceiveAsync(readEventArgs))
{
this.ProcessReceive(readEventArgs);
}
}
else
{
Console.WriteLine("There are no more available sockets to allocate.");
}
}
catch (SocketException ex)
{
Token token = e.UserToken as Token;
Console.WriteLine("Error when processing data received from {0}:\r\n{1}",
token.Connection.RemoteEndPoint, ex.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
// Accept the next connection request.
this.StartAccept(e);
}
}
Code sample courtesy of code project: http://www.codeproject.com/Articles/22918/How-To-Use-the-SocketAsyncEventArgs-Class
When a non-blocking socket tries to read data but finds none you get that error: the socket would like to wait for data but can't because it has to return immediately, being non-blocking.
I'd suggest you switch to blocking sockets, find out why data is missing, adjust accordingly then revert to non-blocking ones. Or, you could handle the error and retry the operation.
I was also receiving this exception on sending data and just found the solution.
You get the exception because the socket's send buffer is full. Because you are trying to send the data via a non-blocking send, the exception is raised to let you know that you MUST send it via a blocking send.
The data is not sent once the exception is raised, so you have to resend it. Your individual send call now becomes;
try
{
m_socket.Send(buffer, bufferSize, SocketFlags.None);
}
catch (SocketException e)
{
if(e.SocketErrorCode == WouldBlock)
{
m_socket.Blocking = true;
m_socket.Send(buffer, bufferSize, SocketFlags.None);
m_socket.Blocking = false;
}
}
It would also be a good idea to increase the socket's SendBufferSize. By default I think it is 8kb. For my needs I had to increase it to 2MB, and afterwards the Send call no longer threw that exception.
This exception is too general. Per MSDN,
If you receive a SocketException, use the SocketException.ErrorCode property to obtain the specific error code. After you have obtained this code, refer to the Windows Sockets version 2 API error code documentation in the MSDN library for a detailed description of the error.
Sockets error codes are here.
I'm currently in the process of developing a C# Socket server that can accept multiple connections from multiple client computers. The objective of the server is to allow clients to "subscribe" and "un-subscribe" from server events.
So far I've taken a jolly good look over here: http://msdn.microsoft.com/en-us/library/5w7b7x5f(v=VS.100).aspx and http://msdn.microsoft.com/en-us/library/fx6588te.aspx for ideas.
All the messages I send are encrypted, so I take the string message that I wish to send, convert it into a byte[] array and then encrypt the data before pre-pending the message length to the data and sending it out over the connection.
One thing that strikes me as an issue is this: on the receiving end it seems possible that Socket.EndReceive() (or the associated callback) could return when only half of the message has been received. Is there an easy way to ensure each message is received "complete" and only one message at a time?
EDIT: For example, I take it .NET / Windows sockets does not "wrap" the messages to ensure that a single message sent with Socket.Send() is received in one Socket.Receive() call? Or does it?
My implementation so far:
private void StartListening()
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
IPEndPoint localEP = new IPEndPoint(ipHostInfo.AddressList[0], Constants.PortNumber);
Socket listener = new Socket(localEP.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(localEP);
listener.Listen(10);
while (true)
{
// Reset the event.
this.listenAllDone.Reset();
// Begin waiting for a connection
listener.BeginAccept(new AsyncCallback(this.AcceptCallback), listener);
// Wait for the event.
this.listenAllDone.WaitOne();
}
}
private void AcceptCallback(IAsyncResult ar)
{
// Get the socket that handles the client request.
Socket listener = (Socket) ar.AsyncState;
Socket handler = listener.EndAccept(ar);
// Signal the main thread to continue.
this.listenAllDone.Set();
// Accept the incoming connection and save a reference to the new Socket in the client data.
CClient client = new CClient();
client.Socket = handler;
lock (this.clientList)
{
this.clientList.Add(client);
}
while (true)
{
this.readAllDone.Reset();
// Begin waiting on data from the client.
handler.BeginReceive(client.DataBuffer, 0, client.DataBuffer.Length, 0, new AsyncCallback(this.ReadCallback), client);
this.readAllDone.WaitOne();
}
}
private void ReadCallback(IAsyncResult asyn)
{
CClient theClient = (CClient)asyn.AsyncState;
// End the receive and get the number of bytes read.
int iRx = theClient.Socket.EndReceive(asyn);
if (iRx != 0)
{
// Data was read from the socket.
// So save the data
byte[] recievedMsg = new byte[iRx];
Array.Copy(theClient.DataBuffer, recievedMsg, iRx);
this.readAllDone.Set();
// Decode the message recieved and act accordingly.
theClient.DecodeAndProcessMessage(recievedMsg);
// Go back to waiting for data.
this.WaitForData(theClient);
}
}
Yes, it is possible you'll have only part of message per one receiving, also it can be even worse during transfer only part of message will be sent. Usually you can see that during bad network conditions or under heavy network load.
To be clear on network level TCP guaranteed to transfer your data in specified order but it not guaranteed that portions of data will be same as you sent. There are many reasons for that software (take a look to Nagle's algorithm for example), hardware (different routers in trace), OS implementation, so in general you should never assume what part of data already transferred or received.
Sorry for long introduction, below some advices:
Try to use relatevely "new" API for high-performance socket server, here samples Networking Samples for .NET v4.0
Do not assume you always send full packet. Socket.EndSend() returns number of bytes actually scheduled to send, it can be even 1-2 bytes under heavy network load. So you have to implement resend rest part of buffer when it required.
There is warning on MSDN:
There is no guarantee that the data
you send will appear on the network
immediately. To increase network
efficiency, the underlying system may
delay transmission until a significant
amount of outgoing data is collected.
A successful completion of the
BeginSend method means that the
underlying system has had room to
buffer your data for a network send.
Do not assume you always receive full packet. Join received data in some kind of buffer and analyze it when it have enough data.
Usually, for binary protocols, I add field to indicate how much data incoming, field with message type (or you can use fixed length per message type (generally not good, e.g. versioning problem)), version field (where applicable) and add CRC-field to end of message.
It not really required to read, a bit old and applies directly to Winsock but maybe worth to study: Winsock Programmer's FAQ
Take a look to ProtocolBuffers, it worth to learn: http://code.google.com/p/protobuf-csharp-port/, http://code.google.com/p/protobuf-net/
Hope it helps.
P.S. Sadly sample on MSDN you refer in question effectively ruin async paradigm as stated in other answers.
Your code is very wrong. Doing loops like that defeats the purpose of asynchronous programming. Async IO is used to not block the thread but let them continue doing other work. By looping like that, you are blocking the thread.
void StartListening()
{
_listener.BeginAccept(OnAccept, null);
}
void OnAccept(IAsyncResult res)
{
var clientSocket = listener.EndAccept(res);
//begin accepting again
_listener.BeginAccept(OnAccept, null);
clientSocket.BeginReceive(xxxxxx, OnRead, clientSocket);
}
void OnReceive(IAsyncResult res)
{
var socket = (Socket)res.Asyncstate;
var bytesRead = socket.EndReceive(res);
socket.BeginReceive(xxxxx, OnReceive, socket);
//handle buffer here.
}
Note that I've removed all error handling to make the code cleaner. That code do not block any thread and is therefore much more effecient. I would break the code up in two classes: the server handling code and the client handling code. It makes it easier to maintain and extend.
Next thing to understand is that TCP is a stream protocol. It do not guarentee that a message arrives in one Receive. Therefore you must know either how large a message is or when it ends.
The first solution is to prefix each message with an header which you parse first and then continue reading until you get the complete body/message.
The second solution is to put some control character at the end of each message and continue reading until the control character is read. Keep in mind that you should encode that character if it can exist in the actual message.
You need to send fixed length messages or include in the header the length of the message. Try to have something that allows you to clearly identify the start of a packet.
I found 2 very useful links:
http://vadmyst.blogspot.com/2008/03/part-2-how-to-transfer-fixed-sized-data.html
C# Async TCP sockets: Handling buffer size and huge transfers
Naturally, BeginReceive() will never end if there's no data.
MSDN suggests that calling Close() would abort BeginReceive().
However, calling Close() on the socket also performs a Dispose() on it, as figured out in this great answer, and consequently EndReceive() would throw an exception because the object is already disposed (and it does!).
How should I proceed?
It seems like this is by (the very dumb) design. You must have this exception thrown and caught in your code.
MSDN looks silent about it indeed, but if you look at the documentation of another asynchronous socket method, BeginConnect(), here's what we find:
To cancel a pending call to the
BeginConnect() method, close the
Socket. When the Close() method is
called while an asynchronous operation
is in progress, the callback provided
to the BeginConnect() method is
called. A subsequent call to the
EndConnect(IAsyncResult) method will
throw an ObjectDisposedException to
indicate that the operation has been
cancelled.
If it is the proper way of doing for BeginConnect, it is probably so for BeginReceive as well. This is certainly a poor design on the part of Microsoft's async API, because making the user necessarily throw and catch exception as a part of a normal flow would annoy the debugger. You have really no way to "wait" until the operation is completed, because Close() is what completes it in the first place.
I am surprised no one recommended using SocketOptions.
Once the stack has the send or receive operation it is bound by the socket options of the socket.
Use a small send or receive timeout and use it before the operation so you don't care if it's changed during that same operation to something shorter or longer.
This will cause more context switching but will not require closing the socket under any protocol.
For example:
1) Set a small timeout
2) Perform operations
3) Set timeout larger
This is similar to using Blocking = false but with an automatic timeout that you specify.
You can read my solution of this problem here(using comment of Pavel Radzivilovsky here):
UdpClient.ReceiveAsync correct early termination
For TCP socket connections, you can use the Connected property to determine the state of the socket before trying to access any disposed methods. Per MSDN:
"The Connected property gets the connection state of the Socket as of the last I/O operation. When it returns false, the Socket was either never connected, or is no longer connected."
Since it says "no longer connected" it implies that a Close() was previously called on the socket. If you check whether the socket is Connected at the start of the receive callback, there will be no exception.
In the ReceiveCallback I checked client.Connected within the try block.
Now, when data is received after BeginReceive, I can call client.Close();
This way, I do not see exceptions. I send modbus-TCP requests every 200mS, and get responses in time. The console output looks clean. I used a windows forms app, to test this.
private static void ReceiveCallback(IAsyncResult ar)
{
try
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
if (client.Connected)
{
// Read data from the remote device.
state.dataSize = client.EndReceive(ar);
if (state.dataSize > 0)
{
Console.WriteLine("Received: " + state.dataSize.ToString() + " bytes from server");
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, state.dataSize));
// Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BUFFER_SIZE, 0,
new AsyncCallback(ReceiveCallback), state);
state.dataSizeReceived = true; //received data size?
dataSize = state.dataSize;
buffer = state.buffer.ToArray();
dataSizeReceived = state.dataSizeReceived;
string hex = ByteArrayToString(state.buffer, state.dataSize);
Console.WriteLine("<- " + hex);
receiveDone.Set();
client.Close();
}
else
{
Console.WriteLine("All the data has arrived");
// All the data has arrived; put it in response.
if (state.sb.Length > 1)
{
Console.WriteLine("Length: " + state.sb.Length.ToString());
}
// Signal that all bytes have been received.
receiveDone.Set();
}
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
Another solution would be to send "yourself" a "control message" using a socket bound to a different port. It's not exactly an abort, but it would end your async operation.
I was struggling with this as well but as far as I can tell using a simple boolean flag before calling .BeginReceive() will work as well (so there'll be no need for exception handling). Since I already had start/stop handling, this fix was a matter of one if statement (scroll down to the bottom of the OnReceive() method).
if (_running)
{
_mainSocket.BeginReceive(_data, 0, _data.Length, SocketFlags.None, OnReceive, null);
}
Should I have overlooked something with this approach, let me know!