c # сокет стек нескольких пакетов - PullRequest
1 голос
/ 29 июня 2011

У меня проблема с использованием сокетов в c #.Вот пример.Скажем, я отправляю число 1, а затем сразу же отправляю число 2. Проблема, с которой я иногда сталкиваюсь, заключается в том, что клиент, который должен получить его, получит пакет, содержащий «12».Мне было интересно, есть ли встроенный способ различать пакеты без использования символов или чего-то другого для разделения данных.

Чтобы подвести итог еще раз, я сказал два пакета.Один с номером «1», другой с номером «2».Сервер получает 1 пакет с данными «12».Я не хочу разделять пакеты с символами, такими как ': 1 :: 2:' или чем-то подобным, поскольку я не всегда могу контролировать формат входящих данных.

Любойидеи?

Например, если я сделаю это

client.Send(new byte[1]{'1'}, 1,SocketFlags.None);
client.Send(new byte[1]{'2'}, 1,SocketFlags.None);

, то на стороне сервера

byte[] data = new byte[1024];
client.Receive(data);

данные иногда возвращаются с "12", хотя я делаю два отдельныхпосылает.

Ответы [ 4 ]

5 голосов
/ 29 июня 2011

TCP / IP работает с потоком абстракцией, не пакетом абстракцией.

Когда вы звоните Send, вы не отправка пакета.Вы только добавляете байты в поток.

Когда вы звоните Receive, вы не получаете пакет.Вы получаете только байты из потока.

Итак, вы должны определить, где начинается и заканчивается «сообщение».Существует только три возможных решения:

  1. Каждое сообщение имеет известный размер (например, все сообщения имеют размер 42 байта).В этом случае вы просто сохраняете Receive данные до тех пор, пока не получите столько байтов.
  2. Используйте разделители, о которых вы уже знаете.
  3. Используйте префикс длины (например, префикс)каждое сообщение с 4-байтовой длиной сообщения).В этом случае вы сохраняете Receive префикс длины до его прибытия, декодируете его, чтобы получить фактическую длину сообщения, а затем продолжаете Receive самого сообщения.

Вы должны сделать сообщение, обрамляющее себя. TCP / IP не может сделать это за вас.

5 голосов
/ 29 июня 2011

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

Размер и масштабирование окна приема TCP @ MSDN

Несмотря на то, что в вашем примере вы наблюдали 1 объединенный блок данных, содержащий 2 байта, на принимающей стороне, можнополучать последовательности размером 1 байт на 1 байт (отправленные вашим отправителем) в зависимости от условий сети, а также множество возможных комбинаций чтений 0, 1 и 2 байта, если вы выполняете неблокирующие чтения.При отладке в типичной не перегруженной локальной сети или настройке обратной связи вы почти никогда не увидите этого, если на стороне отправки нет задержки.На нижних уровнях сетевого стека существуют способы обнаружения передачи по пакетам, но они не используются в типичном программировании TCP и находятся вне области применения.

Если вы переключаетесь на UDP, то каждый пакетбудет получен как он был отправлен, что будет соответствовать вашим ожиданиям.Это может подойти вам лучше, но имейте в виду, что UDP не имеет никаких обещаний доставки, и сетевая маршрутизация может привести к тому, что пакеты будут доставлены не по порядку.

Вы должны изучить вопрос об ограничении ваших данных или найти какой-то другой метод, чтобы определить, когдавы достигли конца единицы данных, определенной вашим приложением, и придерживаетесь протокола TCP.

2 голосов
/ 29 июня 2011

Трудно ответить на ваш вопрос, не зная контекста. Напротив, по умолчанию, TCP / IP обрабатывает управление пакетами для вас автоматически (хотя вы получаете его в потоковом режиме). Однако, когда у вас очень специфическая (плохая?) Реализация, вы можете отправлять несколько потоков по одному сокету одновременно, что делает невозможным обнаружение разницы для TCP / IP более низкого уровня. Тем самым вам очень трудно идентифицировать различные потоки на клиенте. Единственным решением для этого было бы отправить 2 абсолютно уникальных потока (например, поток 1 отправляет только байты ниже 127, а поток 2 отправляет только байты выше или равные 127). Еще раз, это ужасное поведение

1 голос
/ 29 июня 2011

Вы должны поместить в свои разделители TCP-сообщений или использовать количество байтов, чтобы указать, где начинается одно сообщение и начинается другое.

В вашем коде есть еще одна серьезная ошибка. TCP-сокеты могут не дать вам все данные за один вызов. Вы должны зацикливаться на получении данных до тех пор, пока записи вашего конкретного приложения в потоке данных не покажут, что все сообщение было получено. Ваш вызов client.Receive (data) возвращает количество полученных байтов. Вы должны захватить это число.

Аналогичным образом, когда вы отправляете данные, все ваши данные могут быть отправлены не за один вызов. Вы должны зацикливаться на отправке данных до тех пор, пока количество отправленных байтов не станет равным тому, что вы намеревались отправить. Вызов client.Send возвращает фактическое количество отправленных байтов, что может быть не тем, что вы пытались отправить!

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...