WebSocket¶
WebSocket предоставляет альтернативу ограничению эффективной связи между сервером и веб-браузером, обеспечивая двунаправленную полнодуплексную связь клиент-сервер в реальном времени. Сервер может отправлять данные клиенту в любое время. Поскольку он работает по протоколу TCP, он также обеспечивает низкоуровневую низкоуровневую связь и снижает издержки каждого сообщения .
Традиционная модель НТТР была обременительной практически для любого приложения, выходящего за пределы простой модели данных “получить и отправить контент”. Представьте себе клиент приложения для интерактивной переписки, в котором участники могут отправлять сообщения в любом порядке и сотни участников могут общаться одновременно.
Для подобных целей стандартный подход “запрос-ответ” налагает слишком сильные ограничения. Первыми попытками обойти эти ограничения стали AJAX и Comet. Оба были основаны на так называемых длинных опросах: открытии НТТР-соединения и поддержании его в активном состоянии (сохранении соединения открытым) посредством незавершения отправки ответа.
Java API¶
Сервер : все в пакете javax.websocket.server
Клиент + API на стороне клиента, а также общие библиотеки для сервера и клиента : содержимое пакета javax.websocket
- Существует два способа настройки конечных точек:
- annotation - based
Использование специальных аннотаций на уровне методов
- extension-based
Расширение класса
javax.websocket.Endpoint_
Конфигурация конечный точек с помощью аннотаций¶
События жизненного цикла конечной точки WebSocket обрабатываются следующими аннотациями:
@ServerEndpointКонтейнер обеспечивает доступность класса как сервера WebSocket, слушающего конкретное пространство URI.
@ClientEndpointКласс, украшенный этой аннотацией, обрабатывается как клиент WebSocket.
@OnOpenМетод Java с
@OnOpenвызывается контейнером когда новое соединение WebSocket инициируется.@OnMessageМетод Java, аннотированный
@OnMessage, получает информацию из контейнера WebSocket, когда сообщение отправляется в конечную точку.https://docs.oracle.com/javaee/7/api/javax/websocket/OnMessage.html
@OnErrorМетод с
@OnErrorвызывается при наличии проблем со связью.@OnCloseИспользуется для оформления метода Java, вызываемого, когда соединение WebSocket закрывается.
Типы сообщений:
Спецификация WebSocket поддерживает два формата данных на проводе - текстовый и двоичный (java.nio.ByteBuffer или byte[]).
Прикладной программист может добавить свои специфические типы данных с помощью Encoder и Decoder
@ServerEndpoint(
value="/chat/{username}",
decoders = MessageDecoder.class,
encoders = MessageEncoder.class )
Пример:
// URI, где развернута конечная точка.
// URI определяется относительно корня контейнера сервера и должен начинаться с косой черты.
@ServerEndpoint(value="/chat/{username}")
public class ChatEndpoint {
private Session session;
private static Set<ChatEndpoint> chatEndpoints
= new CopyOnWriteArraySet<>();
private static HashMap<String, String> users = new HashMap<>();
// Когда новый пользователь входит в систему (@OnOpen),
// он немедленно сопоставляется со структурой данных активных пользователей.
// Затем сообщение создается и отправляется на все конечные точки
// с помощью метода broadcast (см ниже).
@OnOpen
public void onOpen(
Session session,
@PathParam("username") String username) throws IOException {
this.session = session;
chatEndpoints.add(this);
users.put(session.getId(), username);
Message message = new Message();
message.setFrom(username);
message.setContent("Connected!");
broadcast(message);
}
// Этот метод также используется всякий раз, когда новое сообщение
// отправляется (@OnMessage) любым из подключенных пользователей
@OnMessage
public void onMessage(Session session, Message message)
throws IOException {
message.setFrom(users.get(session.getId()));
broadcast(message);
}
// Наконец, когда пользователь больше не подключен к чату,
// метод @OnClose очищает конечную точку и передает всем пользователям,
// что пользователь был отключен.
@OnClose
public void onClose(Session session) throws IOException {
chatEndpoints.remove(this);
Message message = new Message();
message.setFrom(users.get(session.getId()));
message.setContent("Disconnected!");
broadcast(message);
}
// Если в какой-то момент происходит ошибка, метод с аннотацией @OnError обрабатывает ее.
// Вы можете использовать этот метод для регистрации информации об ошибке
// и очистки конечных точек.
@OnError
public void onError(Session session, Throwable throwable) {
//Do error handling here
}
private static void broadcast(Message message)
throws IOException, EncodeException {
chatEndpoints.forEach(endpoint -> {
synchronized (endpoint) {
try {
endpoint.session.getBasicRemote().
sendObject(message);
} catch (IOException | EncodeException e) {
e.printStackTrace();
}
}
});
}
}