2016-10-26 5 views
2

Я запускаю NetCore в Windows 10, у меня есть две программы - сервер и клиент, которые я запускаю как локально.Объяснить странное поведение C# TcpClient/TcpListener на NetCore

Последовательность выполнения заключается в следующем:

  • Run сервер - есть петля для обработки клиентов
  • Запуск клиент - клиент делает свою работу и процесс завершается
  • Run клиент во второй раз

Эта последовательность производит следующий вывод с сервера, используя этот код:

Waiting for client. 
Reading message. 
Incoming message: This message is longer than what 
Sending message. 
Closing connection. 
Waiting for client. 
Reading message. 
Incoming message: This message is longer than what 
Sending message. 
Closing connection. 
Waiting for client. 

и после выхода из клиента:

Connecting to server. 
Sending message. 
Reading message. 
Incoming message: Thank you! 


Connecting to server. 
Sending message. 
Reading message. 

Unhandled Exception: System.AggregateException: One or more errors occurred. (Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.) ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host 

Другими словами - вторая попытка запуска клиента заканчивается исключением. Это странная часть.

Вот мой минимальный образец кода для воспроизведения проблемы.

Серверный код:

public static async Task StartServerAsync() 
{ 
    TcpListener listener = new TcpListener(IPAddress.Any, 1234); 
    listener.Server.NoDelay = true; 
    listener.Server.LingerState = new LingerOption(true, 0); 
    listener.Start(); 

    while (true) 
    { 
    Console.WriteLine("Waiting for client."); 
    using (TcpClient client = await listener.AcceptTcpClientAsync()) 
    { 
     client.NoDelay = true; 
     client.LingerState = new LingerOption(true, 0); 

     Console.WriteLine("Reading message."); 
     using (NetworkStream stream = client.GetStream()) 
     { 
     byte[] buffer = new byte[32]; 
     int len = await stream.ReadAsync(buffer, 0, buffer.Length); 
     string incomingMessage = Encoding.UTF8.GetString(buffer, 0, len); 
     Console.WriteLine("Incoming message: {0}", incomingMessage); 


     Console.WriteLine("Sending message."); 
     byte[] message = Encoding.UTF8.GetBytes("Thank you!"); 
     await stream.WriteAsync(message, 0, message.Length); 
     Console.WriteLine("Closing connection."); 
     } 
    } 
    } 
} 

код клиента:

public static async Task StartClientAsync() 
{ 
    using (TcpClient client = new TcpClient()) 
    { 
    client.NoDelay = true; 
    client.LingerState = new LingerOption(true, 0); 

    Console.WriteLine("Connecting to server."); 
    await client.ConnectAsync("127.0.0.1", 1234); 

    Console.WriteLine("Sending message."); 
    using (NetworkStream stream = client.GetStream()) 
    { 
     byte[] buffer = Encoding.UTF8.GetBytes("This message is longer than what the server is willing to read."); 
     await stream.WriteAsync(buffer, 0, buffer.Length); 

     Console.WriteLine("Reading message."); 
     int len = await stream.ReadAsync(buffer, 0, buffer.Length); 
     string message = Encoding.UTF8.GetString(buffer, 0, len); 

     Console.WriteLine("Incoming message: {0}", message); 
    } 
    } 
} 

Как ни странно, если мы изменим сообщение клиента от

"This message is longer than what the server is willing to read." 

в

"This message is short." 

оба экземпляра клиента завершают без сбоев и выдают тот же результат.

Обратите внимание, что если я опустил все три строки, где установлен LingerState, это не имеет значения. И нет никакой разницы в поведении, если я изложу LingerState к

new LingerState(true, 5)

И нет никакой разницы, если я изложу NODELAY ложь. Другими словами, установка любых значений для LingerState и NoDelay с обеих сторон, похоже, не влияет на сбой. Единственный способ предотвратить крах - прочитать весь ввод от клиента на стороне сервера.

Это странно, и мне интересно, может ли кто-нибудь объяснить это. Я не уверен, что он относится и к .NET Framework, но тестируется только с NetCore 1.0.0 и Windows 10.

ответ

1

Это окончательно ответили на GitHub командой .NET Core.

Длинный ответ - смотри последний комментарий о https://github.com/dotnet/corefx/issues/13114

Короткий ответ -

поведение, как ожидается, и по-дизайн.

-1

Я взял ваши фрагменты кода и создал фактический runable demo solution, все еще находящийся в неотвеченном состоянии.

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

byte[] buffer = new byte[32]; 
int len = await stream.ReadAsync(buffer, 0, buffer.Length); 
string incomingMessage = Encoding.UTF8.GetString(buffer, 0, len); 
Console.WriteLine("Incoming message: {0}", incomingMessage); 

с:

StringBuilder sb = new StringBuilder(); 
byte[] buffer = new byte[32]; 
int len; 
do 
{ 
    len = await stream.ReadAsync(buffer, 0, buffer.Length); 
    sb.Append(Encoding.UTF8.GetString(buffer, 0, len)); 
} while (len == buffer.Length); 
Console.WriteLine("Incoming message: {0}", sb.ToString()); 

Просто взгляните на историю the demo solution, и вы увидите все изменения минимальны.

+0

Извините, но уже написано в вопросе, что «Единственный способ предотвратить крах - прочитать весь ввод от клиента на стороне сервера». Сам вопрос заключается в том, почему он сбой, когда весь сообщение не читается. – Wapac

Смежные вопросы