0

I have a program that begins itself by listening for connections. I wanted to implement a pattern in which the server would accept a connection, pass that individual connection to a user class for processing: future packet reception, and handling of the data.

I ran into trouble with the synchronous pattern before I found out that asynchronous use of the Socket class isn't scary. But then I ran into more trouble. It seemed that, in a while (true) loop, since BeginAccept() is asynchronous, the program would constantly move through this loop and eventually run into an OutOfMemoryException. I needed something to listen for a connection, and immediately hand off responsibility of that connection to some other class.

So I read Microsoft's example and found out about ManualResetEvent. I could actually specify when I was ready for the loop to begin listening again! But after reading some questions here on Stack Overflow, I have become confused.

My worry is that even though I have asynchronously accepted a connection, the entire program will block while it's trying to listen for a new connection upon re-entering the loop. This isn't ideal if I'm handling multiple users.

I'm very new to the world of asynchronous I/O, so I would appreciate even the angriest of comments about my vocabulary or a misuse of a phrase.

Code:

static void Main(string[] args)
{
    MainSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
    MainSocket.Bind(new IPEndPoint(IPAddress.Parse("192.168.1.74"), 1626));
    MainSocket.Listen(10);

    while (true)
    {
        Ready.Reset();
        AcceptCallback = new AsyncCallback(ConnectionAccepted);
        MainSocket.BeginAccept(AcceptCallback, MainSocket);
        Ready.WaitOne();
    }
}

static void ConnectionAccepted(IAsyncResult IAr)
{
    Ready.Set();
    Connection UserConnection = new Connection(MainSocket.EndAccept(IAr));
}
Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
Fuselight
  • 554
  • 6
  • 17

1 Answers1

1

The Microsoft example, in which they use the old-style WaitHandle based events, will work but frankly it is a very odd and awkward way to implement asynchronous code. I get the feeling that the events are there in the example mainly as a way of artificially synchronizing the main thread so it has something to do. But it's not really the right approach.

One option is to just not even accept sockets asynchronously. Instead, use the asynchronous I/O for when the socket is connected and use a synchronous loop in the main thread to accept sockets. This winds up being pretty much exactly what the Microsoft sample does anyway, but keeps all of the accept logic in the main thread instead of switching back and forth between the main thread (which starts the accept operation) and some IOCP thread that handles the completion.

Another option is to just give the main thread something else to do. For a simple example, this could be simply waiting for some user input to signal that the program should shut down. Of course, in a real program the main thread could be something useful (e.g. handling the message loop in a GUI program).

If the main thread is given something else to do, then you can use the asynchronous BeginAccept() in the way it was intended: you call the method to start the accept operation, and then don't call it again until that operation completes. The initial call happens when you initialize your server, but all subsequent calls happen in the completion callback.

In that case, your completion callback method looks more like this:

static void ConnectionAccepted(IAsyncResult IAr)
{
    Connection UserConnection = new Connection(MainSocket.EndAccept(IAr));
    MainSocket.BeginAccept(ConnectionAccepted, MainSocket);
}

That is, you simply call the BeginAccept() method in the completion callback itself. (Note that there's no need to create the AsyncCallback object explicitly; the compiler will implicitly convert the method name to the correct delegate type instance on your behalf).

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • I like option one best, but i can't have Socket.Accept(); holding up the entire program. Will it not? – Fuselight May 16 '15 at 12:37
  • Using the synchronous `Accept()` will behave exactly as the event-based implementation Microsoft's shown, and yes that means it will cause _that thread_ to block. If you have something else you want that thread to be doing, then use `BeginAccept()` as I described; i.e. don't use a blocking loop as in the Microsoft example, but instead just call `BeginAccept()` when you want your server to start, let the main thread go on to do whatever it is you want it to do, and call `BeginAccept()` again in your `ConnectionAccepted()` method. – Peter Duniho May 16 '15 at 15:56
  • That causes the application to exit. How do I remedy that? – Fuselight May 17 '15 at 03:53
  • That doesn't make any sense. You expressed a concern of "holding up the entire program" (by which I assume you simply mean the main thread...one thread can't in and of itself hold up any other thread). For it to matter if it holds up the main thread, that would mean that the main thread was supposed to be doing something else. But if it's doing something else, the program won't exit. Your two concerns are mutually exclusive. If you don't want the main thread to exit and have nothing better for it to do, just use the synchronous `Accept()`. – Peter Duniho May 17 '15 at 03:56
  • Your suggestion won't work. When I use that, only one person can connect. I need multiple people to connect, and they need to be able to interact with the game as individuals for as long as the socket is active. – Fuselight May 17 '15 at 15:33
  • _"Your suggestion won't work"_ -- sorry, but you are simply wrong about that. As I said before: **if you have something else for the main thread of the program to do, then use `BeginConnect()`**. Your complaint that using `BeginConnect()` allows the program to exit isn't true if you have something else for the program to do. The program will do that thing instead of exiting. – Peter Duniho May 17 '15 at 19:59
  • Frankly, much of the problem here is that you haven't provided anything close to [a realistic code example](http://stackoverflow.com/help/mcve) which means that the only discussion that can occur is abstract, and you don't know enough about the API you're asking about to understand answers presented in the abstract. – Peter Duniho May 17 '15 at 20:00
  • `BeginAccept()` does not allow for the continuation of the program; it immediately exits! What's so hard for you to understand? `BeginAccept()` doesn't stall the program until a connection is made like `Accept()` does. You shouldn't have to depend on me providing code. My goal is simple: take connections in as they come without halting server-side processing of gameplay. [Code](http://pastebin.com/xmyCgDLR) – Fuselight May 17 '15 at 22:49
  • _"it immediately exits"_ -- Please pay attention. This is the last time I will try to explain this to you: the reason your program "immediately exits" is that you haven't given the main thread anything else to do. So, duh. It exits. In a **REAL PROGRAM** (which you haven't provided), your main thread will presumably be busy **doing something**. It **won't** exit. _"without halting server-side processing of gameplay"_ -- and where is that code running? as long as it's running in a foreground thread, your program **WON'T EXIT**. Period. – Peter Duniho May 17 '15 at 23:05