Как правильно реализовать линейный сетевой рабочий класс?
Я создаю 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 ответа
Если вы прерываете поток слушателя, то это плохо. Но если приложение закрывается (или если вы закрываете базовый сокет), тогда все будет в порядке.
Перечислите большинство других, я не рекомендую опрашивать. Я бы предложил решение, которое использует асинхронное чтение и запись, хотя его гораздо сложнее программировать.
Старый вопрос, вопрос, должно быть, давно решен. Во всяком случае, это очень похоже на еще более старый вопрос C# - Каков хороший метод для обработки потоковых потоков ввода-вывода на основе линии? - Переполнение стека
Если это так, ответ должен предоставить часть решения. По сути, это класс, который обеспечивает Process(byte[])
метод, возвращающий IEnumerable<string>
, который сохраняет состояние частичного содержимого, еще не сформировав полную строку.
Использование этого позволит разделить проблемы. Просто прочитайте данные как они есть. Каждый раз, когда фрагмент читается, вводите его в этот метод, вы получаете строки, когда они полностью сформированы.
Вот кое-что, чтобы вы пошли.
Обратите внимание, что это не очень эффективно использовать 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 {};
}
Также обратите внимание, что я не обрабатывал никаких исключений в этом коде, что является обязательным. Любые необработанные исключения в асинхронных методах прекратят работу вашего приложения.