Афоризм
"Нахал!" – совсем не значит "Прекратите".
Наталья Резник
Последние статьи

 • Активности Android
Многоэкранные Android приложения
 • Fragment dynamic
Динамическая загрузка фрагментов в Android
 • Fragment lifecycle
Жизненный цикл Fragment'ов в Android
 • Fragment example
Пример Fragment'ов в Android
 • Data Binding
Описание и пример Data Binding
 • Пример MVVM
Пример использования MVVM в Android
 • Компонент TreeTable
Описание компонента TreeTable для Swing
 • Пример TreeTable
Пример использования TreeTable
 • Хранилища Android
Внутренние и внешние хранилища данных
 • Пример SQLite
Пример использования SQLite в Android
 • WebSocket
Описание и пример реализации WebSocket
 • Визуальные компоненты
Улучшен компонент выбора даты из календаря
 • Анимация jQuery
Описание и примеры анимации элементов DOM
 • APK-файл Android
Создание apk-файла для android устройств, .dex файлы
 • платформа JaBricks
Платформа OSGi-приложения JaBricks
Поддержка проекта

Если Вам сайт понравился и помог, то будем признательны за Ваш «посильный» вклад в его поддержку и развитие
 • Yandex.Деньги
  410013796724260

 • Webmoney
  R335386147728
  Z369087728698

WebSocket, описание и пример

WebSocket — это независимый протокол, основанный на протоколе TCP и предназначенный для обмена сообщениями между браузером и веб-сервером в режиме реального времени. Таким образом, изначально синхронный протокол HTTP, построенный на модели «запрос — ответ», становится полностью асинхронным и симметричным. При использовании WebSocket'a нет клиента и сервера с фиксированными ролями, а есть два равноправных участника обмена данными. Каждый участник функционирует самостоятельно : отправил сообщение и продолжил выполнять свои функции. Участник, получивший сообщение, может вообще не отвечать : протокол дает полную свободу в обмене данными.

Достоинство WebSocket

WebSocket - это двунаправленный, полнодуплексный протокол, который используется в сценарии взаимодействия «Клиент-сервер». Протокол WebSocket предполагает, что соединение устанавливается отправкой запроса, начинающегося с префикса 'ws://'. В случае безопасного соединения (https, SSL-протокол) необходимо отправить запрос на соединение с префиксом 'wss://'.

Плюсы WebSocket'a :
  1. Двусторонний обмен данными : одновременно можно получать и передавать информацию.
  2. Кроссплатформенная совместимость : для сервера websocket'a в качестве клиента может выступать как WEB-сайт, так и мобильное приложение.
  3. Объем накладных расходов в виде описания заголовка запроса для веб-сокета составляет всего 2 байта, в то время как для HTTP потребуется до 2000 байтов. Запросы и ответы приходят без задержек и сетевой нагрузки.
  4. Кратковременное отсутствие связи не разрывает соединение. Даже если интернет работает нестабильно, клиенту с сервером не нужно каждый раз заново устанавливать websocket'ное соединение; они могут продолжить обмениваться данными в обычном режиме, пока связь не восстановится.
  5. Протокол позволяет работать в асинхронном режиме вместо привычной для веба работы «Запрос-ответ». Т.е. у клиента и сервера равноправные роли в процессе обмена данными, и они действуют автономно.
Минусы WebSocket'a :
  1. Молчаливый разрыв соединения. При отправке сообщения в Websocket Вы не узнаете о том, доставлено оно или нет, пока не пройдёт время таймаута (по умолчанию 75 секунд). Зачастую приходится вводить дополнительные механизмы общения между клиентом и сервером, чтобы быстрее реагировать на молчание (не ответ) клиента.
  2. Смена сети клиентом. В случае, если во время сеанса необходимо сменить сеть, то сервер ничего не узнает о переподключении и о смене адреса клиента.

Описание протокола WebSocket

Прежде чем переходить к описанию примера необходимо, в первую очередь, рассмотреть протокол WebSocket, определенный стандартом RFC 6455. Протокол WebSocket определяет две URI (Uniform Resource Identifier - унифицированный идентификатор ресурса) схемы :

  • ws : нешифрованное соединение;
  • wss : шифрованное соединение.
// Синтаксис конструктора объекта
let ws = new WebSocket(uri);
let ws = new WebSocket(uri, protocols);

// Пример создания объекта
let ws = new WebSocket("ws://demo-websocket.org/params");
 

uri – URL адрес, к которому нужно подключиться (сервер WebSocket).

protocols – строка, либо массив строк протокола, которые используются для указания вложенных протоколов, чтобы один сервер мог реализовать несколько вложенных протоколов WebSocket. Необязательный параметр.

Примечание : включенные в URI params можно использовать как параметры, передаваемые серверному объекту. В примере таким образом передается наименование чата и имя пользователя.

Созданный объект WebSocket имеет четыре callback'a : один при получении данных и три – при изменениях состояния соединения :


ws.onmessage = function(event) {
    alert("Получены данные " + event.data);
};

ws.onopen = function() {
    alert("Соединение установлено.");
};

ws.onclose = function(event) {
    if (event.wasClean) {
        alert('Соединение закрыто чисто');
    } else {
       alert('Обрыв соединения');  // при остановке сервера
    }
    alert('Код: '+ event.code +' причина: '+event.reason);
};

ws.onerror = function(error) {
  alert("Ошибка " + error.message);
};
 

Ваш браузер не поддерживает Javascript. Интегрированный в страницу пример c Websocket'ом не будет функционировать без Javascript. Если Вы хотите увидеть пример в действии необходимо разрешить использование Javascript и перезагрузить страницу.

Пример WebSocket

Примечание :
1. Ниже представлен пример WebSocket'a в виде простенького chat'а. Пример разработан в IDE Eclipse под управлением Tomcat.
2. На данной странице пример функционирует под управлением серверов Apache HTTP и Tomcat с использованием защищенного SSL-протокола.
3. При использовании Apache HTTP и Tomcat необходимо иметь версию Apache HTTP не ниже 2.4, подключить модули mod_proxy, mod_proxy_http, mod_proxy_wstunnel. Модуль mod_proxy_wstunnel является прокси-сервером Websocket.
4. Чтобы пример функционировал, Вам необходимо авторизоваться на сайте, после чего кнопка «Подключение» будет разблокирована. Если Вы не зарегистрированы, то необходимо пройти и эту процедуру.

Как работает этот пример?
Заполните поля «Наименование чата», «Имя пользователя» и нажмите кнопку «Подключение». После подключения к серверу текстовое поле «Сообщение» и кнопка «Отправить» становятся доступными. Вам необходимо только ввести сообщение и нажать кнопку «Отправить». Сервер, получив сообщение, распространит его среди всех участников чата. Если к чату подключены только Вы, то, следовательно, сообщение получите только Вы. Но, если у Вас имеется возможность параллельно подключиться к чату из другого браузера, либо из другой вкладки браузера, то Вы увидите рассылку сообщения всем участникам чата. Поступившие сообщения отображаются в поле «Сообщения» в следующем виде :

  • отправленные Вами сообщения выравниваются по правому краю и включают время отправки;
  • полученные сообщения от других участников чата выравниваются по левому краю и включают время и имя отправителя.

Введите наименование чата и имя :

Наименование чата
Имя пользователя

Участники чата
Сообщения

Отправка сообщений

С точки зрения изучения WebSocket'a Вы можете использовать данный пример для обучения. Ниже приводится описание примера.

Описание примера

Разделим описание примера на две части. В первой части будет представлено описание интерфейсной части, реализованной в данной статье. Во второй части — описание серверной части.

Интерфейсная часть примера

включает представленный ниже html-код, интегрированный в интерфейс страницы, и javascript-код в виде файла websocket.js, реализующий описанную ниже бизнес-логику.

Листинг html-кода

Особый комментарий к листингу не требуется; здесь всё тривиально просто, выше представлено его отображение в браузере. Обратите внимание, что к кнопкам не подключены обработчики событий нажатия click : это реализовано в javascript-коде.

листинг


<p>Введите наименование чата и имя :</p>
<table>
   <tr><td>Наименование чата</td>
       <td><input type="text" id="chat" /></td></tr>
   <tr><td>Имя пользователя</td>
       <td><input type="text" id="user" /></td></tr>
   <tr><td><input type="button" id="btnConnect"
                  value="Подключение" /></td>
       <td><input type="button" id="btnDisconnect"
                  value="Отключение" /></td></tr>
</table>

<p style="margin-top:10px" />
<div style="margin-bottom:5px">
   <div style="display:inline-block;font-weight:bold">
        Участники чата</div>
  <div style="display:inline-block;font-weight:bold">
        Сообщения</div>
</div>
<div id="users"
     style="float:left;border:1px solid #bbb;
             width:210px;height:240px">
</div>
<div id="messages"
     style="margin-left:225px;border:1px solid #bbb;
            width:450px;height:240px;padding:5px">
</div>

<p style="margin-bottom:10px" />

<table>
   <tr><td colspan="2">
       <b>Отправка сообщений</b></td></tr>
   <tr><td><input id="message" type="text" size="92"
            placeholder="Введите текст..." /></td></tr>
  <tr><td><input type="button" id="btnSend" 
                 value="Отправить" /></td></tr>
</table>

 

Листинг javascript-кода

Бизнес-логика интерфейсной части примера определена в js-файле websocket.js. Ниже представлена только структура : переменные и наименования методов. Наименования переменных отражают элементы доменной структуры страницы. Исходные коды и описание методов будет представлено ниже.

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

листинг


(function() {
var btnConnect    = null;
var btnDisconnect = null;
var btnSend       = null;
var txtMessage    = null;
var txtMessages   = null;
var txtUsers      = null;
var txtChat       = null;
var txtUser       = null;

var ws            = null;
//-----------------------------------------------
function init()                 { ... }
function lockFields(mode)       { ... }
function connect()              { ... }
function onConnect(msg)         { ... }
function userList(users)        { ... }
function addUser2List(userName) { ... }
function disconnect()           { ... }
function clearComponents()      { ... }
function sendMessage()          { ... }
function addMessage2List(msg)   { ... }
//-----------------------------------------------
   window.addEventListener('load', init, false);
})();
 

Методы init, lockFields

Метод init инициализирует переменные с использованием функции document.getElementById(), т.е. переменные «связываются» с элементами DOM страницы. Кроме этого, к кнопкам управления подключаются слушатели, которые определяют вызываемые при нажатии на кнопки методы.

Метод lockFields, в зависимости от значения параметра mode, блокирует и разблокирует компоненты формы (элементы DOM-структуры). Данный метод вызывается при создании WebSocket, т.е. при установлении соединения с сервером, и при отключении (disconnect) от сервера.

листинг


function init()
{
   txtChat      = document.getElementById('chat'         );
   txtUser      = document.getElementById('user'         );
   btnConnect   = document.getElementById('btnConnect'   );
   btnDisconnect= document.getElementById('btnDisconnect');
   btnSend      = document.getElementById('btnSend'      );

   txtMessage   = document.getElementById('message'      );
   txtMessages  = document.getElementById('messages'     );
   txtUsers     = document.getElementById('users'        );

   btnConnect   .addEventListener('click',connect    ,false);
   btnDisconnect.addEventListener('click',disconnect ,false);
   btnSend      .addEventListener('click',sendMessage,false);
   lockFields(true);
}
//----------------------------------------------------------
function lockFields(mode)
{
   txtChat      .disabled =  mode;
   txtUser      .disabled =  mode;
   btnConnect   .disabled =  mode;
   btnDisconnect.disabled = !mode;
   btnSend      .disabled = !mode;
   txtMessage   .disabled = !mode;
}
 

Методы connect, onConnect, userList, addUser2List

Ниже представлен метод connect для создания WebSocket'a, а также методы onConnect, userList, addUser2List, вызываемые после установления соединения с сервером. Метод connect вызывается при нажатии на кнопку «Подключение». В методе определяется URI WebSocket'a, включающего host, наименование чата и имя пользователя. Бизнес-логика данного примера предполагает, что участники чата должны знать его наименование; посторонние не должны здесь появиться. Кроме этого, не допускается дублирование имени пользователя. Поэтому, после создания объекта ws, т.е. после установления соединения с сервером, он вышлет сразу же сообщение с типом msg.type="connection" с результатом соединения msg.result. Если результат положительный, то в методе onConnect(msg) пользователь будет добавлен в список участников чата, в противном случае откроется окно с сообщением 'Ошибка подключения к чату'; причина связана с дублированием имени пользователя.

Если к чату подключаются новые пользователи, то сервер всем участникам чата рассылает список в виде сообщения msg.type="userlist", которое отправляется в процедуру userList(users), в которой обновляется список участников чата.

листинг


function connect()
{
   let chat = $("#chat").val();
   let user = $("#user").val();

   // определение URI 
   let host     = document.location.host + "/";
   let pathname = document.location.pathname;
   let data     = pathname.split('/');

   if (data.length > 2)
       pathname = "/" + data[1] + "/websocket";
   else
       pathname = "/websocket";
       
   let uri = null;
   if (window.location.protocol == 'http:')
       uri = 'ws://' + host + 
                       pathname + "/" + chat + "_" + user; 
   else
       uri = 'wss://' + host + ":9090" + 
                        pathname + "/" + chat + "_" + user;
   // создание объекта WebSocket
   ws = new WebSocket(uri);
   // подключение обработчика поступивших сообщений
   ws.onmessage = function(event) {
      let msg = JSON.parse(event.data);

      switch(msg.type) {
         case "connection" :
               onConnect(msg);
               break;
         case "userlist" :
               userList(msg.users);
               break;
         case "message" :
               addMessage2List(msg);
               break;
         default :
               console.log("Unknown message received : " 
                                             + msg.type);
      }
   };	  
}
//---------------------------------------------------------
function onConnect(msg)
{
    if (msg.result) {
        lockFields(false);
        addUser2List(msg.username);
    } else
        alert ('Ошибка подключения к чату');
}
//---------------------------------------------------------
function userList(users)
{
    while (txtUsers.firstChild)
        txtUsers.removeChild(txtUsers.firstChild);

    users.forEach(function(username) {
        addUser2List(username);
    });
}
//---------------------------------------------------------
function addUser2List(userName)
{
    let p = document.createElement("p");
    p.setAttribute('style', 'margin:0px');
    if (userName === $("#user").val())
        p.innerHTML = "<b>" + userName + "</b><br />";
    else
        p.innerHTML = userName + "<br />";
    txtUsers.appendChild(p);
}
 
ПРИМЕЧАНИЕ :
При определении URI используются параметры host и pathname, чтобы совместить разработку в IDE Eclipse (DEVELOP) и выкладывание данной страницы на сервер (PRODUCTION).

----- Среда DEVELOP -----
URL страницы : http://localhost:8080/java-online/websocket.xhtml
URI WebSocket'a : ws://localhost:8080/java-online/test_alex

• host=localhost:8080
• pathname=/java-online/websocket.xhtml

--------------------------------------------------------------------------------------------
----- Среда PRODUCTION -----
URL страницы : https://java-online.ru/websocket.xhtml
URI WebSocket'a : wss://java-online.ru:9090/test_alex

• host=java-online.ru
• pathname=/websocket.xhtml

Методы disconnect, clearComponents

Ниже представлен листинг метода отключения от сервера disconnect и метода очистки компонентов clearComponents. При отключении от сервера очищаются компоненты и блокируются компоненты формы, разблокируется кнопка подключения, после чего закрывается соединение WebSocket'a. При очистке компонентов формы удаляются дочерние элементы списка участников чата и списка поступивших сообщений, а также поле отправки сообщения.

листинг


function disconnect()
{
   // очистка компонентов формы
   clearComponents();
   // блокирование полей 
   lockFields(true);
   // закрытие соединения
   ws.close();
}
//---------------------------------------------------------
function clearComponents()
{
    while (txtUsers.firstChild)
        txtUsers.removeChild(txtUsers.firstChild);
    while (txtMessages.firstChild)
        txtMessages.removeChild(txtMessages.firstChild);
    $("#message").val("");
}
 

Методы sendMessage, addMessage2List

Ниже представлен листинг метода отправки сообщения sendMessage на сервер и метод представления поступившего сообщения от сервера (от других участников чата). Перед отправкой сообщения на сервер оно конвертируется в объект JSON. Сервер, получивший сообщение, рассылает его всем участникам чата. Поступившее пользователю сообщение методом addMessage2List отображается в списке : личные сообщения выравниваются по правому краю компонента, остальные – по левому.

листинг


function sendMessage()
{
   let msg = {
      from : $("#user").val(),
      chat : $("#chat").val(), 
      type : "message",
      content : $("#message").val()
   };
   // конвертация JavaScript-объекта в JSON 
   let msgJSON = JSON.stringify(msg);
   // отправка объекта на сервер
   ws.send(msgJSON);
}
//---------------------------------------------------------
function addMessage2List(msg)
{
    let from = null;
    let time = new Date();

    // определение стиля выравнивания сообщения
    let align  = "";
    let margin = "";
    if (msg.from === $("#user").val()) {
        align  = "text-align:right";
    	margin = 'margin:0px 0px 0px 130px;';
    	from   = time.toLocaleTimeString() + "<br />";
    } else {
        align  = "text-align:left";
    	margin = 'margin:0px;';
    	from   = time.toLocaleTimeString() + 
                 ' ' + msg.from + "<br />";
    }
    let style = 'width:320px;' + margin + align;
    
    // создание элемента DOM
    let span  = document.createElement("p");
    span.setAttribute('style', style);
    
    // определение текста сообщения
    span.innerHTML = from + msg.content + "<br />";
    // добавление сообщения к списку
    txtMessages.appendChild(span);
}
 

Серверная часть WebSocket'a

Сервер WebSocket работает аналогично клиентам WebSocket : он также реагирует на определенные события. Независимо от используемого языка программирования каждый сервер WebSocket выполняет определенные действия. Как правило, примеры описания серверной части WebSocket'a связаны с использованием Node.JS. Здесь же будет представлено описание серверной части WebSocket'a на Java, реализованное на этой странице и включающее следующие модули :

  • WebSocketMessage
  • MessageEncoder
  • MessageDecoder
  • WebSocketUser
  • WebSocketEndpoint

Листинг методов WebSocketMessage, MessageEncoder, MessageDecoder

Ниже представлены листинги следующих классов :

  • WebSocketMessage — класс обмена сообщениями между клиентским и серверным WebSocket'ами;
  • MessageEncoder — кодировщик сообщений;
  • MessageDecoder — декодировщик сообщений.

WebSocketMessage включает поля, определяющие отправителя from, наименование чата chat, тип сообщения type и текст сообщения content. Классы кодировки MessageEncoder и декодировки MessageDecoder сообщений используют com.google.gson.Gson для форматирования объектов сообщений.

листинг


public class WebSocketMessage
{
    private String from;
    private String chat;
    private String type;
    private String content;

    public String getFrom() {
        return from;
    }
    public void setFrom(String from) {
        this.from = from;
    }
    public String getChat() {
        return chat;
    }
    public void setChat(String chat) {
        this.chat = chat;
    }
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
}
//---------------------------------------------------------
import com.google.gson.Gson;

import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
import javax.websocket.EncodeException;

public class MessageEncoder 
             implements Encoder.Text<WebSocketMessage>
{
    private static Gson gson = new Gson();
    @Override
    public String encode(WebSocketMessage message) 
                                 throws EncodeException {
        String json = gson.toJson(message);
        return json;
    }
    @Override
    public void init(EndpointConfig endpointConfig) {
        // Custom initialization logic
    }
    @Override
    public void destroy() {
        // Close resources
    }
}
//---------------------------------------------------------
import com.google.gson.Gson;

import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
import javax.websocket.DecodeException;

public class MessageDecoder
             implements Decoder.Text<WebSocketMessage>
{
    private static Gson gson = new Gson();

    @Override
    public WebSocketMessage decode(String s) 
                            throws DecodeException
    {
        WebSocketMessage message;
    	message = gson.fromJson(s, WebSocketMessage.class);
        return message;
    }
    @Override
    public boolean willDecode(String s) {
        return (s != null);
    }
    @Override
    public void init(EndpointConfig endpointConfig) {
        // Custom initialization logic
    }
    @Override
    public void destroy() {
        // Close resources
    }
}
 

Листинг метода WebSocketUser

Класс описания пользователя, включающего наименования чата chat с пользователем user и объект сессии session. Экземпляр объекта WebSocketUser используется сервером для учета чатов и связанных с ним пользователей : дублирование пользователей в чате не допускается.

листинг


import javax.websocket.Session;

public class WebSocketUser 
{
    private  String   chat;
    private  String   user;
    private  Session  session;

    public WebSocketUser(final Session session, 
                         final String chat, 
                         final String user)
    {
        this.session = session;
        this.chat    = chat;
        this.user    = user;
    }
    public Session getSession() {
        return session;
    }
    public void setSession(Session session) {
        this.session = session;
    }
    public String getChat() {
        return chat;
    }
    public void setChat(String chat) {
        this.chat = chat;
    }
    public String getUser() {
        return user;
    }
    public void setUser(String user) {
        this.user = user;
    }
}
 

Листинг метода WebSocketEndpoint

Ниже представлен листинг основного класса WebSocketEndpoint, формирующего серверную часть WebSocket'a и организующего взаимодействие с клиентскими частями web-сокетов. Имеется 2 способа формирования конечной точки WebSocket'a : либо использование анотации @ServerEndpoint, либо расширение (extends) класса javax.websocket.server.ServerEndpoint. При описании WebSocketEndpoint используется аннотация @ServerEndpoint, которая включает одно обязательное поле/значение и два опциональных (возможно использование 4-х полей). Модуль, наследующий свойства javax.websocket.server.ServerEndpoint (аннотируемый @ServerEndpoint), имеет следующие методы :

  • @OnOpen — метод вызывается контейнером (Tomcat,JBoss, ...) при установлении соединения с клиентским WebSocket'ом;
  • @OnMessage — метод вызывается контейнером при поступлении сообщения от клиентского WebSocket'a;
  • @OnError — метод вызывается при возникновении проблем с коммуникациями;
  • @OnClose — метод вызывается при закрытии соединения с клиентским WebSocket'ом.

Кроме обязательных методов WebSocketEndpoint включает следующие дополнительные методы :

void userListDistribution(final String chat) метод рассылки сообщений со списком участников чата;
boolean addUser(Session session,
String chat_name, String user_name)
метод добавления пользователя в чат : true - пользователь добавлен, false - ошибка добавления;
WebSocketUser getWebSocketUser(Session session) метод получения пользователя (WebSocketUser) из списка зарегистрированных участников по объекту сессии Session;
void broadcast(WebSocketMessage wsmsg) метод рассылки сообщений WebSocketMessage всем участникам чата.

В листинге каждый метод включает комментарии.

листинг


import java.io.IOException;
import java.util.ArrayList;

import java.util.List;

import javax.websocket.EncodeException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/websocket/{username}", 
                decoders = MessageDecoder.class, 
                encoders = MessageEncoder.class)
public class WebSocketEndpoint
{
    // список зарегистрированных пользователей чатов
    private List<WebSocketUser> chat_list = null;

    // шаблон сообщения создания соединения 
    String MSG_CONNECTION = "{'type':'connection',
                              'username':'%s',
                              'result':%s}";
    // шаблон списка пользователей 
    String MSG_USER_LIST  = "{'type':'userlist',
                              'users':[%s]}";
    //-----------------------------------------------------
    /**
     * Метод onOpen вызывается контейнером при создании
     * соединения с клиентским WebSocket'ом
     */
    @OnOpen
    public void onOpen(Session session, 
                    @PathParam("username") String username)
                    throws IOException, EncodeException {
        // определение чата и пользователя
        String   chat = null;
        String   user = null;
        String[] data = username.split("_");
        if (data.length > 1) {
            chat  = data[0];
            user  = data[1];
        } else 
            user  = username;

        String content = null;
        try {
            // добавление пользователя
            boolean bool = addUser(session, chat, user); 
            // результат добавления
            String  result = (bool) ? "true" : "false";
            // формирование ответного сообщения
            content = String.format(MSG_CONNECTION, 
                                    chat, user, result);
            synchronized (session) {
                // отправка сообщения
                session.getAsyncRemote().sendText(content);
            }
        } catch (Exception e) {}

        // набор чатов/пользователей
        if (chat_list == null)
            chat_list = new ArrayList<WebSocketUser>();
        if (chat_list.size() > 1) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {}
            // рассылка списка пользователей чата
            userListDistribution(chat);
        }
    }
    //-----------------------------------------------------
    /**
     * Метод onOpen вызывается контейнером при поступлении
     * сообщения от клиентских WebSocket'ом
     */
    @OnMessage
    public void onMessage(final Session session, 
                          WebSocketMessage msg) 
                      throws IOException, EncodeException {
        // рассылка сообщения пользователя чата
        if (msg.getType().equalsIgnoreCase("message"))
            broadcast(msg);
    }
    //-----------------------------------------------------
    /**
     * Метод onClose вызывается контейнером при закрытии
     * соединения с клиентским WebSocket'ом
     */
    @OnClose
    public void onClose(Session session) 
                       throws IOException, EncodeException {
        // получение пользователя
        WebSocketUser wuser = getWebSocketUser(session);
        // удаление пользователя из списка
        chat_list.remove(wuser);
        // рассылка обновленного списка пользователей чата
        userListDistribution();
    }
    //-----------------------------------------------------
    /**
     * Метод обработки ошибки
     */
    @OnError
    public void onError(Session session, Throwable t) {
        // Do error handling here
    }
    //-----------------------------------------------------
    /**
     * Метод рассылки списка пользователей чата
     */
    private void userListDistribution(final String chat) 
                       throws IOException, EncodeException {
        if (chat_list.size() == 0)
            return;
        // список пользователей
        String users = "";
        int    cnt   = 0;
        for (int i = 0; i < chat_list.size(); i++) {
             WebSocketUser wuser = chat_list.get(i);
            if (cnt > 0)
                users += ",";
            if (wuser.getChat().equalsIgnoreCase(chat)) {
                users += "\"" + wuser.getUser() + "\"";
                cnt++;
            }
        }
        // сообщение со списком пользователей
        String message=String.format(MSG_USER_LIST, users);

        if (users.length() > 0) {
            // рассылка списка пользователей в цикле 
            for(int i = 0; i < chat_list.size(); i++){
                WebSocketUser wuser = chat_list.get(i);
                // проверка принадлежности пользователя 
                // к чату и состояние сессии
                if (wuser.getChat().equalsIgnoreCase(chat)
                    && wuser.getSession().isOpen()){
                        synchronized (wuser.getSession()) {
                            try {
                                // отправка сообщения
                                wuser.getSession()
                                     .getAsyncRemote()
                                     .sendText(message);
                            } catch (Exception e) {}
                        }
                    }
                }
            }
        }
    }
    //-----------------------------------------------------
    /**
     * Метод добавления пользователя в набор
     */
    private boolean addUser(final Session session, 
                            final String chat_name, 
                            final String user_name) {
        boolean result = false;
        for (int i = 0; i < chat_list.size(); i++) {
            if (chat_list.get(i)
                         .getChat()
                         .equalsIgnoreCase(chat_name) && 
                chat_list.get(i)
                         .getUser()
                         .equalsIgnoreCase(user_name)) {
                break;
            }
        }
        if (!result) {
            chat_list.add(new WebSocketUser(session, 
                                            chat_name,
                                            user_name));
            result = true;
        }
        return result;
    }
    //-----------------------------------------------------
    /**
     * Метод чтения пользователя
     */
    private WebSocketUser getWebSocketUser(Session session)
    {
        WebSocketUser wuser = null;
        // Цикл перебора
        for (int i = 0; i < chat_list.size(); i++) {
            // Контроль сессии
            if (chat_list.get(i)
                         .getSession()
                         .getId()
                         .equals(session.getId())){
                wuser = chat_list.get(i);
                break;
            }
        }
        return wuser;
    }
    //-----------------------------------------------------
    /**
     * Метод рассылки сообщений
     */
    private void broadcast(WebSocketMessage wsmsg) 
                       throws IOException, EncodeException {
        // Цикл перебора списка пользователй
        for(WebSocketUser wuser : chat_list){
            // Контроль принадлежности к чату
            if (wuser.getChat()
                     .equalsIgnoreCase(wsmsg.getChat())) {
                synchronized (wuser) {
                    try {
                        // Отправка сообщения
                        wuser.getSession()
                             .getAsyncRemote()
                             .sendObject(wsmsg);
                    } catch (Exception e) {}
                }
            }
       }
    }
}
 
  Рейтинг@Mail.ru