Лучший способ указать, что все данные были получены по сети?

У меня есть клиент-серверное приложение, которое работает довольно хорошо, но в нем отсутствует одна важная часть поведения, чтобы сделать его более надежным.

Прямо сейчас, это далеко не "сильный" с точки зрения сетевых возможностей. Я пытаюсь получить его там, и исследования привели меня к убеждению, что мне нужен какой-то протокол, чтобы гарантировать, что никакие данные не будут потеряны во время передачи по сети.

Я слышал о нескольких методах. Я думаю, что лучше всего подойдет для наших ситуаций использование терминатора, что-то вроде <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.

Другие вопросы по тегам