Какой метод потоков наиболее оптимален для работы в сети с главным сервером? - PullRequest
1 голос
/ 19 марта 2011

Следуя этому вопросу, я решил начать с более конкретного подхода, вместо того, чтобы бросать теоретическую и концептуальную информацию, которую я не могу интегрировать: Являются ли Socket. * Асинхронные методы в потоке?

Суть в том, чтобы сохранить текучесть для всех клиентов при оптимизации сервера. Это означает асинхронность тем или иным способом, чтобы не блокировать основную операцию.

Вот некоторые методы, которые я придумал. «Процесс» - это гипотетический метод, который обрабатывает данные, полученные от клиента. Учтите, что обычно это может занять 1-5 мс, возможно, 500-2000 мс для редких вызовов базы данных.

Использование сокета. * Асинхронизация и цикл

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

Плюсы:

  • Только одна тема!
  • Довольно прост в управлении.

Минусы:

  • Хотя (верно): слишком высокая загрузка ЦП?
  • Поскольку все операции следуют друг за другом, они замедляют работу всех подключенных клиентов.

Полагаю, это как-то хорошее начало, возможно, лучшее из моих решений. Если бы я мог смешать в пуле потоков, разделить принятие, получение и процесс в разных потоках, мы могли бы куда-то идти.

Использование 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();
    }
}

Использование пула потоков

На самом деле я понятия не имею, как его использовать, может кто-нибудь дать мне пример для этого?

Предложения

Вероятно, решение не из тех, что приведены в этом посте. Как бы вы справились с этим?

Как видите, я использовал. * Асинхронные методы, методы Begin * / End * и методы блокировки, но все они имеют относительно серьезные недостатки.

Заранее спасибо :) Не могу дождаться, чтобы увидеть примеры кода S / O.

1 Ответ

3 голосов
/ 19 марта 2011

Вы не используете Begin/End правильно.Не нужно ждать событий, пусть фреймворк справится с этим.Обратите внимание, что в примере MSDN цикл приема не использует явное событие, хотя цикл приема используется для простоты управления и экспозиции.

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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...