@simply_user

Нужно ли проверять полученные данные и отсылать уведомление о их получении при использовании TCP?

Здравствуйте!
Пишу программу для обмена файлами на языке C# используя протокол TCP и сокеты. Знаю, что этот протокол обеспечивает надёжную и упорядоченную доставку данных. Но можно ли полагаться на него полностью, в плане того, что полученные данные не содержат ошибок возникших при передаче? Вот цитата из Вики:
Хотя протокол осуществляет проверку контрольной суммы по каждому сегменту, используемый алгоритм считается слабым. (...) В общем случае распределенным сетевым приложениям рекомендуется использовать дополнительные программные средства для гарантирования целостности передаваемой информации.

Если при передаче действительно возможны ошибки, то вот как я предполагаю их обойти. В моей программе есть класс NetMessage, инкапсулирующий логику отправки и получения данных. Он содержит экземпляр структуры Header - заголовок, в самом простом случае это длина данных и код команды; и, собственно, сами данные - байтовый массив. Вот так происходит отправка данных:
public async Task ReceiveAsync()
        {
            await header.ReceiveAsync(netHelper); //Получаю заголовок.

            if (header.LengthData != -1)
                buffer = await netHelper.ReceiveAsync(header.LengthData); //Если в сообщении есть данные, то получаю их.
        }

Если предположить, что при передаче возможны ошибки, то и после получения заголовка, и после получения данных необходимо отсылать уведомления о том, что данные получены успешно или нет. Выглядеть это может примерно так:
public async Task ReceiveAsync()
        {
            const byte SUCCESS = 1;
            const byte FAILURE = 2;

            if (await header.ReceiveAsync(netHelper))
                await netHelper.SendAsync(new byte[1] { SUCCESS });
            else
                await netHelper.SendAsync(new byte[1] { FAILURE });

            if (header.LengthData != -1)
            {
                buffer = await netHelper.ReceiveAsync(header.LengthData);

                if (CheckHash(buffer, header.Hash))
                    await netHelper.SendAsync(new byte[1] { SUCCESS });
                else
                    await netHelper.SendAsync(new byte[1] { FAILURE });
            }
        }

Выглядит неплохо, но только теперь на каждую отправку данных кроме двух вызовов метода ReceiveAsync для отправки данных, ещё добавятся два вызова метода SendAsync для отправки уведомлений, и соответственно, два вызова ReceiveAsync на принимающей стороне для их получения в аналогичном методе SendAsync класса NetMessage. Эти лишние вызовы повлияют и на скорость передачи данных и увеличат нагрузку на канал связи, особенно если их будет много (при передаче большого файла, например. Он будет разделён на части, допустим по 8 кб, и каждая эта часть будет передана с помощью NetMessage).

Вопрос в том, нужно ли это всё делать? Действительно ли возможны ошибки? И если возможны, то нужно использовать тот подход, который я предложил, или можно сделать иначе и сэкономить на сетевых операциях?
Спасибо за внимание.
  • Вопрос задан
  • 235 просмотров
Решения вопроса 3
Zifix
@Zifix
Barbatum
Весь вопрос в критичности — если от битых данных может произойти авария — то проверять еще раз, а если постить котиков, то заморачиваться не нужно.
Ответ написан
Комментировать
@none7
В нынешнем Интернете это вероятно не нужно. В Ethernet и оптоволокне шумы приводящие к ошибкам, возможны только при грубом нарушении правил строительства сети. В Wi-Fi, xDSL, сотовых сетях обеспечивают больший контроль целостности на канальном уровне. В IPv6 по этому поводу даже чексумму убрали, осталась только в TCP и UDP. Таких ошибок было много во времена Dial-Up, где контроль целостности на канальном уровне был явно недостаточен.
Ответ написан
Комментировать
TCP защищает от случайных повреждений, для этого к пакету добавляется 32-битная чексума. Вероятность повреждения пакета вообще не очень высока, т.к. чексумы есть еще и на канальном уровне, случайное повреждение такое, что сойдется чексума исключительно маловероятно. Но чексума защищает не от всех повреждений данных и точно не защищает от умышленных. Например если в каждом октете занулять какой-либо бит, то чексума сойдется. Дальше все зависит от ваших потребностей. Но добавлять свою чексуму большого смысла точно нет, если вы хотите более надежно защищать данные - защищайте их криптографически. Вы можете это делать пустив свой протокол поверх SSL/TLS, совсем не обязательно городить защиту внутри протокола.

Подтверждение доставки данных на уровне протокола хотя бы в виде ответа на запрос требуется в любом случае. Если вы откроете сокет, установите соединение, отправите данные и закроете сокет - вы не будете знать дошли они или нет, особенно при работе с асинхронными сокетами.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы