Как использовать один порт для нескольких логических потоков данных (Winsock)? - PullRequest
2 голосов
/ 13 июня 2011

Я разрабатываю клиент-серверное приложение Winsock (Visual C ++), которое должно передавать различные виды данных (видеопоток, аудиопоток, сервисные уведомления и т. Д.) По сети.Я знаю, что более чистый подход заключается в использовании отдельных портов в отдельных потоках для каждого отдельного типа данных (я называю это «поток» здесь).Но для этого потребовалось бы занять как минимум 5 разных портов, что могло бы стать проблемой для некоторых сетевых инфраструктур (переадресация портов брандмауэра и т. Д.).

Поэтому я пытаюсь реализовать однопортовое соединение (TCP), только односокет будет использоваться для передачи разных потоков.Отдельные пакеты будут иметь информацию в заголовке, которая будет указывать, какому потоку он принадлежит, ожидаемому общему размеру сообщения и т. Д. Скажем, у меня есть 5 разных потоков.Я планирую использовать 5 потоков для вызова send () одного и того же сокета.Это безопасно?Я знаю, что пакеты из разных потоков будут поступать смешанно, но если я включу необходимую метаинформацию в каждый отправленный пакет, она может быть правильно восстановлена ​​на другой стороне, верно?

Но реальная проблема - это принимающая сторона.Хотя, вероятно, нормально вызывать send () из нескольких потоков в одном сокете (хотя я не уверен и нуждаюсь в вашем подтверждении!), Вызов recv () из нескольких потоков будетне имеет особого смысла.Поэтому я должен использовать одну блокировку recv () из одного потока.Но затем, основываясь на заголовке пакета (определяющем, к какому потоку принадлежит данный пакет), я должен разделить обработку в разных потоках.Видеопоток должен обрабатываться одним потоком, аудиопоток - другим и т. Д. Но я не совсем знаю, как это сделать - как раскошелиться на обработку данных из принимающего потока.Производительность является главным приоритетом, поэтому нельзя рассматривать обработку всех потоков одним потоком.

Итак, для подведения итогов у меня есть три вопроса:

  • Можно ли вызывать send () для одного сокета из нескольких потоков?(при условии, что в заголовке пакета будет информация о том, к какому потоку отправителя (т.е. к подсистеме) он принадлежит).
  • Наличие одного блокирующего сокета на принимающей стороне, вызывающего recv () в цикле из одного потока, как «разветвлять» полученные пакеты разных логических потоков в разные рабочие потоки?
  • Какие были бы ваши дополнительные рекомендации для реализации многопотоковой передачи через один порт?

PS: Афаик, нет никакого способа использовать один порт несколькими сокетами, верно?

Это платформа Windows, Winsock2, Visual C ++ (если бы вы могли предоставить подсказки для конкретной платформы, которые были быудивительно).

= ОБНОВЛЕНИЕ =

  • Когда вы говорите «блокировка сокета», вы имеете в виду сериализацию доступа к send () функция?например, с Критическое сечение ?

  • Что касается принимающей стороны ... Я думаю, что я соберу сообщения (я называю "message" логической полной структурой данных, например,видеокадр, или звуковой пример, и т. д.), когда recv () - включает их в цикл (буферизация сообщений из отдельных потоков в отдельные буферы), а затем я просто передаю собранные сообщения (когда одно будет полностьюполучил), чтобы ветки темы. Теперь вопрос в том, как их передать. Единственный способ, о котором я подумал: Объекты событий : SetEvent () из потока получателя для запуска WaitForSingleObject () (который находится в некотором цикле) в разных потоках.Можете ли вы посоветовать, если это приемлемое решение?Можете ли вы предложить что-нибудь лучше?Разве нет более быстрых решений для этого («запустить» другой поток того же приложения), чем Event Objects?А как передать данные?

Ответы [ 2 ]

3 голосов
/ 13 июня 2011
  • Можно ли вызывать send () для одного и того же сокета из нескольких потоков? (при условии, что в пакете будет информация заголовок о том, какой поток отправителя (т.е. подсистема) принадлежит).

Конечно, вы можете вызывать его из нескольких потоков. Но помните, что вы должны поставить блокировку на сокете, чтобы синхронизировать это. В противном случае вы можете получить информацию, написанную противоречиво.

  • Наличие одного блокирующего сокета на принимающей стороне, вызов recv () в цикле из одних ниток, как "раскошелиться" полученные пакеты разных логические потоки к другому работнику темы?

Как насчет написания BeginRecv и EndRecv, аналогичных boost asios library (или переходу на Boost Asio. Он не зависит от платформы!). Когда вы получили полное сообщение, вы можете отправить его. Я полагаю, вы хотели бы поместить это куда-нибудь еще, чтобы продолжить получать из сети? Вы можете просто использовать CreateThread и отправить ваши данные через параметр lpParameter, и это будет проблемой кого-то другого, чем recv. Не будет особого смысла в разветвлении этого до загрузки ваших данных, потому что вам все равно нужно загрузить полный пакет, прежде чем сможет быть следующий.

  • Какими были бы ваши дополнительные рекомендации для реализации многопотоковая передача через один порт

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

Когда вы говорите «заблокировать сокет», вы означает сериализовать доступ к send () функционировать? например с критическим разделом?

Да, действительно. Вы можете сделать это довольно используя, например, RAII для блокировки (тогда она будет автоматически удалена / разблокирована при возврате функции). Но да, используйте функцию sendData, в которой вы сначала блокируете объект, а затем отправляете свои данные.

Теперь вот вопрос - как их пройти.

Ну, вы можете просто передать его как новый поток.

DWORD WINAPI myVideoProcessor(LPVOID theData){
    StructForKeepingVideoData* data = dynamic_cast<StructForKeepingVideoData*>(theData);
    // process the data that is passed in theData
    ...
}
...
void ReceiveData(){
    while (true){
        ...
        char buffer[SIZE];
        recv(mySocket, buffer, SIZE, 0);
        StructForKeepingVideoData* data = new StructForKeepingVideoData(buffer);

        HANDLE mId = CreateThread(NULL, 0, myVideoProcessor, data, 0, NULL);
        // now, the video processing will be done somewhere else. let's continue receiving data!
    }
}

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

Единственный способ, о котором я подумал, это Объекты события: SetEvent () из поток приемника для запуска WaitForSingleObject () (который находится в какая-то петля) в разных темах. Можно ваш совет, если это приемлемо решение?

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

Можете ли вы предложить что-нибудь лучше? Не там быстрее решения для этого («триггер» другая нить того же приложение) чем объекты событий? А также как передать данные?

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

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

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

Можно ли вызывать send () для одного и того же сокета из нескольких потоков?

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

"разветвлять" полученные пакеты разных логических потоков в разные рабочие потоки?

Ваш протокол должен будет это сделать.Соберите видео / аудио / любой другой протокольный блок из входящего потока байтов и поставьте PDU в очередь в соответствующий поток обработчика.

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

Rgds, Martin

...