Как правильно реализовать линейный сетевой рабочий класс? - PullRequest
1 голос
/ 27 апреля 2011

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

Моим первым методом было создать StreamReader с именем _reader, используя TcpClient.GetStream() в качестве основы, и простоделает _reader.ReadLine().Преимущество этого заключается в том, что фоновый поток прослушивания автоматически останавливается до получения строки.Проблема в том, что когда я отключаюсь и останавливаю поток слушателя (или приложение просто завершается), я не уверен, насколько хорошо для сбора мусора и т. Д., Что поток с ожидающим ReadLine() просто убивается.Может ли поток стать осиротевшим и каким-то образом остаться в фоновом краже ресурсов?

Мой второй метод - создать NetworkStream с именем _reader на основе того же TcpClient.GetStream() и создать цикл, проверяющий _reader.DataAvailable и continue цикл while, если он ложный, и в противном случае помещает байты в StringBuilder, который мгновенно проверяется на \r\n и извлекает все целые строки.Это дает тот же эффект, что и ReadLine при извлечении данных, но мне не нравится постоянный цикл _reader.DataAvailable.На моем процессоре Core i5 он не требует никакого процессора, но на моем гораздо более мощном ноутбуке i9 он постоянно крадет виртуальный процессор.Thread.Sleep(1) "решает" это, но выглядит как грязное исправление, и многочисленные статьи в Интернете классифицируют это как запах кода.

Так, каково было бы правильное решение?

Ответы [ 3 ]

1 голос
/ 27 апреля 2011

Если вы прерываете поток слушателя, тогда это плохо.Но если приложение закрывается (или если вы закрываете базовый сокет), то все будет в порядке.

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

0 голосов
/ 19 августа 2015

Старый вопрос, проблема, должно быть, давно решена. Во всяком случае, это очень похоже на еще более старый вопрос c # - Что является хорошим методом для обработки потоков ввода-вывода на основе линии? - переполнение стека

Если это так, то ответ должен содержать часть решения. По сути, это класс, который предоставляет метод Process(byte[]), возвращающий IEnumerable<string>, который сохраняет состояние частичного содержимого, еще не формируя полную строку.

Использование этого позволит разделить проблемы. Просто прочитайте данные как они есть. Каждый раз, когда фрагмент читается, вводите его в этот метод, вы получаете строки, когда они полностью сформированы.

0 голосов
/ 29 апреля 2011

Вот что вам нужно.

Обратите внимание, что не очень эффективно использовать string при создании сообщений.Но это делает все намного более читабельным.

public abstract class LineBasedChannel
{   
    Socket _socket;
    string _inbuffer = string.Empty;
    Encoding _encoding;
    byte[] _buffer = new byte[8192];

    public LineBasedChannel(Socket socket)
    {
        _socket = socket;
        _encoding = Encoding.ASCII;
        _sb = new StringBuilder();
    }

    public void Start()
    {
        _socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None,OnRead, null);
    }

    public void OnRead(IAsyncResult res)
    {
        var bytesRead = _socket.EndReceive(res);
        if (bytesRead == 0)
        {
            HandleDisconnect();
            return;
        }

        _inbuffer += _encoding.GetString(_buffer, 0, bytesRead);
        _socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None,OnRead, null);

        while (true)
        {
            int pos = _inbuffer.IndexOf("\r\n");
            if (pos == -1)
                break;

            OnReceivedLine(_inbuffer.SubString(0, pos+2);
            _inbuffer = _inbuffer.Remove(0,pos+1);
        }
    }

    protected abstract void OnReceivedLine(string line);
}

public class IrcTcpChannel : LineBasedChannel
{
    protected override void OnReceivedLine(string line)
    {
        var cmd = new IrcCommand();
        cmd.Channel = line.SubString(x,y);
        cmd.Message = line.SubString(z,a);
        CommandReceived(this, new IrcCommandEventArgs(cmd));
    }

    public event EventHandler<IrcCommandEventArgs> CommandReceived = delegate {};
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...