Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
 Languages
 С
 GNU С Library 
 Qt 
 STL 
 Threads 
 C++ 
 Samples 
 stanford.edu 
 ANSI C
 Libs
 LD
 Socket
=> Pusher
 Pipes
 Encryption
 Plugin
 Inter-Process
 Errors
 Deep C Secrets
 C + UNIX
 Linked Lists / Trees
 Asm
 Perl
 Python
 Shell
 Erlang
 Go
 Rust
 Алгоритмы
NEWS
Последние статьи :
  Тренажёр 16.01   
  Эльбрус 05.12   
  Алгоритмы 12.04   
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
 
TOP 20
 MINIX...3057 
 Solaris...2933 
 LD...2905 
 Linux Kernel 2.6...2470 
 William Gropp...2182 
 Rodriguez 6...2014 
 C++ Templates 3...1945 
 Trees...1938 
 Kamran Husain...1866 
 Secure Programming for Li...1792 
 Максвелл 5...1710 
 DevFS...1694 
 Part 3...1684 
 Stein-MacEachern-> Час...1632 
 Go Web ...1625 
 Ethreal 4...1619 
 Arrays...1607 
 Стивенс 9...1603 
 Максвелл 1...1592 
 FAQ...1538 
 
  01.01.2024 : 3621733 посещений 

iakovlev.org

Pusher

Код лежит тут

У меня на работе встал вопрос с реализацией удаленных прямых телевизионных трансляций . Нужно было написать TCP клиент-серверное приложение для их поддержки. Этот проект мы назвали Pusher - от слова толкать .
Рассмотрим схему:

 
 
                         ============                                                         ================
                         | клиент 1 |                                                   <-->  |  приемник 1  |
                         ============                                                         ================
    ==============       ============                                     ============        ================
    |  источник  | <-->  | клиент 2 |     <--- ... Интернет ...  ---->    |  сервер  |  <-->  |  приемник 2  |
    ==============       ============                                     ============        ================
                         ============                                                         ================
                         | клиент 3 |                                                   <-->  |  приемник 3  |
                         ============                                                         ================
 
 
 
Имеется веб-сервер с 'белым' ip-шником , к которому коннектятся приемники трансляции. На другом конце - источник трансляции , физически расположенный на клиентском компьютере с 'серым' ip-шником . Задача - передавать в real-time видео-поток с клиента на сервер .

Эта схема укладывается в традиционную архитектуру 'TCP client-server' . На сервере размещены 2 прослушивающих сокета : правый слушает запросы с серверного приемника , левый слушает запросы с клиентской машины. На клиенте - также 2 сокета , правый клиентский сокет коннектится к левому сокету сервера , левый клиентский сокет коннектится к источнику .

                                клиент
                           ================ 
                     <---->| 8003 | 8005  |<---->
                           ================
 
                                сервер 
                           ================ 
                     <---->| 8005 | 8007  |<---->
                           ================
 

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

Для того , чтобы протестировать эту модель локально , мы возьмем в качестве источника и приемника медиа-проигрыватель VLC , который умеет вещать и делать http-запросы . Так , в качестве источника запустим VLC с параметрами :

vlc -vvv film.avi --sout '#standard{access=http,mux=asf,dst=127.0.0.1:8003}'

В нашей схеме источник будет вещать на порт 8003 , к которому будет коннектиться левый клиент .

Общение между правым клиентом и левым сервером будет происходить по порту 8005.

В качестве приемника запустим VLC с параметрами - можно запустить сразу несколько команд :

vlc http://127.0.0.1:8007

VLC в качестве приемника будет делать запросы на порт 8007 , который будет обслуживать правый сервер. Если вы сразу замените в локальном варианте порт 8007 на 8003 , то приемник непосредственно обратится к источнику , откроется окно VLC , и вы начнете смотреть фильм . В нашей схеме , если все правильно написано , в результате должно произойти то же самое - откроется VLC-ное окно для просмотра фильма .

Я написал сначала версию на питоне , позже сделал си-шный вариант .
Рассмотрим вариант на С .

Пушер имеет многопоточную архитектуру на базе pthread : такой выбор я сделал для удобства разделения логики работы. В проекте 2 многопоточных приложения - клиент и сервер . Два сокета внутри сервера разнесены по разным потокам , равно как и два сокета внутри клиента . Обмен данными между двумя потоками как внутри сервера , так и внутри клиента , выполняется через глобальные связные списки. Потоки внутри приложения также обращаются к глобальным структурам , доступ к которым синхронизирован с помощью стандартных блокировок .

В заголовок каждого фрейма записывается id-шник дескриптора приемника , которому предназначен этот фрейм . В нужный момент он парсится при получении .

Особенность локальной реализации , которая завязана на VLC , обуславливает следующую последовательность событий :

1. Правый слушающий сокет получает от приемника специфическую VLC-шную строку коннекта и сохраняет ее в глобальной переменной.
2. Левый слушающий сокет проверяет эту переменную и передает строку коннекта правому клиенту , который сохраняет ее в свою очередь также в глобальной переменной.
3. Левый клиент читает эту глобальную переменную и отдает строку источнику
4. Получив 'нормальную' строку коннекта , VLC начинает отдавать видео-данные левому клиенту, при этом создается отдельный поток для обслуживания данного приемника .
5. Левый tcp-клиент получает видео-данные и добавляет фрейм в связаный список client_frame. Он увеличивает счетчик фреймов и сообщает об правому клиенту , который может находиться в спячке .
6. Правый tcp-клиент , получив сообщение , просыпается , извлекает видео-данные из связного списка client_frame , сохраняет id-шник дескриптора приемника в заголовке фрейма, записывает фрейм в свой дескриптор , после чего уменьшает счетчик фреймов .
7. Левый tcp-сервер читает свой дескриптор и извлекает видео-данные из фрейма, получает id-шник дескриптора приемника из заголовка фрейма , после чего добавляет фрейм в связный список server_frame , затем увеличивает счетчик фреймов и сообщает об этом правому серверу , который может находиться в спячке .
8. Правый tcp-сервер просыпается , извлекает видео-данные из связного списка server_frame и записывает фрейм в свой дескриптор данного приемника , уменьшая счетчик фреймов .
9. Приемник получает свои данные , и вы начинаете смотреть фильм .

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

В этой версии еще есть над чем поработать :
выключение работающего приемника в данной версии некорректное и приводит к перегрузке сервера .

Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье