Multithreading using AsyncCallback and GUI controls - c#

Multithread programming is a new concept for me. I’ve done a bunch of reading and even with many examples, I just can’t seem to figure it out. I'm new to C# and programming.
I have a winform project with lots of custom controls I’ve imported and will utilize many tcpclients. I’m trying to get each control to be hosted on it’s own separate thread. Right now, I’m trying to get 1 control to behave appropriately with it’s own thread.
I'll show you what I have and then follow up with some questions regarding guidance.
string asyncServerHolder; // gets the server name from a text_changed event
int asyncPortHolder; // gets the port # from a text_changed event
TcpClient wifiClient = new TcpClient();
private void btnStart_Click(object sender, EventArgs e)
{
... // variable initialization, etc.
... // XML setup, http POST setup.
send(postString + XMLString); // Content to send.
}
private void send(string msg)
{
AsyncCallback callBack = new AsyncCallback(ContentDownload);
wifiClient.BeginConnect(asyncServerHolder, asyncPortHolder, callBack, wifiClient);
wifiClient.Client.Send(System.Text.Encoding.ASCII.GetBytes(msg));
}
private void ContentDownload(IAsyncResult result)
{
if (wifiClient.Connected)
{
string response4 = "Connected!!"; //debug msg
byte[] buff = new byte[1024];
int i = wifiClient.Client.Receive(buff);
do
{
response1 = System.Text.Encoding.UTF8.GetString(buff, 0, i);
} while (response1.Length == 0);
response2 = response1.Substring(9, 3); // pick out status code to be displayed after
wifiClient.Client.Dispose();
wifiClient.Close();
}
}
If you're knowledgeable about this, I bet you see lots of problems above. As it stands right now, I always get an exception one my first iteration of running this sequence:
"A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied"
Why is this? I have confirmed that my asyncServerHolder and my asyncPortHolder are correct. My second iteration of attempting allowed me to see response4 = "Connected!!" but I get a null response on response1.
Eventually I'd like to substitute in my user controls which I have in a List. I'd just like to gracefully connect, send my msg, receive my response and then allow my form to notify me from that particular control which plays host to that tcp client. My next step would be link up many controls.
Some questions:
1) Do I need more TCP clients? Should they be in a list and be the # of controls I have enabled at that time of btnStart_Click?
2) My controls are on my GUI, does that mean I need to invoke if I'm interacting with them?
3) I see many examples using static methods with this context. Why is this?
Thanks in advance. All criticism is welcome, feel free to be harsh!

BeginConnect returns immediately. Probably, no connection has been established yet when Send runs. Make sure that you use the connection only after having connected.
if (wifiClient.Connected) and what if !Connected? You just do nothing. That's not a valid error recovery strategy. Remove this if entirely.
In your read loop you destroy the previously read contents on each iteration. In fact, you can't split up an UTF8 encoded string at all and decode the parts separately. Read all bytes into some buffer and only when you have received everything, decode the bytes to a string.
wifiClient.Client.Dispose();
wifiClient.Close();
Superstitious dispose pattern. wifiClient.Dispose(); is the canonical way to release everything.
I didn't quite understand what "controls" you are talking about. A socket is not a control. UI controls are single-threaded. Only access them on the UI thread.
Do I need more TCP clients?
You need one for each connection.
Probably, you should use await for all blocking operations. There are wrapper libraries that make the socket APIs usable with await.

Related

How to bind web socket to events in .NET C#

I am writing a plugin for the Elgato Stream Deck. The Stream Deck uses a web socket for communication with plugins. I am trying to write my plugin as a console app in C#. The "compiled plugin" example provided by Elgato is broken (this GitHub issue points to one problem, and there appear to be more) and does not appear to run - so I have been digging through the code to make sense of it and translate it to C# without using Visual Studio debugging. I did find this, however (link to code file on GitHub):
void ESDConnectionManager::Run()
{
try
{
// Create the endpoint
mWebsocket.clear_access_channels(websocketpp::log::alevel::all);
mWebsocket.clear_error_channels(websocketpp::log::elevel::all);
// Initialize ASIO
mWebsocket.init_asio();
// Register our message handler
mWebsocket.set_open_handler(websocketpp::lib::bind(&ESDConnectionManager::OnOpen, this, &mWebsocket, websocketpp::lib::placeholders::_1));
mWebsocket.set_fail_handler(websocketpp::lib::bind(&ESDConnectionManager::OnFail, this, &mWebsocket, websocketpp::lib::placeholders::_1));
mWebsocket.set_close_handler(websocketpp::lib::bind(&ESDConnectionManager::OnClose, this, &mWebsocket, websocketpp::lib::placeholders::_1));
mWebsocket.set_message_handler(websocketpp::lib::bind(&ESDConnectionManager::OnMessage, this, websocketpp::lib::placeholders::_1, websocketpp::lib::placeholders::_2));
websocketpp::lib::error_code ec;
std::string uri = "ws://127.0.0.1:" + std::to_string(mPort);
WebsocketClient::connection_ptr connection = mWebsocket.get_connection(uri, ec);
if (ec)
{
DebugPrint("Connect initialization error: %s\n", ec.message().c_str());
return;
}
mConnectionHandle = connection->get_handle();
// Note that connect here only requests a connection. No network messages are
// exchanged until the event loop starts running in the next line.
mWebsocket.connect(connection);
// Start the ASIO io_service run loop
// this will cause a single connection to be made to the server. mWebsocket.run()
// will exit when this connection is closed.
mWebsocket.run();
}
catch (websocketpp::exception const & e)
{
// Prevent an unused variable warning in release builds
(void)e;
DebugPrint("Websocket threw an exception: %s\n", e.what());
}
}
This appears to be using some library called boost.asio? The closest thing I can find in C# is the MessageWebSocket from Windows.Networking.Sockets (documentation here) but this appears to be only for Windows RT?
How do I create something analogous to this in a .NET console app? I have found references to System.Net.WebSockets.ClientWebSocket (documentation here), which appears to be the class I need, but I am not sure.
I have found a few samples (such as this one, and this one - both of which use yet another socket class, Socket) that show how to use Sockets in general, but they do not seem event-driven. They seem to send, and then immediately receive data. I want a web socket that calls local methods (akin to event handlers) when events/data are received by the socket. I believe this is what is going on in the sample C++ code above (specifically Open, Fail, Close, and Message). My understanding is that I need to do some work ("register" with the Stream Deck) on the web socket "Open" event, and then handle the web socket "Message" event and parse out what event the Stream Deck is reporting happened.
If you scroll down some lines in your example https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.socket?view=net-7.0 you will find a example called: Asyncronous Mode that handles sending and receiving separately.
Send part
int bytesSent = 0;
while (bytesSent < requestBytes.Length)
{
bytesSent += await socket.SendAsync(requestBytes.AsMemory(bytesSent), SocketFlags.None);
}
If you do not want the program to lock up, the easiest way would be to start a thread for listening maybe?

Can a TCP c# client receive and send continuously/consecutively without sleep?

This is to a degree a "basics of TCP" question, yet at the same time I have yet to find a convincing answer elsewhere and believe i have a ok/good understanding of the basics of TCP. I am not sure if the combination of questions (or the one questions and while i'm at it the request for confirmation of a couple of points) is against the rules. Hope not.
I am trying to write a C# implementation of a TCP client, that communicates with an existing app containing a TCP server (I don't have access to its code, so no WCF). How do I connect to it, send and receive as needed as new info comes in or out, and ultimately disconnect. Using the following MSDN code as an example where they list "Send" and "Receive" asynchronous methods (or just TcpClient), and ignoring the connect and disconnect as trivial, how can I best go about continuously checking for new packets received and at the same time send when needed?
I initially used TCPClient and GetStream(), and the msdn code still seems to require the loop and sleep described in a bit (counter intuitively), where I run the receive method in a loop in a separate thread with a sleep(10) milliseconds, and Send in the main (or third) thread as needed. This allows me to send fine, and the receive method effectively polls at regular intervals to find new packets. The received packets are then added to a queue.
Is this really the best solution? Shouldn't there be a DataAvailable event equivalent (or something i'm missing in the msdn code) that allows us to receive when, and only when, there is new data available?
As an afterthought I noticed that the socket could be cut from the other side without the client becoming aware till the next botched send. To clarify then, the client is obliged to send regular keepalives (and receive isn't sufficient, only send) to determine if the socket is still alive. And the frequency of the keepalive determines how soon I will know that link is down. Is that correct? I tried Poll, socket.connected etc only to discover why each just doesn't help.
Lastly, to confirm (i believe not but good to make sure), in the above scenario of sending on demand and receiving if tcpclient.DataAvailable every ten seconds, can there be data loss if sending and receiving at the same time? If at the same time I am receiving I try and send will one fail, overwrite the other or any other such unwanted behaviour?
There's nothing wrong necessarily with grouping questions together, but it does make answering the question more challenging... :)
The MSDN article you linked shows how to do a one-and-done TCP communication, that is, one send and one receive. You'll also notice it uses the Socket class directly where most people, including myself, will suggest using the TcpClient class instead. You can always get the underlying Socket via the Client property should you need to configure a certain socket for example (e.g., SetSocketOption()).
The other aspect about the example to note is that while it uses threads to execute the AsyncCallback delegates for both BeginSend() and BeginReceive(), it is essentially a single-threaded example because of how the ManualResetEvent objects are used. For repeated exchange between a client and server, this is not what you want.
Alright, so you want to use TcpClient. Connecting to the server (e.g., TcpListener) should be straightforward - use Connect() if you want a blocking operation or BeginConnect() if you want a non-blocking operation. Once the connection is establish, use the GetStream() method to get the NetworkStream object to use for reading and writing. Use the Read()/Write() operations for blocking I/O and the BeginRead()/BeginWrite() operations for non-blocking I/O. Note that the BeginRead() and BeginWrite() use the same AsyncCallback mechanism employed by the BeginReceive() and BeginSend() methods of the Socket class.
One of the key things to note at this point is this little blurb in the MSDN documentation for NetworkStream:
Read and write operations can be performed simultaneously on an
instance of the NetworkStream class without the need for
synchronization. As long as there is one unique thread for the write
operations and one unique thread for the read operations, there will
be no cross-interference between read and write threads and no
synchronization is required.
In short, because you plan to read and write from the same TcpClient instance, you'll need two threads for doing this. Using separate threads will ensure that no data is lost while receiving data at the same time someone is trying to send. The way I've approached this in my projects is to create a top-level object, say Client, that wraps the TcpClient and its underlying NetworkStream. This class also creates and manages two Thread objects, passing the NetworkStream object to each during construction. The first thread is the Sender thread. Anyone wanting to send data does so via a public SendData() method on the Client, which routes the data to the Sender for transmission. The second thread is the Receiver thread. This thread publishes all received data to interested parties via a public event exposed by the Client. It looks something like this:
Client.cs
public sealed partial class Client : IDisposable
{
// Called by producers to send data over the socket.
public void SendData(byte[] data)
{
_sender.SendData(data);
}
// Consumers register to receive data.
public event EventHandler<DataReceivedEventArgs> DataReceived;
public Client()
{
_client = new TcpClient(...);
_stream = _client.GetStream();
_receiver = new Receiver(_stream);
_sender = new Sender(_stream);
_receiver.DataReceived += OnDataReceived;
}
private void OnDataReceived(object sender, DataReceivedEventArgs e)
{
var handler = DataReceived;
if (handler != null) DataReceived(this, e); // re-raise event
}
private TcpClient _client;
private NetworkStream _stream;
private Receiver _receiver;
private Sender _sender;
}
Client.Receiver.cs
private sealed partial class Client
{
private sealed class Receiver
{
internal event EventHandler<DataReceivedEventArgs> DataReceived;
internal Receiver(NetworkStream stream)
{
_stream = stream;
_thread = new Thread(Run);
_thread.Start();
}
private void Run()
{
// main thread loop for receiving data...
}
private NetworkStream _stream;
private Thread _thread;
}
}
Client.Sender.cs
private sealed partial class Client
{
private sealed class Sender
{
internal void SendData(byte[] data)
{
// transition the data to the thread and send it...
}
internal Sender(NetworkStream stream)
{
_stream = stream;
_thread = new Thread(Run);
_thread.Start();
}
private void Run()
{
// main thread loop for sending data...
}
private NetworkStream _stream;
private Thread _thread;
}
}
Notice that these are three separate .cs files but define different aspects of the same Client class. I use the Visual Studio trick described here to nest the respective Receiver and Sender files under the Client file. In a nutshell, that's the way I do it.
Regarding the NetworkStream.DataAvailable/Thread.Sleep() question. I would agree that an event would be nice, but you can effectively achieve this by using the Read() method in combination with an infinite ReadTimeout. This will have no adverse impact on the rest of your application (e.g., UI) since it's running in its own thread. However, this complicates shutting down the thread (e.g., when the application closes), so you'd probably want to use something more reasonable, say 10 milliseconds. But then you're back to polling, which is what we're trying to avoid in the first place. Here's how I do it, with comments for explanation:
private sealed class Receiver
{
private void Run()
{
try
{
// ShutdownEvent is a ManualResetEvent signaled by
// Client when its time to close the socket.
while (!ShutdownEvent.WaitOne(0))
{
try
{
// We could use the ReadTimeout property and let Read()
// block. However, if no data is received prior to the
// timeout period expiring, an IOException occurs.
// While this can be handled, it leads to problems when
// debugging if we are wanting to break when exceptions
// are thrown (unless we explicitly ignore IOException,
// which I always forget to do).
if (!_stream.DataAvailable)
{
// Give up the remaining time slice.
Thread.Sleep(1);
}
else if (_stream.Read(_data, 0, _data.Length) > 0)
{
// Raise the DataReceived event w/ data...
}
else
{
// The connection has closed gracefully, so stop the
// thread.
ShutdownEvent.Set();
}
}
catch (IOException ex)
{
// Handle the exception...
}
}
}
catch (Exception ex)
{
// Handle the exception...
}
finally
{
_stream.Close();
}
}
}
As far as 'keepalives' are concerned, there is unfortunately not a way around the problem of knowing when the other side has exited the connection silently except to try sending some data. In my case, since I control both the sending and receiving sides, I've added a tiny KeepAlive message (8 bytes) to my protocol. This is sent every five seconds from both sides of the TCP connection unless other data is already being sent.
I think I've addressed all the facets that you touched on. I hope you find this helpful.

C# "using" SerialPort transmit with data loss

I'm new to this forum, and I have a question that has been bothering me for a while.
My setup is a serial enabled character display connected to my pc with a usb/uart converter. I'm transmitting bytes to the display via the serialPort class in a separate write buffer thread in a C++ style:
private void transmitThread(){
while(threadAlive){
if(q.Count > 0){ // Queue not empty
byte[] b = q.Dequeue();
s.Write(b,0,b.Length);
System.Threading.Thread.Sleep(100);
}
else{ // Queue empty
System.Threading.Thread.Sleep(10);
}
}
}
Assuming the serial port is already opened, this works perfectly and transmits all the data to the display. There are though no exception handling at all in this snippet. Therefore I was looking into implementing a typical C# feature, the 'using' statement and only opening the port when needed, like so:
private void transmitThread(){
while(threadAlive){
if(q.Count > 0){ // Queue not empty
byte[] b = q.Dequeue();
using(s){ //using the serialPort
s.Open();
s.Write(b,0,b.Length);
s.Close();
}
System.Threading.Thread.Sleep(100);
}
else{ // Queue empty
System.Threading.Thread.Sleep(10);
}
}
}
The problem with this function is, that it only transmits a random amount of the data, typically about one third of the byte-array of 80 bytes. I have tried different priority settings of the thread, but nothing changes.
Am I missing something important, or do I simply close the port too fast after a transmit request?
I hope you can help me. Thanks :)
No, that was a Really Bad Idea. The things that go wrong, roughly in the order you'll encounter them:
the serial port driver discards any bytes left in the transmit buffer that were not yet transmitted when you close the port. Which is what you are seeing now.
the MSDN article for SerialPort.Close() warns that you must "wait a while" before opening the port again. There's an internal worker thread that needs to shut down. The amount of time you have to wait is not specified and is variable, depending on machine load.
closing a port allows another program to grab the port and open it. Serial ports cannot be shared, your program will fail when you try to open it again.
Serial ports were simply not designed to be opened and closed on-the-fly. Only open it at the start of your program, close it when it ends. Not calling Close() at all is quite acceptable and avoids a deadlock scenario.
I think you're missing the point of the using block. A typical using block will look like this:
using (var resource = new SomeResource())
{
resource.DoSomething();
}
The opening happens at the very beginning. Typically as part of the constructor. But sometimes on the first line of the using block.
But the big red flag I see is that the closing happens automatically. You don't need the .Close() call.
If the successful operation of your serial device is dependent on the calls to Thread.Sleep then perhaps the thread is being interrupted at some point, sufficient to make the data transmission out of sync with the device. There would most likely be ways to solve this but the first thing I would do is try to use the .NET SerialPort class instead. The Write method is very similar to what you want to do, and there are C++ code examples in those articles.

C# SocketAsyncEventArgs not sending all data

I have a problem with the SocketAsyncEventArgs class..the problem is when I try to send 8K of data
for example over internet, the socket sometimes only sends 1K or 2K, I know that this is normal for
TCP socket and that one send doesn't guarantee one receive.
now for this to work for me I modified my code to resend the remaining data, for example
when I the SocketAsyncEventArgs.SendAsync completes, in the callback i check whether it sent all the 8K or not if it's not, I call the SocketAsyncEventArgs.SendAsync again with the remaining data untill
I send it all.
Now, when I looked at some SocketAsyncEventArgs code.. I saw that most people don't do so!
and they just clean up when the send complete without checking if it sent all the data or not!
also when I looked at Microsoft's example, they were saying the ONE call to SocketAsyncEventArgs.SendAsync guarantees that all the data will be sent.
I mean I tested it myself and NO all data will not be sent in one call to SocketAsyncEventArgs.SendAsync.
What I'm doing wrong ?
thanks in advance.
Edit:
Here is the code which doesn't send all data(exactly like microsoft's)
the SendAsyncComplete will be called when the socket sends for example 1Kb of data
not all 8K!
public virtual void Send(byte[] packet, int offset, int length)
{
if (_tcpSock != null && _tcpSock.Connected)
{
var args = SocketHelpers.AcquireSocketArg();
if (args != null)
{
args.Completed += SendAsyncComplete;
args.SetBuffer(packet, offset, length);
args.UserToken = this;
var willRaiseEvent = _tcpSock.SendAsync(args);
if (!willRaiseEvent)
{
ProcessSend(args);
}
unchecked
{
_bytesSent += (uint)length;
}
Interlocked.Add(ref _totalBytesSent, length);
}
else
{
log.Error("Client {0}'s SocketArgs are null", this);
}
}
}
private static void ProcessSend(SocketAsyncEventArgs args)
{
args.Completed -= SendAsyncComplete;
SocketHelpers.ReleaseSocketArg(args);
}
private static void SendAsyncComplete(object sender, SocketAsyncEventArgs args)
{
ProcessSend(args);
}
There are many things I would change there. As a preamble, read this.
First of all, whenever you sent any data on the socket, you must process that event: either you stop the whole send process, or you issue another socket send operation to send the remaining data.
So it makes sense to have 3 methods, like this:
// This is the Send() to be used by your class' clients
1) public void Send(byte[] buffer);
This method will take care to apply any data formatting that you need, create (retrieve) a SocketAsyncEventArgs object, set the token to hold your buffer, and call the next method bellow:
2) private void Send(SocketAsyncEventArgs e);
This one actually calls
Socket.SendAsync(SocketAsyncEventArgs e)
and copies the contents from the token (the buffer, remember?) to the SAEA object. Now that is why because method number (2) might be called several times to send the remaining data that couldn't be sent in one operation by the socket. So here you copy the remaining data from the token to the SAEA buffer.
3) private void ProcessSent(SocketAsyncEventArgs e);
This last method will examine the data that has been sent by the socket. If all the data has been sent, the SAEA object will be released. If not, method (2) will be called again for the rest of the data. In order to keep track of sent data, you use SAEA.BytesTransferred. You should add this value to a value stored in the custom token I advise you to create (so do not use "this" as a token).
This is where you also check for SocketError on the SAEA parameter.
This last method will be called in two places:
in the 2nd method, like this:
// Attempt to send data in an asynchronous fashion
bool isAsync = this.Socket.SendAsync(e);
// Something went wrong and we didn't send the data async
if (!isAsync)
this.ProcessSent(e);
This bit is important any many people missed it even when using the more traditional Begin/EndXXX pattern (in that case, via IAsyncResult). If you don't place it, once in a while (quite rare), a StackOverflow exception will pop out of nowhere and will keep you puzzled for long time.
in the Completed event handler:
private void Completed(object sender, SocketAsyncEventArgs e)
{
// What type of operation did just completed?
switch (e.LastOperation)
{
case SocketAsyncOperation.Send:
{
ProcessSent(e);
break;
}
}
}
The tricky thing is to use one SocketAsyncEventArgs object per 1st Send(byte[]) operation, and release it in 3rd operation, if all data has been sent.
For that, you must create a custom token (class or immutable struct) to place in SocketAsyncEventArgs.UserToken inside the 1st method, and then keep track of how much data you have transferred on each Socket.SendAsync() operation.
When you are reading the article provided in the beginning, notice how the writer reuses the same SAEA object when at the end of Send() operation proceeds with a Receive() operation, if all the data has been sent. That is because his protocol is: each one of the parties (server and client) talk to each other in turns.
Now, should multiple calls to the 1st Send() method occur at the same time, there is no rule in which order they will be handled by the OS. If that is likely to happen and message order is important, and since any call to Send(byte[]) from an "outside entity" result in a Socket.SendAsync(), I suggest that the 1st method actually writes down the received bytes in an internal buffer. As long as this buffer is not empty, you keep sending this data internally. Think of it like a producer-consumer scenario, where the "outside entity" is the producer and the internal send op is the consumer. I prefer an optimistic concurrency scenario here.
The documentation on the matter is rather shallow, with the exception of this article, and even in this case, when you start implementing your own application, some things turn out to be different. This SocketAsyncEventArgs model is arguably a bit counter-intuitive.
Let me know if you need more help, I had my times of struggle with this a while ago when I developed my own library.
Edit:
If I were you, I would move
unchecked
{
_bytesSent += (uint)length;
}
Interlocked.Add(ref _totalBytesSent, length);
to your ProcessSend(SAEA), and use args.BytesTransferred instead of "length".

C# Socket.Receive message length

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

Categories