2010-08-24 3 views
1
var buffer = new byte[short.MaxValue]; 
var splitString = new string[] {"\r\n"}; 
while (_tcpClient.Connected) 
{ 
    if (!_networkStream.CanRead || !_networkStream.DataAvailable) 
    continue; 

    var bytesRead = _networkStream.Read(buffer, 0, buffer.Length); 
    var stringBuffer = Encoding.ASCII.GetString(buffer, 0, bytesRead); 
    var messages = 
    stringBuffer.Split(splitString, StringSplitOptions.RemoveEmptyEntries); 
    foreach (var message in messages) 
    { 
    if (MessageReceived != null) 
    { 
     MessageReceived(this, new SimpleTextClientEventArgs(message)); 
    } 
    } 
} 

Проблема в том, что даже с буфером размером с Short.MaxValue вы можете фактически заполнить буфер. Когда вы разделяете строку, создаваемую из буфера, последняя строка прерывается, а остальная часть - со следующим чтением.IRC, использующий NetworkStream - заполнение буфера и линия попадают в ловушку

Я думал о создании буфера, достаточно большого для одной строки (которая по RFC2812 составляет 512 символов), извлекая подстроку до первого «\ r \ n», а затем копируя остальную часть данных к началу буфера и используя параметр offset, чтобы читать больше данных на конце данных, которые не были извлечены последней итерацией. Извините, если это было трудно ...

Это лучшее решение, или я пропустил здесь очевидное?

+0

вы уверены, что буфер заполнен, не то, что 'bytesRead' не того же размера, что и количество отправленных байтов? –

+0

Это может быть так, но обычно это не так. – carlsb3rg

ответ

0

Так вот, как я в конечном итоге решить ее:

var buffer = new byte[Resources.MaxBufferSize]; 
var contentLength = 0; 
while (_tcpClient.Connected) 
{ 
    if (!_networkStream.CanRead || !_networkStream.DataAvailable) 
    continue; 

    var bytesRead = _networkStream.Read(buffer, contentLength, buffer.Length - contentLength - 1); 
    contentLength += bytesRead; 
    var message = string.Empty; 
    do 
    { 
    message = ExtractMessage(ref buffer, ref contentLength); 
    if (!String.IsNullOrEmpty(message)) 
    { 
     if (MessageReceived != null) 
     { 
     MessageReceived(this, new SimpleTextClientEventArgs(message)); 
     }       
    } 
    } while (message != string.Empty); 
} 

private string ExtractMessage(ref byte[] buffer, ref int length) 
{ 
    var message = string.Empty; 
    var stringBuffer = Encoding.UTF8.GetString(buffer, 0, length); 
    var lineBreakPosition = stringBuffer.IndexOf(Resources.LineBreak); 
    if (lineBreakPosition > -1) 
    { 
    message = stringBuffer.Substring(0, lineBreakPosition); 
    var tempBuffer = new byte[Resources.MaxBufferSize]; 
    length = length - message.Length - Resources.LineBreak.Length; 
    if (length > 0) 
    { 
     Array.Copy(buffer, lineBreakPosition + Resources.LineBreak.Length, tempBuffer, 0, length); 
     buffer = tempBuffer; 
    } 
    } 
    return message; 
} 
1

Вы имеете дело с TCP/IP, что означает, что вы имеете дело с потоком данных. Вы должны не полагаться на то, как данные поступают с точки зрения того, даст ли один вызов Read вам все данные или нет. В таком случае вы, вероятно, захотите просто продолжать чтение (оно будет блокироваться до тех пор, пока не появятся некоторые данные) и найти преобразование двоичных данных в текстовый буфер. Когда вы видите терминатор строки в текстовом буфере, вы можете уведомить более высокий уровень этого сообщения и удалить его из буфера ... но не предполагайте ничего о том, что происходит после этого сообщения. У вас может быть еще больше данных для чтения.

В качестве примечания, является IRC действительно только ASCII? Если да, то, что, по крайней мере делает вещи немного проще ...

+0

-Это то, что он говорит: - «Конкретный набор символов не задан. Протокол основан на наборе кодов, состоящих из восьми (8) битов, составляющих октет. Каждое сообщение может состоять из любого числа из этих октетов, однако некоторые значения октета используются для управляющих кодов, которые действуют как разделители сообщений ». -Но я знаю, что были недокументированные/нечетко документированные усовершенствования протокола IRC, поэтому я хотел бы сделать вещи максимально надежными. Я думаю, что ваше предложение - это то, что я предлагаю в своем заключительном абзаце. – carlsb3rg

+0

@ carlsb3rg: Да, похоже, что вы думаете по тем же линиям, что и я. Похоже, что протокол полностью сломан, если он не указывает кодировку, хотя ... и, предположив ASCII, вы можете легко потерять важные данные. В частности, похоже, что вы должны просматривать массив байтов для разделителя сообщений, а не декодированного текста. –