• java websocket学习


    引言:

      websocket,webservice傻傻分不清楚,都觉得是很高深的东西,理解中的webservice是一种协议,通信协议,类似http协议的那种,比如使用webservice协议调后台接口,而websocket呢?与socket挂钩?长连接?对未知的东西总是恐惧的,所以默默不敢说话

    启航:

      学习过程中突然接触到了websocket的简单讲解,哦,websocket也是一种协议,它类似ajax,但连接不中断,接到消息就响应。叫什么双端通信。websocket请求头是ws://或者wss://开头,非安全与安全,后面就和http请求类似。后台写法当然与默认的http servlet有些不同,但变化不大,与springMVC的requestMapping有些相似,接受到请求可以进行拦截等处理,当然也可以限制接收请求的具体参数。

    概念来一波:

      

      

      

    原先实现模拟双端通信的手段:  

    在WebSocket规范提出之前,开发人员若要实现这些实时性较强的功能,经常会使用折衷的解决方法:轮询(polling)Comet技术。其实后者本质上也是一种轮询,只不过有所改进。

      轮询是最原始的实现实时Web应用的解决方案。轮询技术要求客户端以设定的时间间隔周期性地向服务端发送请求,频繁地查询是否有新的数据改动。明显地,这种方法会导致过多不必要的请求,浪费流量和服务器资源。

      Comet技术又可以分为长轮询流技术长轮询改进了上述的轮询技术,减小了无用的请求。它会为某些数据设定过期时间,当数据过期后才会向服务端发送请求;这种机制适合数据的改动不是特别频繁的情况。流技术通常是指客户端使用一个隐藏的窗口与服务端建立一个HTTP长连接,服务端会不断更新连接状态以保持HTTP长连接存活;这样的话,服务端就可以通过这条长连接主动将数据发送给客户端;流技术在大并发环境下,可能会考验到服务端的性能。

      这两种技术都是基于请求-应答模式,都不算是真正意义上的实时技术;它们的每一次请求、应答,都浪费了一定流量在相同的头部信息上,并且开发复杂度也较大。

    特点:

      1、双端通信

      2、建立在TCP之上

      3、协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

    接入:

      注意:JavaEE 7中出了JSR-356:Java API for WebSocket规范。不少Web容器,如Tomcat,Nginx,Jetty等都支持WebSocket。Tomcat从7.0.27开始支持 WebSocket,从7.0.47开始支持JSR-356

    websocket客户端:

      在客户端,没有必要为 WebSockets 使用 JavaScript 库。实现 WebSockets 的 Web 浏览器将通过 WebSockets 对象公开所有必需的客户端功能(主要指支持 Html5 的浏览器)。

    客户端API:  

      以下 API 用于创建 WebSocket 对象。

      var Socket = new WebSocket(url, [protocol] );

      以上代码中的第一个参数 url, 指定连接的 URL。第二个参数 protocol 是可选的,指定了可接受的子协议。

      WebSocket 属性

      以下是 WebSocket 对象的属性。假定我们使用了以上代码创建了 Socket 对象:

    属性描述
    Socket.readyState 只读属性 readyState 表示连接状态,可以是以下值:0 - 表示连接尚未建立。1 - 表示连接已建立,可以进行通信。2 - 表示连接正在进行关闭。3 - 表示连接已经关闭或者连接不能打开。
    Socket.bufferedAmount 只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。

      WebSocket 事件

      以下是 WebSocket 对象的相关事件。假定我们使用了以上代码创建了 Socket 对象:

    事件事件处理程序描述
    open Socket.onopen 连接建立时触发
    message Socket.onmessage 客户端接收服务端数据时触发
    error Socket.onerror 通信发生错误时触发
    close Socket.onclose 连接关闭时触发

      WebSocket 方法

      以下是 WebSocket 对象的相关方法。假定我们使用了以上代码创建了 Socket 对象:

    方法描述
    Socket.send() 使用连接发送数据
    Socket.close() 关闭连接

        客户端实例:   

    <%--
      Created by IntelliJ IDEA.
      User: zhen
      Date: 2018/12/10
      Time: 17:36
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>welcome page</title>
    </head>
    <body>
        welcome to ssm all annotation project!
    
        <input id="text" type="text"/>
        <button onclick="send()">发送消息</button>
        <hr/>
        <button onclick="closeWebSocket()">关闭webSocket连接</button>
        <hr/>
        <div id="message"></div>
        </body>
    
        <script type="text/javascript">
            var webSocket = null;
            //判断当前浏览器是否支持webSocket
            if ('WebSocket' in window) {
                webSocket = new WebSocket("ws://localhost:8080/spring4webSocket/myHandler")
            } else {
                alert("当前浏览器 Not support webSocket");
            }
    
            webSocket.onerror = onError;
            webSocket.onopen = onOpen;
            webSocket.onmessage = onMessage;
            webSocket.onclose = onClose;
    
            function onError() {
                setMessageInnerHTML("WebSocket连接发生错误");
            }
    
            function onOpen() {
                setMessageInnerHTML("WebSocket连接成功");
            }
    
            function onMessage(event){
                //将接受到的数据直接输出
                setMessageInnerHTML(event.data);
            }
    
            function onClose() {
                setMessageInnerHTML("webSocket连接关闭");
            }
    
            function setMessageInnerHTML(message) {
                var messageDiv = document.getElementById("message");
                messageDiv.innerHTML = messageDiv.innerHTML + "<br/>" + message;
            }
    
            //监听串口关闭事件,当窗口关闭时,主动去关闭webSocket连接,防止还没断开就关闭窗口,srever端会抛异常
            window.onbeforeunload = function (ev) {
                closeWebSocket();
            }
    
            function closeWebSocket(){
                webSocket.close();
            }
    
    
            //发送消息
            function send() {
                var message = document.getElementById("text").value;
                webSocket.send(message);
            }
    
        </script>
    </html>
    客户端实例

    服务端(java):

      基于servlet api:

        1、导入有关jar包     

            <dependency>
                <groupId>javax</groupId>
                <artifactId>javaee-api</artifactId>
                <version>7.0</version>
                <scope>provided</scope>
            </dependency>

      API:

        @ServerEnpoint 声明webSocket服务端,指明映射url

        @OnMessage 标注接收到消息执行监听方法

        @OnOpen 标注打开连接时候执行监听方法

        @OnClose 标注关闭连接时执行监听方法

        @OnError 标注连接异常时执行监听方法

      服务端实例:

    package com.zhen.websocket;
    
    /**
     * @author zhen
     * @Date 2018/12/6 10:29
     */
    
    import java.io.*;
    import java.util.*;
    import javax.websocket.EncodeException;
    import javax.websocket.OnClose;
    import javax.websocket.Session;
    import javax.websocket.server.ServerEndpoint;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    
     // @ServerEndpoint 注解允许你声明一个WeoSocket,定义url映射,定义编码和解码
    @ServerEndpoint(
        value="/story/notifications",
        encoders={StickerEncoder.class},
        decoders={StickerDecoder.class}
       )
    public class StoryWebSocket {
    
    
        private static final List<Sticker> stickers = Collections.synchronizedList(new LinkedList<Sticker>());
        private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<Session>());
    
         @OnMessage
         public void onMessage(Session session, Sticker sticker){
             // 有消息从客户端发送过来,保存到列表中,然后通知所有的客户端
             stickers.add(sticker);
             for(Session openSession : sessions){
                 try {
                     openSession.getBasicRemote().sendObject(sticker);
                 } catch (IOException | EncodeException e) {
                     sessions.remove(openSession);
                 }
             }
         }
    
         @OnOpen
         public void onOpen(Session session) throws IOException, EncodeException{
             // 有新的客户端连接时,保存此客户端的session,并且把当前所有的sticker发送给它
             sessions.add(session);
             for(Sticker sticker : stickers){
                 session.getBasicRemote().sendObject(sticker);
             }
         }
    
         @OnClose
         public void onClose(Session session){
             // 有客户端断开连接时 ,从session列表中移除此客户端的session
             sessions.remove(session);
         }
    
    
    }
    服务端实例

    看的有些混乱不能很好理解的时候就敲一些例子,功能出来就更容易理解了。

      

    package com.zhen.websocket;
    
    /**
     * @author zhen
     * @Date 2018/12/6 10:28
     */
    public class Sticker {
    
        private int x;
        private int y;
        private String image;
    
        public Sticker() {
        }
    
        public int getX() {
            return x;
        }
    
        public void setX(int x) {
            this.x = x;
        }
    
        public int getY() {
            return y;
        }
    
        public void setY(int y) {
            this.y = y;
        }
    
        public String getImage() {
            return image;
        }
    
        public void setImage(String image) {
            this.image = image;
        }
    }
    
    package com.zhen.websocket;
    
    import javax.json.JsonObject;
    import javax.json.JsonReader;
    import javax.json.spi.JsonProvider;
    import javax.websocket.DecodeException;
    import javax.websocket.Decoder;
    import javax.websocket.EndpointConfig;
    import java.io.IOException;
    import java.io.Reader;
    
    
    /**
     * @author zhen
     * @Date 2018/12/6 11:08
     * 用来读取webSocket流中的数据使用Decode.TextStream接口。 这个接口允许你读取数据从socket中通过JsonReader对象,构造传入Reader对象,并且转换客户端返回文本JSON数据
     */
    public class StickerDecoder implements Decoder.TextStream<Sticker>{
    
        // Do not create a JsonReader object. To create readers and writes, use the
        // JsonProvider class.
    
        @Override
        public void init(EndpointConfig config) {
            // TODO Auto-generated method stub
        }
    
        @Override
        public void destroy() {
            // TODO Auto-generated method stub
        }
    
        @Override
        public Sticker decode(Reader reader) throws DecodeException, IOException {
            JsonProvider provider = JsonProvider.provider();
            JsonReader jsonReader = provider.createReader(reader);
            JsonObject jsonSticker = jsonReader.readObject();
    
            Sticker sticker = new Sticker();
            sticker.setX(jsonSticker.getInt("x"));
            sticker.setY(jsonSticker.getInt("y"));
            sticker.setImage(jsonSticker.getString("sticker"));
            return sticker;
        }
    
    }
    
    package com.zhen.websocket;
    
    import javax.json.JsonObject;
    import javax.json.JsonWriter;
    import javax.json.spi.JsonProvider;
    import javax.websocket.EncodeException;
    import javax.websocket.Encoder;
    import javax.websocket.EndpointConfig;
    import java.io.IOException;
    import java.io.Writer;
    
    /**
     * @author zhen
     * @Date 2018/12/6 10:59
     * 这个类编码Sticker对象并且传递给WebSocket服务器通过写入流中
     */
    public class StickerEncoder implements Encoder.TextStream<Sticker> {
    
        @Override
        public void init(EndpointConfig config) {
            // TODO Auto-generated method stub
        }
    
        @Override
        public void destroy() {
            // TODO Auto-generated method stub
        }
    
        @Override
        public void encode(Sticker sticker, Writer writer) throws EncodeException, IOException {
            JsonProvider provider = JsonProvider.provider();
            JsonObject jsonSticker = provider.createObjectBuilder()
                .add("action", "add")
                .add("x", sticker.getX())
                .add("y", sticker.getY())
                .add("sticker", sticker.getImage())
                .build();
            JsonWriter jsonWriter = provider.createWriter(writer);
            jsonWriter.write(jsonSticker);
        }
    
    }
    
    package com.zhen.websocket;
    
    /**
     * @author zhen
     * @Date 2018/12/6 10:29
     */
    
    import java.io.*;
    import java.util.*;
    import javax.websocket.EncodeException;
    import javax.websocket.OnClose;
    import javax.websocket.Session;
    import javax.websocket.server.ServerEndpoint;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    
     // @ServerEndpoint 注解允许你声明一个WeoSocket,定义url映射,定义编码和解码
    @ServerEndpoint(
        value="/story/notifications",
        encoders={StickerEncoder.class},
        decoders={StickerDecoder.class}
       )
    public class StoryWebSocket {
    
    
        private static final List<Sticker> stickers = Collections.synchronizedList(new LinkedList<Sticker>());
        private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<Session>());
    
         @OnMessage
         public void onMessage(Session session, Sticker sticker){
             // 有消息从客户端发送过来,保存到列表中,然后通知所有的客户端
             stickers.add(sticker);
             for(Session openSession : sessions){
                 try {
                     openSession.getBasicRemote().sendObject(sticker);
                 } catch (IOException | EncodeException e) {
                     sessions.remove(openSession);
                 }
             }
         }
    
         @OnOpen
         public void onOpen(Session session) throws IOException, EncodeException{
             // 有新的客户端连接时,保存此客户端的session,并且把当前所有的sticker发送给它
             sessions.add(session);
             for(Sticker sticker : stickers){
                 session.getBasicRemote().sendObject(sticker);
             }
         }
    
         @OnClose
         public void onClose(Session session){
             // 有客户端断开连接时 ,从session列表中移除此客户端的session
             sessions.remove(session);
         }
    
    
    }
    
    
    
    
    var socket = null;
    function initialize() {
        var canvas = document.getElementById("board");
        var ctx = canvas.getContext("2d");
        var img = document.getElementById("background_img");
        ctx.drawImage(img, 0, 0);
        socket = new WebSocket("ws://localhost:8080/stickStory/story/notifications");
        socket.onmessage = onSocketMessage;
    }
    
    function drag(ev) {
        var bounds = ev.target.getBoundingClientRect();
        var draggedSticker = {
            sticker: ev.target.getAttribute("data-sticker"),
            offsetX: ev.clientX - bounds.left,
            offsetY: ev.clientY - bounds.top
        };
        var draggedText = JSON.stringify(draggedSticker);
        ev.dataTransfer.setData("text", draggedText);
    }
    
    function drop(ev) {
        ev.preventDefault();
        var bounds = document.getElementById("board").getBoundingClientRect();
        var draggedText = ev.dataTransfer.getData("text");
        var draggedSticker = JSON.parse(draggedText);
        var stickerToSend = {
            action: "add",
            x: ev.clientX - draggedSticker.offsetX - bounds.left,
            y: ev.clientY - draggedSticker.offsetY - bounds.top,
            sticker: draggedSticker.sticker
        };
        socket.send(JSON.stringify(stickerToSend));
        log("Sending Object " + JSON.stringify(stickerToSend));
    }
    
    function allowDrop(ev) {
        ev.preventDefault();
    }
    
    function onSocketMessage(event) {
        if (event.data) {
            var receivedSticker = JSON.parse(event.data);
            log("Received Object: " + JSON.stringify(receivedSticker));
            if (receivedSticker.action === "add") {
                var imageObj = new Image();
                imageObj.onload = function() {
                    var canvas = document.getElementById("board");
                    var context = canvas.getContext("2d");
                    context.drawImage(imageObj, receivedSticker.x, receivedSticker.y);
                };
                imageObj.src = "resources/stickers/" + receivedSticker.sticker;
            }
        }
    }
    
    function toggleLog() {
        var log = document.getElementById("logContainer");
        if (!log.getAttribute("style")) {
            log.setAttribute("style", "display:block;");
        } else {
            log.setAttribute("style", "");
        }
    }
    
    var logCount = 0;
    function log(logstr) {
        var logElement = document.getElementById("log");
        logElement.innerHTML = "<b>[" + logCount + "]: </b>" + logstr + "<br>" + logElement.innerHTML;
        logCount++;
    }
    
    window.onload = initialize;
    
    
    <?xml version='1.0' encoding='UTF-8' ?>
    <!DOCTYPE html>
    <html>
    <head>
        <title>Sticker Story</title>
        <link href="resources/styles.css" rel="stylesheet" type="text/css" >
        <script src="resources/story-page.js" type="text/javascript"></script>
    </head>
    <body>
    <header>
        <h1>Sticker Story Book</h1>
    </header>
    <nav>
        Drag stickers from the left bar to the canvas.
    </nav>
    <aside>
        <h2>Stickers</h2>
        <div id="stickerContainer">
            <img src="resources/stickers/bear.png"
                 data-sticker="bear.png"
                 style="float:left"
                 draggable="true" ondragstart="drag(event);" >
            <img src="resources/stickers/chicken.png"
                 data-sticker="chicken.png"
                 style="float:left"
                 draggable="true" ondragstart="drag(event);" >
            <img src="resources/stickers/leopard.png"
                 data-sticker="leopard.png"
                 style="float:left"
                 draggable="true" ondragstart="drag(event);" >
            <img src="resources/stickers/monkey.png"
                 data-sticker="monkey.png"
                 style="float:left"
                 draggable="true" ondragstart="drag(event);" >
            <img src="resources/stickers/horse.png"
                 data-sticker="horse.png"
                 style="float:left"
                 draggable="true" ondragstart="drag(event);" >
            <img src="resources/stickers/tiger.png"
                 data-sticker="tiger.png"
                 style="float:left"
                 draggable="true" ondragstart="drag(event);" >
        </div>
    </aside>
    <div id="content">
        <canvas id="board" width="1000" height="580" ondrop="drop(event);"
                ondragover="allowDrop(event);">
            Canvas Not Supported.
        </canvas>
        <img src="resources/canvas2.png" id="background_img"
             width="1000" height="580" style="display:none;"/>
    </div>
    <footer>
        <small>Made with HTML5 + WebSockets and JSON</small>
        <ol>
            <li onclick="toggleLog();">Log</li>
        </ol>
    </footer>
    <div id="logContainer">
        <h2>log</h2>
        <div id="log"></div>
    </div>
    </body>
    </html>
    
    
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.zhen</groupId>
        <artifactId>StickStory</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>war</packaging>
    
    
        <dependencies>
    
            <!-- Servlet -->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.0.1</version>
            </dependency>
    
    
            <dependency>
                <groupId>javax</groupId>
                <artifactId>javaee-api</artifactId>
                <version>7.0</version>
                <scope>provided</scope>
            </dependency>
    
            <!-- JSON工具包 -->
            <dependency>
                <groupId>org.glassfish</groupId>
                <artifactId>javax.json</artifactId>
                <version>1.0.4</version>
            </dependency>
    
    
    
    
        </dependencies>
    
        <build>
            <plugins>
                <!-- 编译插件 -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.5.1</version>
                    <configuration>
                        <source>1.7</source>
                        <target>1.7</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
    
                <!-- tomcat 插件 -->
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <version>2.2</version>
                    <configuration>
                        <port>8080</port>
                        <path>/stickStory</path>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    第一篇敲的项目代码

    这里接受返回消息用到了转换器,接受对象类型json返回对象类型json

    实现发布订阅模式

    项目目录如下:

    第二篇敲的项目代码:

    package com.zhen.model;
    
    /**
     * @author zhen
     * @Date 2018/12/6 15:30
     */
    public class Device {
    
        private int id;
        private String name;
        private String status;
        private String type;
        private String description;
    
        public Device() {
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getStatus() {
            return status;
        }
    
        public void setStatus(String status) {
            this.status = status;
        }
    
        public String getType() {
            return type;
        }
    
        public void setType(String type) {
            this.type = type;
        }
    
        public String getDescription() {
            return description;
        }
    
        public void setDescription(String description) {
            this.description = description;
        }
    }
    
    
    package com.zhen.websocket;
    
    import com.zhen.model.Device;
    
    import javax.enterprise.context.ApplicationScoped;
    import javax.json.JsonObject;
    import javax.json.spi.JsonProvider;
    import javax.websocket.Session;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    /**
     * @author zhen
     * @Date 2018/12/6 15:50
     */
    @ApplicationScoped
    public class DeviceSessionHandler {
    
        private final Set<Session> sessions = new HashSet<>();
        private final Set<Device> devices = new HashSet<>();
        private static AtomicInteger deviceId = new AtomicInteger(0);
    
        public void addSession(Session session) {
            sessions.add(session);
            for (Device device : devices) {
                JsonObject addMessage = createAndMessage(device);
                sendToSession(session, addMessage);
            }
        }
    
        public void removeSession(Session session) {
            sessions.remove(session);
        }
    
        public void addDevice(Device device) {
            device.setId(deviceId.incrementAndGet());
            devices.add(device);
            JsonObject addMessage = createAndMessage(device);
            sendToAllConnectedSessions(addMessage);
        }
    
        public void removeDevice(int id) {
            Device device = getDeviceById(id);
            if (device != null) {
                devices.remove(device);
                JsonProvider provider = JsonProvider.provider();
                JsonObject removeMessage = provider.createObjectBuilder()
                    .add("action", "remove")
                    .add("id", id)
                    .build();
                sendToAllConnectedSessions(removeMessage);
            }
        }
    
        public void toggleDevice(int id) {
            JsonProvider provider = JsonProvider.provider();
            Device device = getDeviceById(id);
            if (device != null) {
                if ("On".equals(device.getStatus())) {
                    device.setStatus("Off");
                } else {
                    device.setStatus("On");
                }
                JsonObject updateDevMessage = provider.createObjectBuilder()
                    .add("action", "toggle")
                    .add("id", device.getId())
                    .add("status", device.getStatus())
                    .build();
                    sendToAllConnectedSessions(updateDevMessage);
            }
        }
    
        public List<Device> getDevices(){
            return new ArrayList<>(devices);
        }
    
        public Device getDeviceById(int id) {
            for (Device device : devices) {
                if (device.getId() == id) {
                    return device;
                }
            }
            return null;
        }
    
        public JsonObject createAndMessage(Device device) {
            JsonProvider provider = JsonProvider.provider();
            JsonObject addMessage = provider.createObjectBuilder()
                .add("action", "add")
                .add("name", device.getName())
                .add("type", device.getType())
                .add("status", device.getStatus())
                .add("description", device.getDescription())
                .build();
            return addMessage;
        }
    
        private void sendToAllConnectedSessions(JsonObject message) {
            for (Session session : sessions) {
                sendToSession(session, message);
            }
        }
    
        private void sendToSession(Session session, JsonObject message) {
            try{
                session.getBasicRemote().sendText(message.toString());
            } catch (IOException ex) {
                sessions.remove(session);
                Logger.getLogger(DeviceSessionHandler.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    
    
    }
    
    package com.zhen.websocket;
    
    import com.zhen.model.Device;
    
    import javax.enterprise.context.ApplicationScoped;
    import javax.inject.Inject;
    import javax.json.Json;
    import javax.json.JsonObject;
    import javax.json.JsonReader;
    import javax.websocket.*;
    import javax.websocket.server.ServerEndpoint;
    import java.io.StringReader;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    /**
     * @author zhen
     * @Date 2018/12/6 15:32
     */
    @ApplicationScoped
    @ServerEndpoint("/actions")
    public class DeviceWebSocketServer {
    
        @Inject
        private DeviceSessionHandler sessionHandler = new DeviceSessionHandler();
    
        @OnOpen
        public void open(Session session) {
            sessionHandler.addSession(session);
        }
    
        @OnClose
        public void close(Session session) {
            sessionHandler.removeSession(session);
        }
    
        @OnError
        public void onError(Throwable error) {
            Logger.getLogger(DeviceWebSocketServer.class.getName()).log(Level.SEVERE, null, error);
        }
    
        @OnMessage
        public void handleMessage(Session session, String message) {
            try(JsonReader reader = Json.createReader(new StringReader(message))){
                JsonObject jsonMessage = reader.readObject();
    
                if ("add".equals(jsonMessage.getString("action"))) {
                    Device device = new Device();
                    device.setName(jsonMessage.getString("name"));
                    device.setDescription(jsonMessage.getString("description"));
                    device.setType(jsonMessage.getString("type"));
                    device.setStatus("Off");
                    sessionHandler.addDevice(device);
                }
    
                if ("remove".equals(jsonMessage.getString("action"))) {
                    int id = (int) jsonMessage.getInt("id");
                    sessionHandler.removeDevice(id);
                }
    
                if ("toggle".equals(jsonMessage.getString("action"))) {
                    int id = (int) jsonMessage.getInt("id");
                    sessionHandler.toggleDevice(id);
                }
            }
        }
    }
    
    
    <?xml version='1.0' encoding='UTF-8' ?>
    <!DOCTYPE html>
    <html>
    <head>
        <title>Index</title>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
        <link rel="stylesheet" href="style.css">
        <script src="websocket.js"></script>
    </head>
    <body>
    <div id="wrapper">
        <h1>Java WebSocket Home</h1>
        <p>Welcome to the Java WebSocket Home. Click the Add a device button to start adding devices.</p>
        <br/>
        <div id="addDevice">
            <div class="button"><a href="#" onclick="showForm()">Add a device</a> </div>
            <form id="addDeviceForm">
                <h3>Add a new device</h3>
                <span>Name: <input type="text" name="device_name" id="device_name"></span>
                <span>
                    Type:
                    <select id="device_type">
                        <option name="type" value="Appliance">Appliance</option>
                        <option name="type" value="Electronics">Electronics</option>
                        <option name="type" value="Lights">Lights</option>
                        <option name="type" value="Other">Other</option>
                    </select>
                </span>
    
                <span>
                    Description:<br/>
                    <textarea name="description" id="device_description" rows="2" cols="50"></textarea>
                </span>
    
                <input type="button" class="button" value="Add" onclick="formSubmit();">
                <input type="reset" class="button" value="Cancel" onclick="hideForm();">
            </form>
        </div>
        <br/>
        <h3>Currently connected devices:</h3>
        <div id="content"></div>
    </div>
    </body>
    </html>
    
    
    body {
        font-family: Arial, Helvetica, sans-serif;
        font-size: 80%;
        background-color: #1f1f1f;
    }
    
    #wrapper {
         960px;
        margin: auto;
        text-align: left;
        color: #d9d9d9;
    }
    
    p {
        text-align: left;
    }
    
    .button {
        display: inline;
        color: #fff;
        background-color: #f2791d;
        padding: 8px;
        margin: auto;
        border-radius: 8px;
        -moz-border-radius: 8px;
        -webkit-border-radius: 8px;
        box-shadow: none;
        border: none;
    }
    
    .button:hover {
        background-color: #ffb15e;
    }
    .button a, a:visited, a:hover, a:active {
        color: #fff;
        text-decoration: none;
    }
    
    #addDevice {
        text-align: center;
         960px;
        margin: auto;
        margin-bottom: 10px;
    }
    
    #addDeviceForm {
        text-align: left;
         400px;
        margin: auto;
        padding: 10px;
    }
    
    #addDeviceForm span {
        display: block;
    }
    
    #content {
        margin: auto;
         960px;
    }
    
    .device {
         180px;
        height: 110px;
        margin: 10px;
        padding: 16px;
        color: #fff;
        vertical-align: top;
        border-radius: 8px;
        -moz-border-radius: 8px;
        -webkit-border-radius: 8px;
        display: inline-block;
    }
    
    .device.off {
        background-color: #c8cccf;
    }
    
    .device span {
        display: block;
    }
    
    .deviceName {
        text-align: center;
        font-weight: bold;
        margin-bottom: 12px;
    }
    
    .removeDevice {
        margin-top: 12px;
        text-align: center;
    }
    
    .device.Appliance {
        background-color: #5eb85e;
    }
    
    .device.Appliance a:hover {
        color: #a1ed82;
    }
    
    .device.Electronics {
        background-color: #0f90d1;
    }
    
    .device.Electronics a:hover {
        color: #4badd1;
    }
    
    .device.Lights {
        background-color: #c2a00c;
    }
    
    .device.Lights a:hover {
        color: #fad232;
    }
    
    .device.Other {
        background-color: #db524d;
    }
    
    .device.Other a:hover {
        color: #ff907d;
    }
    
    .device a {
        text-decoration: none;
    }
    
    .device a:visited, a:active, a:hover {
        color: #fff;
    }
    
    .device a:hover {
        text-decoration: underline;
    }
    
    
    window.onload = init;
    var socket = new WebSocket("ws://localhost:8080/webSocketHome/actions");
    socket.onmessage = onMessage;
    
    function onMessage(event) {
        var device = JSON.parse(event.data);
        if (device.action === "add") {
            printDeviceElement(device);
        }
        if (device.action === "remove") {
            document.getElementById(device.id).remove();
            //device.parentNode.removeChild(device);
        }
        if (device.action === "toggle") {
            var node = document.getElementById(device.id);
            var statusText = node.children[2];
            if (device.status === "On") {
                statusText.innerHTML = "Status: " + device.status + " (<a href="#" OnClick=toggleDevice(" + device.id + ")>Turn off</a>)";
            } else if (device.status === "Off") {
                statusText.innerHTML = "Status: " + device.status + " (<a href="#" OnClick=toggleDevice(" + device.id + ")>Turn on</a>)";
            }
        }
    }
    
    function addDevice(name, type, description) {
        var DeviceAction = {
            action: "add",
            name: name,
            type: type,
            description: description
        };
        socket.send(JSON.stringify(DeviceAction));
    }
    
    function removeDevice(element) {
        var id = element;
        var DeviceAction = {
            action: "remove",
            id: id
        };
        socket.send(JSON.stringify(DeviceAction));
    }
    
    function toggleDevice(element) {
        var id = element;
        var DeviceAction = {
            action: "toggle",
            id: id
        };
        socket.send(JSON.stringify(DeviceAction));
    }
    
    function printDeviceElement(device) {
        var content = document.getElementById("content");
    
        var deviceDiv = document.createElement("div");
        deviceDiv.setAttribute("id", device.id);
        deviceDiv.setAttribute("class", "device " + device.type);
        content.appendChild(deviceDiv);
    
        var deviceName = document.createElement("span");
        deviceName.setAttribute("class", "deviceName");
        deviceName.innerHTML = device.name;
        deviceDiv.appendChild(deviceName);
    
        var deviceType = document.createElement("span");
        deviceType.innerHTML = "<b>Type:</b> " + device.type;
        deviceDiv.appendChild(deviceType);
    
        var deviceStatus = document.createElement("span");
        if (device.status === "On") {
            deviceStatus.innerHTML = "<b>Status:</b> " + device.status + " (<a href="#" OnClick=toggleDevice(" + device.id + ")>Turn off</a>)";
        } else if (device.status === "Off") {
            deviceStatus.innerHTML = "<b>Status:</b> " + device.status + " (<a href="#" OnClick=toggleDevice(" + device.id + ")>Turn on</a>)";
            //deviceDiv.setAttribute("class", "device off");
        }
        deviceDiv.appendChild(deviceStatus);
    
        var deviceDescription = document.createElement("span");
        deviceDescription.innerHTML = "<b>Comments:</b> " + device.description;
        deviceDiv.appendChild(deviceDescription);
    
        var removeDevice = document.createElement("span");
        removeDevice.setAttribute("class", "removeDevice");
        removeDevice.innerHTML = "<a href="#" OnClick=removeDevice(" + device.id + ")>Remove device</a>";
        deviceDiv.appendChild(removeDevice);
    }
    
    function showForm() {
        document.getElementById("addDeviceForm").style.display = '';
    }
    
    function hideForm() {
        document.getElementById("addDeviceForm").style.display = "none";
    }
    
    function formSubmit() {
        var form = document.getElementById("addDeviceForm");
        var name = form.elements["device_name"].value;
        var type = form.elements["device_type"].value;
        var description = form.elements["device_description"].value;
        hideForm();
        document.getElementById("addDeviceForm").reset();
        addDevice(name, type, description);
    }
    
    function init() {
        hideForm();
    }
    
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.zhen</groupId>
        <artifactId>WebSocketHome</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>war</packaging>
    
    
        <dependencies>
    
            <!-- Servlet -->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.0.1</version>
            </dependency>
    
    
            <dependency>
                <groupId>javax</groupId>
                <artifactId>javaee-api</artifactId>
                <version>7.0</version>
                <scope>provided</scope>
            </dependency>
    
            <!-- JSON工具包 -->
            <dependency>
                <groupId>org.glassfish</groupId>
                <artifactId>javax.json</artifactId>
                <version>1.0.4</version>
            </dependency>
    
    
    
    
        </dependencies>
    
        <build>
            <plugins>
                <!-- 编译插件 -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.5.1</version>
                    <configuration>
                        <source>1.7</source>
                        <target>1.7</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
    
                <!-- tomcat 插件 -->
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <version>2.2</version>
                    <configuration>
                        <port>8080</port>
                        <path>/webSocketHome</path>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    第二篇敲的代码

    此案例利用websocket实现了一套增删改查

    项目中使用了CDI注解,如@ApplicationScope,@Inject进行注入功能

    项目结构:

     spring的websocket支持:

      spring4提供了对websocket的支持

    <!-- spring-webSocket,不使用javaee7的api里面了,使用spring的封装 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-websocket</artifactId>
                <version>${spring.version}</version>
            </dependency>

    服务端配置:

    package com.zhen.spring_websocket.config;
    
    import com.zhen.spring_websocket.service.MyHandler;
    import com.zhen.spring_websocket.service.MyHandler1;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.socket.WebSocketHandler;
    import org.springframework.web.socket.config.annotation.EnableWebSocket;
    import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
    import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
    
    /**
     * @author zhen
     * @Date 2018/12/10 18:34
     */
    @Configuration
    @EnableWebSocket
    public class WebSocketConfig implements WebSocketConfigurer {
    
        @Override
        public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
            registry.addHandler(myHandler(),"/myHandler");
            registry.addHandler(myHandler1(),"/myHandler1").withSockJS();
        }
    
        public WebSocketHandler myHandler() {
            return new MyHandler();
        }
    
        public WebSocketHandler myHandler1() {
            return new MyHandler1();
        }
    }
    package com.zhen.spring_websocket.service;
    
    import org.springframework.web.socket.CloseStatus;
    import org.springframework.web.socket.TextMessage;
    import org.springframework.web.socket.WebSocketSession;
    import org.springframework.web.socket.handler.AbstractWebSocketHandler;
    import org.springframework.web.socket.handler.TextWebSocketHandler;
    
    import java.io.IOException;
    
    /**
     * @author zhen
     * @Date 2018/12/10 18:32
     * 此类实现WebSocketHandler,执行处理请求等操作,这里只是接受请求然后再将请求转发回去的功能
     */
    public class MyHandler1 extends AbstractWebSocketHandler {
        @Override
        protected void handleTextMessage(WebSocketSession session, TextMessage message) {
            try{
                session.sendMessage(message);
            }catch (IOException e){
                e.printStackTrace();
            }
    
        }
    
        @Override
        public void afterConnectionEstablished(WebSocketSession session) throws Exception {
            super.afterConnectionEstablished(session);
        }
    
        @Override
        public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
            super.afterConnectionClosed(session, status);
        }
    }

    Handler并不一定是继承abstractWebSocketHandler,只要是WebSocketHandler的子类即可。

    和servlet的websocket api差不多吧

    这句代码:

    registry.addHandler(myHandler1(),"/myHandler1").withSockJS();

    是表示接受的是前端sockJs对象发送的请求。是spring-websocket模块的一个封装功能。

    sockJs是什么呢?

    在不支持WebSocket的情况下,也可以很简单地实现WebSocket的功能的,方法就是使用 SockJS

    它会优先选择WebSocket进行连接,但是当服务器或客户端不支持WebSocket时,会自动在 XHR流、XDR流、iFrame事件源、iFrame HTML文件、XHR轮询、XDR轮询、iFrame XHR轮询、JSONP轮询 这几个方案中择优进行连接。

    它是websocket客户端的拓展与补充。

    使用sockJs之后,spring注册websocket链接就上面代码这样既可。

    客户端:

      引入sockjs.min.js

      使用sockJs对象替代WebSocket对象发送请求,它的语法和原生几乎一致

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ page session="false" %>
    <html>
    <head>
        <title>WebSocket with SockJS</title>
    </head>
    <body>
    <h1>Welcome!</h1>
    <ul id="ul">
    </ul>
    <script src="${pageContext.request.contextPath}/static/js/sockjs.min.js"></script>
    <script>
        // SockJS与原生的WebSocket的方法基本是一致的,
        // 所以只需要将 new WebSocket(url); 换成 new SockJS(url); 就可以了
        var url = "/spring4webSocket/myHandler1";
        var sock = new SockJS(url);
        sock.onopen = function (ev) {
            console.log("opening");
            sayHey();
        };
        sock.onmessage = function (ev) {
            console.log(ev.data);
            var li = document.createElement("li");
            li.innerText = ev.data;
            document.getElementById("ul").appendChild(li);
            setTimeout(sayHey, 2000);
        };
        sock.onclose = function (ev) {
            console.log("closed");
        };
        function sayHey() {
            console.log("sending 'Hey guy!'");
            sock.send("Hey guy!");
        };
    </script>
    </body>
    </html>

    spring的封装还有基于stomp的部分:

      这个没理解好,以后再做研究。与spring配置请求的也涉及到再做补充。虽然写过demo,但是不太理解,用的springbot,使用了spring security的例子,暂过。

    参考链接:https://www.jianshu.com/p/942a2b16e26c

    参考链接:https://baike.baidu.com/item/WebSocket/1953845?fr=aladdin

    参考链接:https://www.cnblogs.com/jingmoxukong/p/7755643.html

    参考链接:http://www.ruanyifeng.com/blog/2017/05/websocket.html

    参考链接:http://www.cnblogs.com/xdp-gacl/p/5193279.html

    参考链接:https://blog.csdn.net/john_62/article/details/78208177

    参考链接:https://blog.csdn.net/dadiyang/article/details/83715569

    此学习得出的经验之谈:如果暂时理解不了,就先使用他,然后再理解  

  • 相关阅读:
    CentOS初步学习记录(五)用户和用户组
    CentOS初步学习记录(四)curl文件传输和grep文本搜索
    微信公众号推文svg点击交互的方法
    layaair写动画的方法 用时间轴最方便
    spring boot 整合Elasticsearch
    java spring boot 拦截器 实现未登录用户不能登录
    java win 安装 activemq教程
    java spring boot Swagger2 构建api文档
    java spring boot @bean的用法
    java spring boot 写入日志
  • 原文地址:https://www.cnblogs.com/aigeileshei/p/10101802.html
Copyright © 2020-2023  润新知