Как безопасно передавать данные через сокет сервера в другой сокет? - PullRequest
0 голосов
/ 31 мая 2011

Я пишу серверное приложение для iPhone-приложения, которое я разрабатываю.Приложение для iPhone написано на C # (MonoTouch), а сервер тоже на C # (.NET 4.0)

Я использую асинхронные сокеты для сетевого уровня.Сервер позволяет двум или более iPhone («устройствам») соединяться друг с другом и иметь возможность отправлять данные в двух направлениях.В зависимости от входящего сообщения сервер либо обрабатывает само сообщение, либо передает данные на другое устройство (устройства) в той же группе, что и отправляющее устройство.Он может принять это решение, сначала декодируя заголовок пакета и решая, какой это тип пакета.

Это делается путем кадрирования потока таким образом, чтобы первые 8 байтов представляли собой два целых числа, длинузаголовка и длины полезной нагрузки (которая может быть намного больше, чем заголовок).

Сервер считывает (асинхронно) из сокета первые 8 байтов, поэтому он имеет длину двух секций.Затем он снова читает, вплоть до общей длины секции заголовка.

Затем десериализует заголовок и, основываясь на информации внутри, может видеть, следует ли пересылать оставшиеся данные (полезную нагрузку) на другое устройство,или это то, с чем сам сервер должен работать.Если его нужно перенаправить на другое устройство, то следующим шагом будет чтение данных, поступающих в сокет, скажем, 1024 байта, и запись их напрямую с помощью асинхронной отправки через другой сокет, подключенный к устройству-получателю.

Это снижает требования к памяти сервера, поскольку я не загружаю весь пакет в буфер, а затем повторно отправляю его по проводам получателю.

Однако из-заВ силу природы асинхронных сокетов я не гарантирую получение всей полезной нагрузки за одно чтение, поэтому я должен продолжать чтение, пока не получу все байты.В случае ретрансляции на конечный пункт назначения это означает, что я вызываю BeginSend () для каждого фрагмента байтов, которые я получаю от отправителя, и пересылаю этот фрагмент на получателя, по одному фрагменту за раз.

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

Когда я пишу это, мне просто интересно, это так же просто, как просто заблокировать объект сокета ?!Будет ли это правильным вариантом или это может вызвать другие проблемы (например, проблемы с получением данных через заблокированный сокет, который отправляется НАЗАД с удаленного устройства?)

Заранее спасибо!

Ответы [ 3 ]

2 голосов
/ 31 мая 2011

Некоторое время назад я столкнулся с подобным сценарием, у меня больше нет полного решения, но вот что я сделал:

  • Я не использовал синхронизирующие сокеты, решил изучить асинхронные сокеты в C # - забавная поездка
  • Я не разрешаю нескольким потокам совместно использовать один ресурс, если только мне действительно не нужно
  • Мои «пакеты» содержали информацию о размере, индексе и общем количестве пакетов для сообщения
  • Первый байт моего пакета был уникальным, чтобы показать, что это начало сообщения, я использовал 0xAA
  • Последние 2 байта моих пакетов были результатом контрольной суммы CRC-CCITT (ushort)
  • Объекты, которые сделали бит приема, содержали буфер со всеми полученными байтами. Из этого буфера я извлекал «полные» сообщения, когда размер был в порядке, и контрольная сумма соответствовала
  • Единственная «блокировка», которую мне нужно было сделать, была в временном буфере, чтобы я мог безопасно анализировать его содержимое между операциями записи / чтения

Надеюсь, это немного поможет

0 голосов
/ 07 июня 2011

Спасибо за помощь всем, я понял, что самый простой подход - использовать команды синхронной отправки на клиенте или, по крайней мере, команду отправки, которая должна завершиться до отправки следующего элемента.Я обрабатываю это с моей собственной очередью отправки на клиенте, а не с различными частями приложения, просто вызываю send (), когда им нужно что-то отправить.

0 голосов
/ 31 мая 2011

Не уверен, где проблема.Поскольку вы упомянули серверы, я полагаю, что TCP, да?

Телефон должен связать часть вашего PDU с другим телефоном.Он подключается как клиент к серверу на другом телефоне.Сокет-пара установлена.Он отправляет данные в сокет сервера.Пара сокетов уникальна - никакие другие потоки, которые могут происходить между двумя телефонами, не должны прерывать это (конечно, это будет замедлять).

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

Есть ли что-то, чего я здесь не вижу?

Кстати, план Maciek по поддержке протокола путем добавления начального байта 'AA'отличная идея - протоколы, зависящие от отправки только длины, так как первый элемент, кажется, в конце концов облажаются и приводят к тому, что узел пытается освободить больше байтов от количества атомов во вселенной.

Rgds, Martin

Хорошо, теперь я понимаю проблему (я совершенно не понял топологию сети OP - я думал, что на каждом телефоне работает TCP-сервер, а также клиент / с, но на ПК есть только один сервер / что угодно-la-чаты).Я не понимаю, почему вы не можете заблокировать класс сокета с помощью мьютекса, поэтому сериализуйте сообщения.Вы можете поставить сообщения в очередь в сокет, но это имеет последствия для памяти, которых вы пытаетесь избежать.

Вы можете посвятить соединение подаче на телефон только инструкций, например, «откройте мне еще одно сокетное соединение и верните этот GUID - после этого сообщение будет передано на сокет».Это использует пару сокетов только для контроля и уменьшает мощность вашего сервера вдвое: (

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

Другая альтернатива, которая снова потребует разбиения сообщений, - это введение «управляющего сообщения» (возможно, это блок с 55 при запуске вместо AA),который содержит идентификатор сообщения (GUID?), который телефон использует для установления второго соединения через сокет с сервером, передает идентификатор и затем отправляет второе сообщение о новом соединении через сокет.

Другой,(еще не надоест?), способ убедить телефон в том, что новое сообщение может быть в ожидании, состоит в том, чтобы закрыть сокет сервера, на который телефон получает сообщение. Затем телефон может снова подключиться, сообщить серверу, что онполучил только хххх байты идентификатора сообщения гггг. Затем сервер мог ответить с инструкцией наen другой сокет для нового сообщения zzzz и затем возобновите отправку сообщения yyyy.Это может потребовать некоторой буферизации на сервере, чтобы гарантировать, что никакие данные не будут потеряны во время «перерыва».Вы можете захотеть реализовать такую ​​функцию «перезапуск потоковой передачи после перерыва» в любом случае, так как телефоны имеют тенденцию проходить под мостами / туннелями, так же как последние КБ видеофайла 360 МБ передаются в потоковом режиме :( Я знаю, что TCP должен заботиться о пропущенных пакетах, но если беспроводный уровень телефона решает закрыть сокет по какой-либо причине ...

Ни одно из этих решений не является особенно удовлетворительным. Интересно посмотреть, какие другие идеи возникают ..

Rgds,Martin

...