Форум » » C# и Websocket. Нужна помощь. » Ответить

C# и Websocket. Нужна помощь.

Paul_T: В C# пока полный 0, но изучать надо... Решил написать модуль для управления LG телевизора, работающий на Webos. Коммуникация ТВ осуществляется по Websocket на порт 3000. В библиотеках SIMPL# есть класс WebSocketClient, но мне показалось он несколько скуднее на методы, чем такой-же клас в библиотеках .NET. Ну да ладно, как я понимаю, сторонние библиотеки не прокатят и нужно пользоваться только теми, что описаны в SIMPL# (ну, кроме System, видимо). Написал клиентскую часть, соединение с сервером, получение клиентского ключа, сохранение ключа в файле, реализовал несколько команд управления. Работает! громкостью управляет, это уже хорошо, можно развивать дальше, но оказалось все не так просто. После простоя в минуту-две (отсутствие посылок клиент-сервер) сервер перестает отвечать на запросы, при этом метот Send(...) возвращает код WEBSOCKET_CLIENT_SUCCESS. Помогает только переподключение, т.е. нужно разорвать соединение и подключиться заново. За основу своего модуля брал простейший модуль написанный на JS, все сообщения соответствуют этому модулю. JS модуль работает в браузере без ограничения времени, поэтому предположу, что проблема не на стороне телеыизора. Но как ее найти, не понимаю... Может нужно делать асинхронное соединение? Но как это сделать методами SIMPL# так пока и не понял. Может кто сталкивался? Спасибо за любую помощь! PS. Еще заметил, что метод Read() выполняется бесконечно долго при отсутствии ответа от сервера. Может он имеет какие либо таймауты? Но я этого не нашел.

Ответов - 63, стр: 1 2 3 4 All

Вячеслав: 1.Можно попробовать не поддерживать соединение все время, а подключаться только для передачи команды. Если задержка на подключение будет приемлема 2.Подсмотреть WIRESHRK-ом в момент потери связи, что отвечает или не отвечает сервер на ТВ. Либо в дебаг выводить RX с сервера, если это проще чем сниффером.

Paul_T: Вячеслав пишет: 1 - не вариант, так как долго подключается, а после этого нужно делать верификацию ключа итд. Вариант плохой. 2 - не совсем понимаю как ловить пакеты не с компа, на который установлен wireshark, можно ли вообще это сделать? Все данные, что шлются/принимаются websocket клиентом вывожу в консоль, это не помогает, т.к. там нифига не видно, что реально передает сервер, а только то, что получил клиент через сокет без заголовков. т.е. только саму значимую часть... Видимо, чтобы читать все, что исходит от сервера нужно асинхронное соединение, а это пока не понятно как сделать... Понимаю, что нужен какой-то EventHandler, но ввиду отсутствия примеров и скудного крестроновкого хелпа, пока не понимаю, как это реализовать. PS Связь не рвется, статус 'подключено' не меняется, ошибки при отправке сообщения не возникает. Возможно надо слать PING серверу периодично. Но мне кажется, что это должно быть реализовано в самом методе websocket. Ибо на пинг, посланный прогой, сервер тоже не отвечает.

olegny: Никогда не работал с WebSocketClient, но из того, что я вижу: - Не совсем понял почему асинхронное соедитение кажется вам панацеей. По сути оно ничем не отличается от обычного кроме того, что вызовы оных методов не блокируют программу и вы можете что-то еще делать пока ожидаете данных или события в отличии от, например, того же блокируещего метода Receive() (который вы почему-то назваетe Read()?). - асинхронно вы работаете с WebSocketClient или синхронно, от этого серверу (телефизору) ни холодно, ни жарко. - для асинхронной работы нужно задать свои callbacks с помощью ConnectionCallBack, DisconnectCallBack, ReceiveCallBack, SendCallBack пропертей, которые WebSocketClient и позовет когда соотвествующая операция выполнится. - чтобы ловить весь Ethernet трафик между контроллером и телевизором вам понадобится простой hub между ними и подключенный туда же комп с wireshark. Если нет хаба, то можно использоваться управляемый (managed) Ethernet switch на котором надо настроить настроить зеркалирование портов. - Проверьте значение KeepAlive, оно должно быть True - И еще надо убедиться, что обе стороны правильно играют в ping-pong (согласно 5.5.2/5.5.3 RFC 6455) что будет видно в wireshark


Paul_T: olegny пишет: Спасибо за ответ. - Идея асинхронного метода родилась в следствии того, что при запросе пользовательского ключа сначала приходит ответ, что запрос получен, а потом пользователь на телевизоре должен разрешить подключение устройства и приходит еще один ответ от телевизора, собственно, с самим ключем. Т.е. ответ без запроса фактически. - Метод, конечно Receive(). Писал по памяти и ошибся. - В любом случае нужно попробовать асинхронный метод, ибо решил реализовать такой модуль ради практического изучения SIMPL# - Все устройства в одном неуправляемом свитче (самый простой. Свитч он или Хаб даже не знаю ))) , бьюсь, но пока траффика между контроллером и ТВ не видно... Видимо надо бежать за управляемым коммутатором... - KeepAlive - True, так и есть. - Вчера посмотрел Wireshark тот JS модуль, с которого брал пример. Он получает Ping с периодичностью примерно минуту и отвечает. При этом в самой программе этого нет. Т.е. это реализовано в самом подключении(методе?). В JS вроде как нельзая отправить сообщение с Opcode "Ping/Pong", а вот в C# Opcode один из параметров метода Send. И, видимо, не с проста. Но пока не получилось... Буду смотреть Wireshark, шлются ли запросы Ping. Спасибо!

Paul_T: Методом 'научного тыка' удалось понять, что крестроновская библиотека не отвечает PONG на PING телевизора. Поэтому я с периодичностью 30 сек шлю телевизору PONG с пустым телом. И о чудо, он не засыпает... Метод, понимаю, так себе... Но пока другой реализации не вижу.

olegny: Да, похоже там лажа с ping/pong в клиенте. Надо им баг открыть на это, ибо - безобразие! Наверное другого способа и нет если сам клиент вам ping от телека не отдает ни синхронно, ни асинхронно... С управляемым свичем еще возиться придется, а старенький хаб где-нить на барахолке можно найти за три копейки я думаю. Вещь в хозяйстве полезная именно для таких случаев! Впрочем крестроновский свитч делает зекралирование портов легко и приятно... ;)

Paul_T: olegny пишет: Похоже на то. Синхронно получить фрейм Ping не получилось, да и это провально, нельзя же минуту ждать пинга и ничего не нажимать на телевизоре. Асинхронный метод пока не потянул. Надо углубиться в изучения. Я много странных вещей вижу в Simpl# может косяки, а может из-за своей некомпетенции. Например свойство WebSocketClient.Connected возвращает 'true' даже если выключить телевизор. А false если разорвать соединение со стороны клиента методом Disconnect. Зачем оно тогда вообще нужно? Потом у меня перестали работать делегаты для передачи состояния в Simpl+. Так и не понял причину, компиляция без ошибок. Один работает, пишешь второй (который никак не связан) перестают работать оба!!! Я собрал все устройства дома, что имеют более 1 разьема RJ45, ничто не заработало. Управляемый свитч найти/купить проще. Ибо я не знаю, как найти старенький хаб, нельзя же скупить все авито в надежде, что что-то заработает )))

olegny: М-да... Я знаю чувака, который все это понаписал для web sockets. Я бы его близко к программированию не допускал! )) Теоретически синхронный Receive тоже запускается в неблокирующем режиме так, что можно было бы использовать его для работы по опросу, но если вы говорите, что он все равно блокируется, то значит нет другого способа узнать есть уже что-то на входе или нет... (( Надо пробовать асинхронные методы! У вас что-то не получается с ними или просто руки пока не доходят? WebSocketClient.Connected это штука зачастую неадекватна реальному состоянию сокета. Например если, как в вашем случае, просто выключить телек, то оный, скорее всего, просто забудет об открытом соединении и ничего не сделает, чтобы его правильно закрыть. Это - типичная ситуация с abandoned TCP connection. В этом случае у другой стороны нет возможности узнать что сервер просто "свалил" непопрощавшись кроме, как послать ему что-нибудь... для этих дел и существуют всякие keepalive, ping/pong и прочие механизмы. Метод Disconnect же делает все правильно и отсюда - false. C Simpl+ помочь врядли смогу ибо надо копать, а времени нет. Если пришлете кусок кода, что могу бысто посмотреть что может быть не так... Я реально имел ввиду купить hub на каком-нибудь радио рынке. Но если на авито написано HUB, а не SWITCH, то скорее всего это оно! )))

Paul_T: Да, метод Receive ,блокирует все выполнение программы на неопределенное время. Даже таймаутов вроде никаких нет (как параметр). С асинхронными методами пока не делал, но и не понимаю, что вообще надо писать ))). Буду изучать, попробую на днях. Да, по WebSocketClient.Connected понятно. Пока не понятно, как отслеживать состояние телевизора. Может обычным Ping'ом? Выключается он полностью и включается только по WOL. Делегаты в Simpl#, чтобы передавать состояние в Simpl+. И загвоздка была в Simpl# как раз. Но может и я, что не так делал. Копать не прошу, сейчас вроде все работает. Ну, попадется, куплю. Но проще в магазине управляемый свитч купить небольшой с port mirroring... Код смогу выложить, как только приведу в более-менее приличное состояние. Там сейчас такой 'быдлокод', стыдно показывть )))

olegny: Ok. Удачи! Обращайтесь если что у Крестрона не так... ;)

Вячеслав: Пожалуй, самый компактный вариант для зеркалирования это какой-нибудь микротик свитч (на ваш выбор, зеркало они думаю все умеют делать) click here Могу подарить HP HUB (в Москве), но он не компактный. А вы скиньте, если не жалко модуль #. Тоже пытаюсь изучать.

Paul_T: Вячеслав пишет: Мне еще микротика не хватало ))). Да обычно зеркалирование указывается в ТТХ. Куплю какой-нибудь... Все таки интересно, что там происходит... Модуль обещаю выложить с основным функционалом, как только приведу в порядок. Все таки 1 опыт C#... Но есть желание получить полностью рабочий инструмент, ибо дома LG ))) Если, кто еще видел API управления для WebOs, буду блаагодарен. А то я все уже облазил, только отрывками а на http://webostv.developer.lge.com описание Luna Servise API, думаю это не совсем то. У меня в модуле команды формата ssap://audio/volumeUp в JSON посылке. Вот пример: {\"type\":\"request\",\"id\":\"volumeup_" + i +"\",\"uri\":\"ssap://audio/volumeUp\"} Но основное все есть. Еще можно разобрать модуль MajorDoMo, на него есть исходники. Ну и GitHub помогает, там и для Crestron можно найти кое что )

Paul_T: Зашел в "дебри". API для "простого" управления довольно скуден. Можно крутить громкость, переключать входы, каналы (вверх/вниз), получить списко каналов, список приложений. Команды управления для передачи кнопок клавиатуры, указателя координат мышки, вверх/вниз/вправо/влево передаются ввиду сообщений по отдельному Сокету. Сокет я получаю от телека ввиде: {"type":"response","payload":{"socketPath":"ws:/resources/c71caedade95e3bcb349163e36b3edcf368a534e/netinput.pointer.sock","returnValue":true}} А, что дальше с ним делать не знаю. Тот пример, что есть в интернетах использует класс MessageWebSocket() из библиотеки Windows.Networking.Sockets. Ничего похожего в библиотеках Simlp# нет. Это тупик?

olegny: Ну да, у Крестрона этой абстракции (сообщения по вебсокетам) нет, ну и что? Это мало что дает вам! Напишите свой класс для отправки и получения сообщений целиком, а не кусочками - вот и вся суть MessageWebSocket... Тем более, что у вас там json, который не трудно парсить и проверять на синтаксис. Вы же как-то выделили приведенное выше сообщение из потока данных от WebSocketClient! Ну вот так и делайте, только программно и в виде класса... ;) Труднее обстоит дело с самим URI от телека в виде "ws:/..../netinput.pointer.sock". Я не знаю как оно там все устроено у LG, но данный uri выглядит как путь к линуксовому файлу-сокету и что с этим делать - х.з. Вы же к нему не имеете доступа чтобы открыть оный сокет и "поговорить" с телеком? )) Другими словами, ваша проблема не в Крестроне, а в LG. Точнее в описании его API и примерах...

Paul_T: olegny пишет: Да. Просто оказалось все несколько сложнее. Так и есть этот URI путь к сокету. Метод MessageWebSocket.ConnectAsync(Uri) как раз в аргументах имеет такой URI. Как удалось выяснить (я все таки купил управляемы коммутатор) механизм подключения к такому сокету одинаков, за исключением того, что в HTTP заголовке в поле GET передается этот URI. Но опять же у Simpl# WebSocketClient нет такого свойства, которое может записать этот URI в поле GET.

olegny: В смысле этот параметр называется так же? ;) По сути-то это два совсем разных URI. Один - для случая когда ваш клиент сидит на той же самой машине, а другой (который вам нужен) для соединения по сети (<host>:<port>). Говорю же, MessageWebSocket вам здесь совсем не важен и мало что дает - можно обойтись и без этого уровня абстракции.

Paul_T: olegny пишет: Думаю нет. Сервер отдает этот URI именно для подключения по сети. Приложение для LG TV из Microsoft Market делает именно так. Такой-же HTTP запрос на подключение, но в поле GET вписан этот URI. Суть в том, (предположительно) что можно было бы подключиться как обычный CrestronWebSocketClient используя этот URI к Сокету, если бы в заголовке в поле GET можно было бы вписать этот URI. Вот примет HTTP запроса, что шлет это приложение. ¨#þ¶¥X`næ'ìE&ý@@À¨2À¨yô ¸¸>Gª.ËP… GET /resources/dac862b930d3cd8aee47f8653db1faeee201ba60/netinput.pointer.sock HTTP/1.1 Sec-WebSocket-Key: aN/Q7VJaMS0KWvR0gIVN3g== Connection: Upgrade Upgrade: websocket Sec-WebSocket-Version: 13 Host: 192.168.1.121:3000 Cache-Control: no-cache Т.е. Хост и порт тот-же и он там есть. В обычном подключении HTTP запрос такой-же, но поле GET пустое. Для этого, походу, придется писать свой WebSocketClient???

DmitriiP: перед самописанием попробуйте как вариант Mono порт SSharpWebSocketClientLibrary.dll от Neil Colvin, может подойдёт для ваших целей http://www.nivloc.com/downloads/crestron/SSharp/include4.dat%20=%202.10.029%20-%20Plugin%202.x/DLLs/

Paul_T: DmitriiP пишет: Так вот я до сих пор не понимаю, можно ли использовать сторонние библиотеки в проекте библиотеки Simpl#. Не думаю, что прокатит. Иначе зачем включать в Simpl# свой websocket, когда он есть стандартной System.net...

DmitriiP: совсем совсем сторонние C# использовать неполучится, а другие Simpl# библиотеки использовать никто не запрещает да и CrestronWebSocketClient это также не родной MS WebSocketClient а Simpl# обёртка



полная версия страницы