Я уже описывал некоторые своеобразные решения для клиент-серверного взаимодействия в мобильных приложениях, но сегодня мы познакомимся, вроятно, с самым интересным инструментом для решения таких задач: с технологией websocket.
В чём её прелесть? Практически полный реалтайм, минимальные накладные расходы на передачу данных, возможность реализовать любой, даже бинарный протокол "внутри" websocket-а, и самое приятное: простота реализации и готовые библиотеки. С этим давайте и разберёмся подробнее.
Websocket на сервере
Технология websocket пока ещё не стала официальным стандартом и не удивительно, что "большие" сервера её ещё не поддерживают. Это даёт простор для развития специализированных websocket-серверов типа phpDaemon, Cowboy или Tornado. Но нам, java-программистам чужды суперскоростные и непостижимые erlang-сервера или python с php. Нам бы что-то родное, понятное и по возможности не громоздкое... Есть, к примеру, jwebsocket. Это комплекс библиотек для реализации сервеной и клиентской поддержки websocket. Вероятно, это неплохое решение для крупного проекта, но мне больше нравится использовать jetty. "Впилим" его в наше java-приложение и получим свой websocket-сервер с блекджеком и т.п. :)
Скачиваем jetty, распаковываем, и из директории lib добавляем в свой проект библиотеки:
jetty-continuation-8.1.3.v20120416.jar
jetty-io-8.1.3.v20120416.jar
jetty-util-8.1.3.v20120416.jar
servlet-api-3.0.jar
jetty-http-8.1.3.v20120416.jar
jetty-server-8.1.3.v20120416.jar
jetty-websocket-8.1.3.v20120416.jar
В вашем случае последняя часть имени файла, само собой, может отличаться.
Теперь пишем код.
import org.eclipse.jetty.server.Server; public class WsProxy { public static void main(String[] args) throws Exception { int port = 8088; if (args.length>0) { try { port = Integer.parseInt(args[0]); } catch (Exception e) { e.printStackTrace(); } } Server jetty = new Server(port); jetty.setHandler(new WsHandler()); jetty.start(); } }
Как видим, чтобы запустить сервер нам потребовалось всего три стороки кода. Дальше пишем код уже в классе WsHandler:
import javax.servlet.http.HttpServletRequest; import org.eclipse.jetty.websocket.WebSocket; import org.eclipse.jetty.websocket.WebSocketHandler; public class WsHandler extends WebSocketHandler { @Override public WebSocket doWebSocketConnect(HttpServletRequest req, String protokol) { try { return new WebSocket.OnTextMessage() { @Override public void onOpen(Connection arg0) { // TODO Auto-generated method stub } @Override public void onClose(int arg0, String arg1) { // TODO Auto-generated method stub } @Override public void onMessage(String arg0) { // TODO Auto-generated method stub } }; } catch (Exception e) { e.printStackTrace(); } return null; } }
Вот и всё. Описываем нашу логику, которая должна работать при подключении нового устройства, отключении и получении сообщения от него. Объект Connection, полученный при подключении следует сохранить, чтобы иметь возможность послать сообщение клиенту при необходимости методом connection.sendMessage(текст сообщения); Сами объекты WebSocket сохраняем в коллекцию для организации взаимодействия между ними.
Websocket в Android
Для реализации клиентской части нашего соединения в Android-приложении также есть несколько вариантов библиотек. Подробно сравнить их характеристики, можно тут. Мне представляется, что самым важным критерием для выбора библиотеки в нашем случае будет поддержка последних версий протокола. Сам протокол ещё не стандартизован, опубликованы (и соответсвенно реализованы в библиотеках) три "черновых" версии и "предрелизная" RFC 6455. Её весьма неплохо реализует библиотека AutobahnAndroid. Она также отличается высокой производительностью за счёт использования Java NIO, хотя это и является причиной её, по-моему, единственного недостатка: отсутствия поддержки wss - защищённого websocket соединения. Впрочем, если вы не передаёте конфиденциальных данных или всё равно намерены реализовать внутренний слой шифрования самостоятельно, для вашего проекта это не проблема. Если же wss - обязательное условие, обратите внимние на Java WebSocket Client. Он хоть и не поддерживает RFC 6455, зато нормально обеспечивает соединение с wss - сервером.
Итак, добавим к себе в Android-приложение библиотеку AutobahnAndroid, а также две библиотеки, от которых она зависит: jackson-core-asl-1.8.5.jar и jackson-mapper-asl-1.8.5.jar.
Теперь метод, который установит нам соединение с сервером будет виглядеть так:
public void connect(View v) { try { WebSocketConnection mConnection = new WebSocketConnection(); mConnection.connect(url, new WebSocketHandler() { @Override public void onOpen() { System.out.println("--open"); } @Override public void onTextMessage(String message) { System.out.println("--received message: " + message); } @Override public void onClose(int code, String reason) { System.out.println("--close"); } }); } catch (Exception e) { e.printStackTrace(); } }Тут в метод connect экземпляра класса WebSocketConnection мы передаём WebSocketHandler, в методах которого ловим соответствующие события и реагируем на них, как нам потребуется. Разорвать соединение можно методом mConnection.disconnect(), а послать сообщение методом mConnection.sendTextMessage(текст сообщения). Похоже на то, что мы делали на серверной стороне, не правда ли?
а как прогнать WebSockets через прокси?
ОтветитьУдалитьПока мне попалось только одно решение: отправлять периодически пустой пакет с сервера, чтобы прокси считал соединение активным, и не разрывал его. Возможно есть решения лучше, не знаю.
Удалитьиспользовать wss
УдалитьNice Blog Post !
ОтветитьУдалить