Recherche…


Remarques

WebSocket est un protocole qui permet la communication entre le client et le serveur / noeud final à l'aide d'une seule connexion TCP.

WebSocket est conçu pour être implémenté dans les navigateurs Web et les serveurs Web, mais il peut être utilisé par n'importe quelle application client ou serveur.

Cette rubrique sur les API Java pour les Websockets développées par JSR 356 et intégrées aux spécifications Java EE 7.

Créer une communication WebSocket

WebSocket fournit un protocole de communication duplex / bidirectionnel sur une seule connexion TCP.

  • le client ouvre une connexion à un serveur qui écoute une requête WebSocket
  • un client se connecte à un serveur en utilisant un URI.
  • Un serveur peut écouter les demandes de plusieurs clients.

Point de terminaison du serveur

Vous pouvez créer un entpoint de serveur WebSocket en annotant simplement un POJO avec @ServerEndpoint . @OnMessage décore une méthode qui reçoit les messages entrants. @OnOpen peut être utilisé pour décorer une méthode à appeler lorsqu'une nouvelle connexion d'un homologue est reçue. De même, une méthode annotée avec @OnClose est appelée lorsqu'une connexion est fermée.

@ServerEndpoint("/websocket")
public class WebSocketServerEndpoint
{

    @OnOpen
    public void open(Session session) {
     System.out.println("a client connected");
    }

    @OnClose
    public void close(Session session) {
     System.out.println("a client disconnected");
    }

    @OnMessage
    public void handleMessage(String message) {
        System.out.println("received a message from a websocket client! " + message);
    }

}

Point de contact client

De manière similaire au point de terminaison du serveur, vous pouvez créer un point de terminaison client WebSocket en annotant un POJO avec @ClientEndpoint .

@ClientEndpoint
public class WebsocketClientEndpoint {

    Session userSession = null;
    
    // in our case i.e. "ws://localhost:8080/myApp/websocket"
    public WebsocketClientEndpoint(URI endpointURI) {
        WebSocketContainer container = ContainerProvider.getWebSocketContainer();
        container.connectToServer(this, endpointURI);
    }

    @OnOpen
    public void onOpen(Session userSession) {
        System.out.println("opening websocket");
        this.userSession = userSession;
    }

    @OnClose
    public void onClose(Session userSession, CloseReason reason) {
        System.out.println("closing websocket");
        this.userSession = null;
    }

    @OnMessage
    public void onMessage(String message) {
        System.out.println("received message: "+ message);
    }

    public void sendMessage(String message) {
        System.out.println("sending message: "+ message);
        this.userSession.getAsyncRemote().sendText(message);
    }
}

Codeurs et décodeur: WebSockets orientés objet

Grâce aux encodeurs et aux décodeurs, le JSR 356 propose des modèles de communication orientés objet.

Définition des messages

Supposons que tous les messages reçus doivent être transformés par le serveur avant d'être renvoyés à toutes les sessions connectées:

public abstract class AbstractMsg {
    public abstract void transform();
}

Supposons maintenant que le serveur gère deux types de messages: un message textuel et un message basé sur un entier.

Les messages entiers multiplient le contenu par lui-même.

public class IntegerMsg extends AbstractMsg {

    private Integer content;

    public IntegerMsg(int content) {
        this.content = content;
    }

    public Integer getContent() {
        return content;
    }

    public void setContent(Integer content) {
        this.content = content;
    }

    @Override
    public void transform() {
        this.content = this.content * this.content;
    }
}

Message de chaîne précédant un texte:

public class StringMsg extends AbstractMsg {

    private String content;

    public StringMsg(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public void transform() {
        this.content = "Someone said: " + this.content;
    }
}

Codeurs et décodeur

Il existe un encodeur par type de message et un seul décodeur pour tous les messages. Les encodeurs doivent implémenter l' Encoder.XXX<Type> lorsque Decoder doit implémenter Decoder.XXX<Type> .

Le codage est assez simple: à partir d'un message, la méthode d' encode doit générer une chaîne au format JSON. Voici l'exemple pour IntegerMsg .

public class IntegerMsgEncoder implements Encoder.Text<IntegerMsg> {

    @Override
    public String encode(IntegerMsg object) throws EncodeException {
        JsonObjectBuilder builder = Json.createObjectBuilder();
        
        builder.add("content", object.getContent());
        
        JsonObject jsonObject = builder.build();
        return jsonObject.toString();
    }

    @Override
    public void init(EndpointConfig config) {
        System.out.println("IntegerMsgEncoder initializing");
    }

    @Override
    public void destroy() {
        System.out.println("IntegerMsgEncoder closing");
    }
}

StringMsg similaire pour la classe StringMsg . De toute évidence, les encodeurs peuvent être factorisés via des classes abstraites.

public class StringMsgEncoder implements Encoder.Text<StringMsg> {

    @Override
    public String encode(StringMsg object) throws EncodeException {
        JsonObjectBuilder builder = Json.createObjectBuilder();

        builder.add("content", object.getContent());

        JsonObject jsonObject = builder.build();
        return jsonObject.toString();
    }

    @Override
    public void init(EndpointConfig config) {
        System.out.println("StringMsgEncoder initializing");
    }

    @Override
    public void destroy() {
        System.out.println("StringMsgEncoder closing");
    }

}

Le décodeur se déroule en deux étapes: vérifier si le message reçu correspond au format excepté avec willDecode , puis transformer le message brut reçu en un objet avec decode :

la classe publique MsgDecoder implémente Decoder.Text {

@Override
public AbstractMsg decode(String s) throws DecodeException {
    // Thanks to willDecode(s), one knows that
    // s is a valid JSON and has the attribute
    // "content"
    JsonObject json = Json.createReader(new StringReader(s)).readObject();
    JsonValue contentValue = json.get("content");

    // to know if it is a IntegerMsg or a StringMsg, 
    // contentValue type has to be checked:
    switch (contentValue.getValueType()) {
        case STRING:
            String stringContent = json.getString("content");
            return new StringMsg(stringContent);
        case NUMBER:
            Integer intContent = json.getInt("content");
            return new IntegerMsg(intContent);
        default:
            return null;
    }

}

@Override
public boolean willDecode(String s) {

    // 1) Incoming message is a valid JSON object
    JsonObject json;
    try {
        json = Json.createReader(new StringReader(s)).readObject();
    }
    catch (JsonParsingException e) {
        // ...manage exception...
        return false;
    }
    catch (JsonException e) {
        // ...manage exception...
        return false;
    }

    // 2) Incoming message has required attributes
    boolean hasContent = json.containsKey("content");

    // ... proceed to additional test ...
    return hasContent;
}

@Override
public void init(EndpointConfig config) {
    System.out.println("Decoding incoming message...");
}

@Override
public void destroy() {
    System.out.println("Incoming message decoding finished");
}

}

ServerEndPoint

Le serveur EndPoint ressemble à la communication WebSocket avec trois différences principales:

  1. Annotation ServerEndPoint a les encoders et decoders attributs

  2. Les messages ne sont pas envoyés avec sendText mais avec sendObject

  3. L'annotation OnError est utilisée. S'il y avait une erreur lors de willDecode , elle sera traitée ici et les informations d'erreur seront renvoyées au client.

    @ServerEndpoint (value = "/ webSocketObjectEndPoint", decoders = {MsgDecoder.class}, encoders = {StringMsgEncoder.class, IntegerMsgEncoder.class}) classe publique ServerEndPoint {

    @OnOpen
    public void onOpen(Session session) {
        System.out.println("A session has joined");
    }
    
    @OnClose
    public void onClose(Session session) {
        System.out.println("A session has left");
    }
    
    @OnMessage
    public void onMessage(Session session, AbstractMsg message) {
        if (message instanceof IntegerMsg) {
            System.out.println("IntegerMsg received!");
        } else if (message instanceof StringMsg) {
            System.out.println("StringMsg received!");
        }
    
        message.transform();
        sendMessageToAllParties(session, message);
    }
    
    @OnError
    public void onError(Session session, Throwable throwable) {
        session.getAsyncRemote().sendText(throwable.getLocalizedMessage());
    }
    
    private void sendMessageToAllParties(Session session, AbstractMsg message) {
        session.getOpenSessions().forEach(s -> {
            s.getAsyncRemote().sendObject(message);
        });
    }
    

    }

Comme j'étais très verbeux, voici un client JavaScript de base pour ceux qui veulent avoir un exemple visuel. S'il vous plaît noter que ceci est un exemple de chat: toutes les parties connectées recevront la réponse.

<!DOCTYPE html>
<html>
    <head>
        <title>Websocket-object</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">        
        <!-- start of BAD PRACTICE! all style and script must go into a
             dedicated CSS / JavaScript file-->
        <style>
            body{
                background: dimgray;
            }

            .container{
                width: 100%;
                display: flex;
            }

            .left-side{
                width: 30%;
                padding: 2%;
                box-sizing:  border-box;
                margin: auto;
                margin-top: 0;
                background: antiquewhite;
            }
            .left-side table{
                width: 100%;
                border: 1px solid black;
                margin: 5px;
            }
            .left-side table td{
                padding: 2px;
                width: 50%;
            }
            .left-side table input{
                width: 100%;
                box-sizing: border-box;
            }

            .right-side{
                width: 70%;
                background: floralwhite;
            }
        </style>

        <script>
            var ws = null;
            window.onload = function () {
                // replace the 'websocket-object' with the
                // context root of your web application.
                ws = new WebSocket("ws://localhost:8080/websocket-object/webSocketObjectEndPoint");
                ws.onopen = onOpen;
                ws.onclose = onClose;
                ws.onmessage = onMessage;
            };

            function onOpen() {
                printText("", "connected to server");
            }

            function onClose() {
                printText("", "disconnected from server");
            }

            function onMessage(event) {
                var msg = JSON.parse(event.data);
                printText("server", JSON.stringify(msg.content));
            }

            function sendNumberMessage() {
                var content = new Number(document.getElementById("inputNumber").value);
                var json = {content: content};
                ws.send(JSON.stringify(json));
                printText("client", JSON.stringify(json));
            }

            function sendTextMessage() {
                var content = document.getElementById("inputText").value;
                var json = {content: content};
                ws.send(JSON.stringify(json));
                printText("client", JSON.stringify(json));
            }

            function printText(sender, text) {
                var table = document.getElementById("outputTable");
                var row = table.insertRow(1);
                var cell1 = row.insertCell(0);
                var cell2 = row.insertCell(1);
                var cell3 = row.insertCell(2);

                switch (sender) {
                    case "client":
                        row.style.color = "orange";
                        break;
                    case "server":
                        row.style.color = "green";
                        break;
                    default:
                        row.style.color = "powderblue";
                }
                cell1.innerHTML = new Date().toISOString();
                cell2.innerHTML = sender;
                cell3.innerHTML = text;
            }
        </script>

        <!-- end of bad practice -->
    </head>
    <body>

        <div class="container">
            <div class="left-side">
                <table>
                    <tr>
                        <td>Enter a text</td>
                        <td><input id="inputText" type="text" /></td>
                    </tr>
                    <tr>
                        <td>Send as text</td>
                        <td><input type="submit" value="Send" onclick="sendTextMessage();"/></td>
                    </tr>
                </table>

                <table>
                    <tr>
                        <td>Enter a number</td>
                        <td><input id="inputNumber" type="number" /></td>
                    </tr>
                    <tr>
                        <td>Send as number</td>
                        <td><input type="submit" value="Send" onclick="sendNumberMessage();"/></td>
                    </tr>
                </table>
            </div>
            <div class="right-side">
                <table id="outputTable">
                    <tr>
                        <th>Date</th>
                        <th>Sender</th>
                        <th>Message</th>
                    </tr>
                </table>
            </div>
        </div>
    </body>
</html>

Le code est complet et a été testé sous Payara 4.1. L'exemple est purement standard (pas de bibliothèque / framework externe)



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow