WebSocket简单的应用

WebSocket介绍

  WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

  WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

  在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

  现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

  HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。

  当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。

  以下 API 用于创建 WebSocket 对象。

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

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

WebSocket 客户端页面实现

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>WebSocket测试</title>
</head>
<body style="text-align: center;">
    <h2>WebSocket测试</h2>
    <div>
        <input type="text" id="txt"/>
        <button onclick="sendWebSocket()">发送</button><br>
        <button onclick="checkWebSocket()">测试WebSocket</button>
        <button onclick="connectWebSocket()">连接WebSocket</button>
        <button onclick="closeWebSocket()">关闭WebSocket</button><br>
        <hr>
        <div id="message"></div>
    </div>
</body>
<script type="text/javascript">

    var websocket = null;

    function checkWebSocket(){
        if ("WebSocket" in window) {
            // alert("您的浏览器支持 WebSocket!");
            setMessageInnerHTML("您的浏览器支持 WebSocket!");
        }
        else {
            // 浏览器不支持 WebSocket
            // alert("您的浏览器不支持 WebSocket!");
            setMessageInnerHTML("您的浏览器不支持 WebSocket!");
        }
    }

    // 连接  WebSocket
    function connectWebSocket(){
        // 打开一个 web socket
        websocket = new WebSocket("ws://localhost:8080/websocket");
        websocket.onopen = function() {
            // Web Socket 已连接上,使用 send() 方法发送数据
            setMessageInnerHTML("WebSocket 已连接...");
        };
        websocket.onmessage = function(evt) {
            var received_msg = evt.data;
            setMessageInnerHTML("收到消息:" + received_msg);
        };
        websocket.onclose = function()
         {
            setMessageInnerHTML("WebSocket 已关闭...");
         };
    }

    // 向WebSocket服务端发送消息
    function sendWebSocket(){
        if (websocket){
            if (websocket.readyState == websocket.OPEN) {
                var message = document.getElementById('txt').value;
                websocket.send(message);
            } else {
                setMessageInnerHTML("WebSocket 未连接...");
            }
        }else {
            setMessageInnerHTML("WebSocket 未创建...");
        }
    }

    // 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function() {
        closeWebSocket();
    }

    // 关闭WebSocket连接
    function closeWebSocket() {
        websocket.close();
    }

    // 将消息显示在网页上
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }
</script>
</html>

WebSocket 服务端Java实现

1、新建一个Maven Web工程,导入依赖

<dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-api</artifactId>
    <version>1.1</version>
    <scope>provided</scope>
</dependency>

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
      </dependency>

2、编辑一个WebSocket服务端类,MyWebSocket.class

package com.test.websocket;

import java.io.IOException;

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

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

    private Session session;

    /**
     * 连接建立后触发的方法
     */
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        WebSocketMapUtil.put(session.getId(), this);
        System.out.println("-------- onOpen: 当前在线人数 " + WebSocketMapUtil.getOnlineCount() + ",连接人 " + session.getId() + " --------");
    }

    /**
     * 连接关闭后触发的方法
     */
    @OnClose
    public void onClose() {
        // 从map中删除
        WebSocketMapUtil.remove(session.getId());
        System.out.println("-------- onClose: 当前在线人数 " + WebSocketMapUtil.getOnlineCount() + ",关闭人 " + session.getId() + " --------");
    }

    /**
     * 接收到客户端消息时触发的方法
     */
    @OnMessage
    public void onMessage(String message, Session session) throws Exception {
        // 获取服务端到客户端的通道
        MyWebSocket myWebSocket = WebSocketMapUtil.get(session.getId());
        System.out.println("收到来自 " + session.getId() + " 的消息:" + message);

        // 返回消息给Web Socket客户端(浏览器)
        myWebSocket.sendMessageAll("服务端已收到消息:" + message);
    }

    /**
     * 发生错误时触发的方法
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("-------- onError: 当前在线人数 " + WebSocketMapUtil.getOnlineCount() + ",连接发生错误 " + session.getId() + "-"+ error.getMessage() + " --------");
        // error.printStackTrace();
    }

    /**
     * 给单个客户端发送消息
     * @param message
     * @param sessionId
     * @throws IOException
     */
    public void sendMessageSingle(String message, String sessionId) throws IOException {

        // session.getBasicRemote().sendText(message); 同步消息
        // session.getAsyncRemote().sendText(message); 异步消息

        MyWebSocket myWebSocket = WebSocketMapUtil.get(sessionId);
        if(myWebSocket != null) {
            myWebSocket.session.getBasicRemote().sendText(message);
        }
    }

    /**
     * 给所有客户端发送消息
     * @param message
     * @throws IOException
     */
    public void sendMessageAll(String message) throws IOException {
        for (MyWebSocket item : WebSocketMapUtil.getValues() ) {
            item.session.getAsyncRemote().sendText(message);
            System.out.println(item.session.getId());
            System.out.println(item.session.isSecure());
            System.out.println(item.session.isOpen());
        }
    }

}
View Code

 3、编辑一个WebSocket 工具类,WebSocketMapUtil.class

package com.test.websocket;

import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class WebSocketMapUtil {

    public static ConcurrentMap<String, MyWebSocket> webSocketMap = new ConcurrentHashMap<>();

    public static void put(String key, MyWebSocket myWebSocket) {
        webSocketMap.put(key, myWebSocket);
    }

    public static MyWebSocket get(String key) {
        return webSocketMap.get(key);
    }

    public static void remove(String key) {
        webSocketMap.remove(key);
    }

    public static Collection<MyWebSocket> getValues() {
        return webSocketMap.values();
    }

    public static int getOnlineCount() {
        return webSocketMap.size();
    }
}
View Code

4、注入一个ServerEndpointExporterBean,该Bean会自动注册使用@ServerEndpoint注解申请的websocket endpoint,代码如下:

@Component
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

启动项目效果如下:

注意:若是项目中集成了shiro会被拦截,页面控制台报302,找到项目中的shiro文件增加一行放行路径就可以了。

原文地址:https://www.cnblogs.com/huigee/p/14628074.html