Zoeken…


Opmerkingen

WebSocket is een protocol dat communicatie tussen de client en de server / eindpunt mogelijk maakt met behulp van een enkele TCP-verbinding.

WebSocket is ontworpen om te worden geïmplementeerd in webbrowsers en webservers, maar kan door elke client of servertoepassing worden gebruikt.

Dit onderwerp over de Java API's voor websockets die zijn ontwikkeld door JSR 356 en zijn opgenomen in de Java EE 7-specificaties.

Een WebSocket-communicatie maken

WebSocket biedt een duplex / bidirectioneel communicatieprotocol via een enkele TCP-verbinding.

  • de client opent een verbinding met een server die luistert naar een WebSocket-aanvraag
  • een client maakt verbinding met een server met behulp van een URI.
  • Een server kan luisteren naar aanvragen van meerdere clients.

Server eindpunt

U kunt een WebSocket-serveringang maken door gewoon een POJO te annoteren met @ServerEndpoint . @OnMessage decoreert een methode die inkomende berichten ontvangt. @OnOpen kan worden gebruikt om een methode te decoreren die moet worden aangeroepen wanneer een nieuwe verbinding van een peer wordt ontvangen. Op dezelfde manier wordt een methode geannoteerd met @OnClose aangeroepen wanneer een verbinding wordt @OnClose .

@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);
    }

}

Client Enpoint

Net als het server-eindpunt kunt u een WebSocket-client-eindpunt maken door een POJO te annoteren met @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);
    }
}

Encoders en decoder: Objectgeoriënteerde WebSockets

Dankzij encoders en decoders biedt de JSR 356 objectgerichte communicatiemodellen.

Definitie van berichten

Laten we aannemen dat alle ontvangen berichten door de server moeten worden getransformeerd voordat ze worden teruggestuurd naar alle verbonden sessies:

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

Laten we nu aannemen dat de server twee berichttypen beheert: een tekstgebaseerd bericht en een geheel getal-bericht.

Geheel getal berichten vermenigvuldigen de inhoud zelf.

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;
    }
}

Stringbericht plaatst tekst voor:

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;
    }
}

Encoders en decoder

Er is één encoder per berichttype en één decoder voor alle berichten. Encoders moet werktuigen Encoder.XXX<Type> -interface wanneer Decoder must werktuigen Decoder.XXX<Type> .

Codering is vrij eenvoudig: vanuit een bericht moet de encode een JSON-geformatteerde String uitvoeren. Hier is het voorbeeld voor 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");
    }
}

Soortgelijke codering voor StringMsg klasse. Het is duidelijk dat encoders kunnen worden ontbonden via abstracte klassen.

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");
    }

}

Decoder verloopt in twee stappen: controleren of het ontvangen bericht in het uitgezonderde formaat past met willDecode en het ontvangen onbewerkte bericht vervolgens transformeren in een object met decode :

public class MsgDecoder implementeert 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

De Server EndPoint lijkt behoorlijk op de WebSocket-communicatie met drie belangrijke verschillen:

  1. ServerEndPoint-annotatie heeft de kenmerken encoders en decoders

  2. Berichten worden niet verzonden met sendText maar met sendObject

  3. OnError-annotatie wordt gebruikt. Als er tijdens willDecode een fout is willDecode , wordt deze hier verwerkt en wordt foutinformatie teruggestuurd naar de client

    @ServerEndpoint (value = "/ webSocketObjectEndPoint", decoders = {MsgDecoder.class}, encoders = {StringMsgEncoder.class, IntegerMsgEncoder.class}) publieke klasse 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);
        });
    }
    

    }

Omdat ik vrij uitgebreid was, is hier een eenvoudige JavaScript-client voor degenen die een visueel voorbeeld willen hebben. Houd er rekening mee dat dit een chatachtig voorbeeld is: alle aangesloten partijen ontvangen het antwoord.

<!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>

Code is compleet en is getest onder Payara 4.1. Voorbeeld is pure standaard (geen externe bibliotheek / framework)



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow