Лучший способ указать, что все данные были получены по сети?
У меня есть клиент-серверное приложение, которое работает довольно хорошо, но в нем отсутствует одна важная часть поведения, чтобы сделать его более надежным.
Прямо сейчас, это далеко не "сильный" с точки зрения сетевых возможностей. Я пытаюсь получить его там, и исследования привели меня к убеждению, что мне нужен какой-то протокол, чтобы гарантировать, что никакие данные не будут потеряны во время передачи по сети.
Я слышал о нескольких методах. Я думаю, что лучше всего подойдет для наших ситуаций использование терминатора, что-то вроде <EOF>
тег. Моя проблема в том, что я не уверен в том, как лучше всего это реализовать.
Вот пара фрагментов кода, которые я буду модифицировать, чтобы включить терминатор после выяснения наилучшего решения.
Клиент:
TcpClient client = new TcpClient();
client.Connect(hostname, portNo);
using (var stream = client.GetStream())
{
//send request
stream.Write(data, 0, data.Length);
stream.Flush();
//read server response
if (stream.CanRead)
{
byte[] buffer = new byte[1024];
string response = "";
int bytesRead = 0;
do
{
bytesRead = stream.Read(buffer, 0, buffer.Length);
response += Encoding.ASCII.GetString(buffer, 0, bytesRead);
} //trying to replace 'DataAvailable', it doesn't work well
while (stream.DataAvailable);
}
}
Обратите внимание, что я пытаюсь заменить stream.DataAvailable
метод проверки большего количества данных в потоке. Это вызывает проблемы.
Сервер:
var listener = new TcpListener(IPAddress.Any, portNo);
listener.Start();
var client = listener.AcceptTcpClient();
using (var stream = client.GetStream())
{
var ms = new System.IO.MemoryStream();
byte[] buffer = new byte[4096];
int bytesRead = 0;
do
{
bytesRead = stream.Read(buffer, 0, buffer.Length);
ms.Write(buffer, 0, bytesRead);
} //also trying to replace this 'stream.DataAvailable'
while (stream.DataAvailable);
ms.Position = 0;
string requestString = Encoding.UTF8.GetString(ms.ToArray());
ms.Position = 0;
/*
process request and create 'response'
*/
byte[] responseBytes = Encoding.UTF8.GetBytes(response);
stream.Write(responseBytes, 0, responseBytes.Length);
stream.Flush();
}
Итак, учитывая эти два примера кода, как я могу изменить их так, чтобы они включали и проверяли какой-либо терминатор данных, который указывает, что можно безопасно прекратить чтение данных?
2 ответа
В итоге мы использовали индикатор EOS (конец потока) на обоих концах нашего проекта. Я не буду публиковать полный пример кода, но вот небольшой фрагмент того, как он работает:
stream.Write(data, 0, data.Length);
stream.WriteByte(Convert.ToByte(ConsoleKey.Escape));
stream.Flush();
На принимающей стороне этого потока цикл считывает данные побайтно. Как только он получает ConsoleKey.Escape
байт, это завершает цикл. Оно работает!
Вы можете положиться на TCP, передающий все данные до FIN. Проблема с вашим кодом в том, что available() не является допустимым тестом для завершения потока. Это тест для данных, доступных сейчас.
Таким образом, вы преждевременно завершаете цикл чтения, и, таким образом, пропускаете данные в получателе и получаете сброс в отправителе.
Вы должны просто заблокировать метод Read(), пока не получите индикацию EOS от API, что бы это ни было в C#.
Вам не нужен ваш собственный дополнительный индикатор EOS.