0

I am trying to create an asynchronous tcp server which can handle multiple concurrent connections. Then, I have read through the example shown in MSDN:

https://msdn.microsoft.com/en-us/library/fx6588te.aspx

But it happpened that I change the asynchronous receive in AcceptCallback function to synchronous receive, so now my AcceptCallback function become:

private void AcceptCallback(IAsyncResult ar)
{
    allDone.Set();

    Socket listener = (Socket)ar.AsyncState;
    Socket handler = listener.EndAccept(ar);

    // Sync receive
    byte[] buff = new byte[1024];
    handler.Receive(buff);
    ConsumeMessage(handler, buff);
 }

Then, my tcp server is not able to handle multiple concurrent connections anymore. If there are two concurrent connections, it can only connect one client, and the other one still in connection queue. After looking around, I found this article:

Asynchronous server socket multiple clients

Groo mentioned that: "To allow mulitple concurrent connections, you need to create a receive socket using handler.BeginReceive, and then call listener.BeginAccept to start listening to new clients."

Can anyone explain why we need to use AsyncReceive here to be able to handle multiple connections? If I want to use SyncReceive, is there any way to make it still able to handle multiple connections?

Community
  • 1
  • 1
OhMyGosh
  • 1,579
  • 4
  • 18
  • 31
  • 2
    Why are you using ancient, low-level technologies? Writing a TCP server is *not* easy. Writing an asynchronous TCP server is *not* easy. Writing an asynchronous TCP server that scales, while maintaining reasonable GC patterns is *not* easy. Why not find a good network library and go with that instead? The MSDN sample is *very* bare-bones, it's far from anything workable in a production environment. Also, why *would* you want to use synchronous `Receive`, when the rest is asynchronous? – Luaan Mar 24 '15 at 07:48
  • 1
    What exactly are you trying to do? Is there any reason you aren't using something like WCF or IIS or SignalR or MSMQ/RabbitMQ? Why do you need a Tcp connection? – Aron Mar 24 '15 at 07:57
  • 1
    And of course, your sample is already showing a ton of misunderstandings of how TCP works. You can't just do a single `Receive` and expect you will get the whole message (and just the one message; TCP doesn't work with messages, it works with streams) - not to mention that you completely ignore the return value of `Receive`, so you have no idea how much data you actually received, or if the remote end initiated a graceful shutdown of the connection. – Luaan Mar 24 '15 at 08:13
  • All that said, I cannot reproduce the error you're seeing - I can still process many clients concurrently. If I take the MSDN sample and change `BeginReceive` to a synchronous `Receive`, it still works fine. You must have changed something else as well. – Luaan Mar 24 '15 at 08:25
  • Hi @Luaan, I didn't change anything except the BeginReceive to Receive function. But I guess the way I process received message is not correct (cause I assumed whole message comes at same time). Let me check my consume message function again, let you know the result assap. Thanks! – OhMyGosh Mar 24 '15 at 08:29

2 Answers2

1

Any thread can only do one thing at a time. By default, the Receive call blocks until data is available, which prevents any other code (such as accepting new connections) from running on that thread.

You could of course create a thread for each separate connection, on which you can receive data synchronously, but in the end you'd be emulating how BeginReceive and have the overhead of having to manage these threads yourself.

There are some alternatives which allow synchronous processing, such as non-blocking sockets and using Socket.Select(), but using those is not recommended (non-blocking sockets require polling, and Socket.Select() is tricky to get right).

C.Evenhuis
  • 25,996
  • 2
  • 58
  • 72
  • 1
    Actually, `AcceptCallback` executes on a fresh threadpool thread, so it *is* a different thread (from the one that started the original listener, that is). – Luaan Mar 24 '15 at 08:09
0

BeginAccept only accepts a single incoming connection. If you want to keep accepting new incoming connections, you have to call BeginAccept again in your AcceptCallback.

Having said that, I would suggest investigating high-level communication libraries or frameworks like WCF. Doing scalable low-level network programming is very tricky to do right.

Tommy Carlier
  • 7,951
  • 3
  • 26
  • 43
  • 1
    Actually, the MSDN sample calls `BeginAccept` in a loop on a different thread - the `allDone.Set();` signals that thread to do another `BeginAccept`. But yeah, I got caught on that as well. MSDN samples are sometimes quite crazy. – Luaan Mar 24 '15 at 08:24