Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
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. Приемник получает свои данные , и вы начинаете смотреть фильм .

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

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

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

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

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