开发者

Which threading method is the most optimal for master server networking?

开发者 https://www.devze.com 2023-02-18 16:30 出处:网络
Following this question, I decided to start with a more concrete approach, rather than being thrown theoretical and conceptual information I cannot integrate: Are Socket.*Async methods threaded?

Following this question, I decided to start with a more concrete approach, rather than being thrown theoretical and conceptual information I cannot integrate: Are Socket.*Async methods threaded?

The point is to keep fluidity for all the clients, while optimizing the server. This means asynchrony in a way or another, in order not to block the main operation.

Here are some methods I came up with. "Process" is an hypothetical method that handles the data received from the client. Consider this could take between 1-5ms usually, perhaps 500-2000ms for rare database calls.

Using Socket.*Async and looping

static void Main()
{
    Socket listener = new Socket(...);
    listener.Bind(new IPEndPoint(IPAddress.Any, 555));

    List<Socket> clients = new List<Socket>();

    SocketAsyncEventArgs e = new SocketAsyncEventArgs();

    while (true)
    {
        if (listener.AcceptAsync(e))
        {
            clients.Add(e.AcceptSocket);
        }

        foreach (Socket client in clients)
        {
            if (client.ReceiveAsync(e))
            {
                Process(e.Buffer);
            }
        }
    }
}

Pros:

  • One thread only!
  • Rather simple to manage.

Cons:

  • While(true): Too CPU intensive?
  • Since all ops are following each other, they slow down all clients connected.

I assume this is somehow a good start, perhaps the best of my solutions. If I could mix in a thread pool, split the accept, the receive, and the process in different threads, we might be going somewhere.

Using Socket.Begin*/End* and ManualResetEvent

static class Server
{
    static Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    static ManualResetEvent acceptDone = new ManualResetEvent(false);

    static void Main()
    {
        listener.Bind(new IPEndPoint(IPAddress.Any, 555));

        while (true)
        {
            acceptDone.Reset();

            listener.BeginAccept(OnAccept, null);

            acceptDone.WaitOne();
        }
    }

    private static void OnAccept(IAsyncResult ar)
    {
        acceptDone.Set();

    开发者_运维技巧    new Receiver(listener.EndAccept(ar));
    }
}

class Receiver
{
    Socket socket;
    byte[] buffer = new byte[1024];
    static ManualResetEvent receiveDone = new ManualResetEvent(false);

    public Receiver(Socket socket)
    {
        this.socket = socket;

        new Thread
        (
            delegate()
            {
                while (true)
                {
                    receiveDone.Reset();

                    socket.BeginReceive(buffer, 0, 1024, SocketFlags.None, OnReceive, null);

                    receiveDone.WaitOne();
                }
            }
        ).Start();
    }

    private void OnReceive(IAsyncResult ar)
    {
        receiveDone.Set();

        int received = socket.EndReceive(ar);
        byte[] toProcess = new byte[received];
        Buffer.BlockCopy(buffer, 0, toProcess, 0, received);
        Process(toProcess);
    }
}

Pros:

  • Fully asynchronous, clients never slowed down by other operations.
  • Using ManualResetEvents allows to stop the server nicely.

Cons:

  • Way too many threads, 1 per client!
  • Wasted processing time since all threads are blocked with reset events?

And finally a way of simplifying this solution, without manual reset events.

Using Blocking Calls and manual threads

static class Server
{
    static Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    static void Main()
    {
        listener.Bind(new IPEndPoint(IPAddress.Any, 555));

        while (true)
        {
            new Receiver(listener.Accept());
        }
    }
}

class Receiver
{
    Socket socket;

    public Receiver(Socket socket)
    {
        this.socket = socket;

        new Thread
        (
            delegate()
            {
                while (true)
                {
                    byte[] buffer = new byte[1024];
                    int received = socket.Receive(buffer);
                    byte[] toProcess = new byte[received];
                    Buffer.BlockCopy(buffer, 0, toProcess, 0, received);
                    Process(toProcess);
                }
            }
        ).Start();
    }
}

Using a Thread Pool

I actually have no idea how to use one, could someone give me an example for it?

Suggestions

Probably that the solution isn't any of the ones in this post. How would you handle it?

As you can see, I used .*Async methods, Begin*/End* methods, and blocking methods, but all have relatively major cons.

Thanks in advance :) I can't wait to see S/O's code examples.


You are not using Begin/End correctly. There is no need to wait on the events, let the framework handle this. Note that in the MSDN example the receive loop doesn't use an explicit event, although the accept loop does for ease of control and exposition.

static class Server
{
    static Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    static void Main()
    {
        listener.Bind(new IPEndPoint(IPAddress.Any, 555));
        listener.BeginAccept(OnAccept, null);

        WaitUntilServerNeedsToShutdown () ;

        // worker threads will die because they are background
    }

    private static void OnAccept(IAsyncResult ar)
    {
        new Receiver(listener.EndAccept(ar));
        listener.BeginAccept(OnAccept, null);
    }
}

class Receiver
{
    Socket socket;
    byte[] buffer = new byte[1024];

    public Receiver(Socket socket)
    {
        this.socket = socket;
        socket.BeginReceive(buffer, 0, 1024, SocketFlags.None, OnReceive, null);
    }

    private void OnReceive(IAsyncResult ar)
    {
        int received = socket.EndReceive(ar);
        byte[] toProcess = new byte[received];
        Buffer.BlockCopy(buffer, 0, toProcess, 0, received);

        // TODO: detect EOF and error conditions
        socket.BeginReceive(buffer, 0, 1024, SocketFlags.None, OnReceive, null);

        // TODO: is it OK to process incomplete data?
        Process(toProcess);
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消