哪种线程方法最适合主服务器网络?

时间:2011-03-19 17:21:27

标签: c# multithreading sockets networking asynchronous

在这个问题之后,我决定从一个更具体的方法开始,而不是抛出我无法整合的理论和概念信息:Are Socket.*Async methods threaded?

重点是在优化服务器的同时保持所有客户端的流动性。这意味着以某种方式异步,以便不阻止主要操作。

以下是我提出的一些方法。 “过程”是一种处理从客户端接收的数据的假设方法。考虑到这通常需要1-5毫秒,对于罕见的数据库调用可能需要500-2000毫秒。

使用Socket。*异步和循环

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);
            }
        }
    }
}

优点:

  • 仅限一个帖子!
  • 管理起来相当简单。

缺点:

  • 虽然(true):CPU密集度过高?
  • 由于所有操作都相互关联,因此会降低所有连接的客户端的速度。

我认为这是一个好的开始,也许是我最好的解决方案。如果我可以在一个线程池中混合,将accept,receive和process分成不同的线程,我们可能会去某个地方。

使用Socket.Begin * / End *和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);
    }
}

优点:

  • 完全异步,客户端从未因其他操作而放慢速度。
  • 使用ManualResetEvents可以很好地停止服务器。

缺点:

  • 太多线程,每个客户1个!
  • 由于所有线程都被重置事件阻止,因此浪费了处理时间?

最后是一种简化此解决方案的方法,无需手动重置事件。

使用屏蔽调用和手动线程

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();
    }
}

使用线程池

我实际上不知道如何使用它,有人可以给我一个例子吗?

建议

可能解决方案不是本文中的任何一个。你会怎么处理它?<​​/ p>

正如你所看到的,我使用过。*异步方法,Begin * / End *方法和阻塞方法,但都有相对较大的缺点。

提前谢谢:)我迫不及待地想看看S / O的代码示例。

1 个答案:

答案 0 :(得分:3)

您没有正确使用Begin/End。没有必要等待事件,让框架处理这个。请注意,在the MSDN example中,接收循环不使用显式事件,尽管接受循环的作用是为了便于控制和说明。

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);
    }
}