StreamWriter не будет сбрасываться в NetworkStream

Использование StreamWriter для записи в NetworkStream и StreamReader для чтения ответа. Приложение отправляет команды и читает ответы на сервер новостей.

Упрощенный код (без обработки ошибок и т. д.):

tcpClient = new TcpClient();
tcpClient.Connect(Name, Port);

networkStream = tcpClient.GetStream();
serverReader = new StreamReader(networkStream, Encoding.Default);
serverWriter = new StreamWriter(networkStream, Encoding.ASCII) {
                     AutoFlush = true
                   };

// reads the server's response to the connect:  "200 news.newsserver.com"
// commenting out these lines doesn't solve the problem
while (serverReader.Peek() > -1) {
    serverReader.ReadLine();
}

serverWriter.WriteLine("authinfo user username");

// expect response "381 more authentication required", but code just blocks
string response = serverReader.ReadLine();

Код блокируется в этой последней строке, предположительно ожидая, пока сетевой поток отправит ответ.

Я могу избежать зависания приложения, установив цикл тайм-аута с помощью serverReader.Peek(), но я всегда буду тайм-аут; Я никогда не получаю ответа.

Если я напрямую подключаюсь к серверу и порту через telnet и ввожу команды, я получаю немедленный ответ.

Если я вызываю serverWriter.Flush() явно, вместо использования свойства AutoFlush, я все равно блокирую и никогда не получаю ответа.

Любые идеи, почему я не получаю ответа от сервера, используя этот подход?

Спасибо!

Решено:

Приведенный выше код действительно работает для меня, поэтому я вернулся и доработал этот код до кода, который не работал.

В коде, который зависает, я все еще использовал цикл тайм-аута с serverReader.Peek(). Peek() всегда возвращает -1, даже если в буфере есть данные для чтения!! Замена цикла Peek() блокирующим вызовом ReadLine() решает мою проблему.

Изначально я установил цикл тайм-аута, потому что приложение многопоточное, и я не хотел блокировать. Мне придется вернуться к этой проблеме и посмотреть, как я могу разрешить синхронизацию потоков без использования Peek().

Всем спасибо, хорошие ответы!


person James King    schedule 11.09.2009    source источник


Ответы (3)


Сомневаюсь, что проблема именно в StreamWriter... но есть простой способ выяснить это. Загрузите WireShark и узнайте, что на самом деле происходит в сети. Это, безусловно, самый простой способ узнать, что происходит.

person Jon Skeet    schedule 11.09.2009
comment
Это хорошая идея, WireShark — отличная программа, хотя я никогда не думал использовать ее в таком случае. Я отложу это для будущего использования. - person James King; 12.09.2009

Попробуйте "имя пользователя authinfo\r\n". RFC говорит, что командные строки NNTP должны завершаться CR-LF.

person R Ubben    schedule 11.09.2009
comment
Он вызывает WriteLine(), который будет использовать \r\n в Windows. Если это под Mono, то другое дело... - person Jon Skeet; 12.09.2009
comment
Да, не под Моно, но я все равно пробовал, тот же результат. Попробовал эквивалент (отправив пустую команду) в приглашении telnet... программа чтения новостей проигнорировала пустую команду. Полезно знать, что если бы я хотел портировать на Mono, я мог бы без проблем добавить дополнительные строки. - person James King; 12.09.2009
comment
Верно, но TCPClient.GetStream() возвращает NetworkStream, а я не думал, что в NetworkStream есть метод WriteLine. (не уверен, что это имеет смысл, за исключением SMTP, NNTP и т. д.) Поэтому, даже если он позволяет вам вызывать метод, я не уверен, что доверяю ему. Я сделал клиент NNTP много лет назад и помню, что явно добавлял cr/lf. Я не помню, использовал ли я NetworkStream. - person R Ubben; 12.09.2009
comment
@R Уббен: он не вызывает это в потоке - он вызывает это в StreamWriter. - person Jon Skeet; 12.09.2009

Приведенный выше код действительно работает для меня, поэтому я вернулся и доработал этот код до кода, который не работал.

В коде, который зависает, я все еще использовал цикл тайм-аута с serverReader.Peek(). Peek() всегда возвращает -1, даже если в буфере есть данные для чтения!! Замена цикла Peek() блокирующим вызовом ReadLine() решает мою проблему.

Изначально я установил цикл тайм-аута, потому что приложение многопоточное, и я не хотел блокировать. Мне придется вернуться к этой проблеме и посмотреть, как я могу разрешить синхронизацию потоков без использования Peek().

Всем спасибо, хорошие ответы!

person James King    schedule 10.08.2010