Java 原生 WebSocket 仅H5支持

2018-03-22  本文已影响0人  酷酷的小k

简述:支持发送公告、发送指定用户及异地登录。

所需jar包:
javaee-api.jar
gson.jar

定义收发消息实体类:

package com.test.springWebsocket;

public class WebMessage {

    /**
     * 用户id
     */
    private Long userId;

    /**
     * 用户名
     */
    private String username;

    /**
     * 客户端标记
     */
    private String clientMark;

    /**
     * 内容
     */
    private String contents;

    /**
     * 消息类型,1.公告,2.点对点发消息,3.检查异地登录
     */
    private String type;

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getClientMark() {
        return clientMark;
    }

    public void setClientMark(String clientMark) {
        this.clientMark = clientMark;
    }

    public String getContents() {
        return contents;
    }

    public void setContents(String contents) {
        this.contents = contents;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}

定义WebSocketServlet类:

package com.test.nativeWebsocket;

import com.google.gson.Gson;
import com.test.springWebsocket.WebMessage;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

/**
 * 所需jar包:javaee-api.jar
 * 客户端连接的url
 */
@ServerEndpoint("/native-socket-endpoint/{userId}/{username}/{clientMark}")
public class CustomWebSocketServlet {

    private Long userId;

    private String username;

    private String clientMark;

    private Session session;

    public Long getUserId() {
        return userId;
    }

    public String getUsername() {
        return username;
    }

    public String getClientMark() {
        return clientMark;
    }

    public Session getSession() {
        return session;
    }

    /**
     * 连接建立成功调用的方法
     *
     * @param session    可选的参数.session为与某个客户端的连接会话,需要通过它来给客户端发送数据
     * @param userId     userId
     * @param username   username
     * @param clientMark clientMark
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") Long userId, @PathParam("username") String username, @PathParam("clientMark") String clientMark) {
        WebMessage webMsg = new WebMessage();
        webMsg.setUserId(userId);
        webMsg.setUsername(username);
        webMsg.setClientMark(clientMark);
        webMsg.setType("3");
        CustomWebSocketHandler.sendToUserMsg(webMsg);
        this.session = session;
        this.userId = userId;
        this.username = username;
        this.clientMark = clientMark;
        CustomWebSocketHandler.addWebSocketServlet(this);
        System.out.println(String.format("用户<%s>打开WebSocket连接...", username));
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        CustomWebSocketHandler.removeWebSocketServlet(this);
        System.out.println(String.format("用户<%s>关闭WebSocket连接...", username));
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param msg     客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage
    public void onMessage(String msg, Session session) {
        WebMessage webMsg = new Gson().fromJson(msg, WebMessage.class);
        if (webMsg.getUserId() == null) {
            CustomWebSocketHandler.sendToAllMsg(webMsg);
        } else {
            CustomWebSocketHandler.sendToUserMsg(webMsg);
        }
    }

    /**
     * 发生错误时调用
     *
     * @param session Session
     * @param error   Throwable
     */
    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }
}

定义WebSocketHandler类:

package com.test.nativeWebsocket;

import com.google.gson.Gson;
import com.test.springWebsocket.WebMessage;

import java.util.ArrayList;
import java.util.List;

public class CustomWebSocketHandler {

    private static List<CustomWebSocketServlet> userSocketList = new ArrayList<>();

    public static synchronized void addWebSocketServlet(CustomWebSocketServlet webSocketServlet) {
        userSocketList.add(webSocketServlet);
    }

    public static synchronized void removeWebSocketServlet(CustomWebSocketServlet webSocketServlet) {
        userSocketList.remove(webSocketServlet);
    }

    /**
     * 将消息发送给所有用户
     *
     * @param webMsg WebMessage
     */
    public static void sendToAllMsg(WebMessage webMsg) {
        String jsonStr = new Gson().toJson(webMsg);
        for (CustomWebSocketServlet webSocketServlet : userSocketList) {
            try {
                //webSocketServlet.getSession().getBasicRemote().sendText(jsonStr);
                webSocketServlet.getSession().getAsyncRemote().sendText(jsonStr);
            } catch (Exception e) {
                System.out.println(String.format("用户<%s>发送消息失败|消息体<%s>", webSocketServlet.getUsername(), jsonStr));
                e.printStackTrace();
            }
        }
    }

    /**
     * 将消息发送给指定用户
     *
     * @param webMsg WebMessage
     */
    public static void sendToUserMsg(WebMessage webMsg) {
        String jsonStr = new Gson().toJson(webMsg);
        for (CustomWebSocketServlet webSocketServlet : userSocketList) {
            if (webSocketServlet.getUserId().equals(webMsg.getUserId())) {
                try {
                    //webSocketServlet.getSession().getBasicRemote().sendText(jsonStr);
                    webSocketServlet.getSession().getAsyncRemote().sendText(jsonStr);
                } catch (Exception e) {
                    System.out.println(String.format("用户<%s>发送消息失败|消息体<%s>", webSocketServlet.getUsername(), jsonStr));
                    e.printStackTrace();
                }
            }
        }
    }
}

定义jsp页面RequestMapping:

/**
     * 原生 WebSocket 页面
     *
     * @param request HttpServletRequest
     * @return String
     */
    @RequestMapping("/native-websocket.xhtm")
    public String nativeWebsocket(HttpServletRequest request) {
        String clientMark = (String) request.getSession().getAttribute("clientMark");
        if (clientMark == null) {
            clientMark = GenerateUtil.getUUID();
            request.getSession().setAttribute("clientMark", clientMark);
        }
        Admin admin = (Admin) request.getSession().getAttribute("admin");
        request.setAttribute("userId", admin.getId());
        request.setAttribute("username", admin.getAdmin());
        request.setAttribute("clientMark", clientMark);
        return "nativeWebsocket/nativeWebsocket";
    }

定义jsp页面:

<!DOCTYPE html>

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>

<%
    String rootPath = request.getContextPath();
    String wsBasePath = "ws://" + request.getServerName() + ":" + request.getServerPort() + rootPath + "/";
%>

<html>
    <head>
        <title>Native WebSocket</title>
        <meta http-equiv="Expires" content="0"/>
        <meta http-equiv="Cache" content="no-cache"/>
        <meta http-equiv="Pragma" content="no-cache"/>
        <meta http-equiv="Cache-Control" content="no-cache"/>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <script type="text/javascript" src="../../../static/js/plugins/jquery/jquery-1.9.1.min.js"></script>
        <script type="text/javascript">
            var userId = "${userId}";
            var username = "${username}";
            var clientMark = "${clientMark}";
            var wsBasePath = "<%=wsBasePath%>";
        </script>
        <style>
            .c_box {
                width: 800px;
                height: 300px;
                border: 1px solid;
                overflow: auto;
            }
        </style>
    </head>
    <body>
        <div>用户id:${userId}</div>
        <div>用户名:${username}</div>
        <div>
            <span>公告:</span>
            <input type="text" id="notice"/>
            <input type="button" id="sendNotice" value="发送"/>
        </div>
        <div>
            <span>用户id:</span>
            <input type="text" id="toUserId"/>
            <span>消息:</span>
            <input type="text" id="toUserMsg"/>
            <input type="button" id="sendToUser" value="发送"/>
        </div>
        <div>公告列表:</div>
        <div id="noticeList" class="c_box"></div>
        <div>消息列表:</div>
        <div id="msgList" class="c_box"></div>
        <script type="text/javascript" src="../../../static/js/nativeWebsocket/nativeWebsocket.js"></script>
    </body>
</html>

定义JavaScript脚本:

var ws = null;

//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
    ws = new WebSocket(wsBasePath + "native-socket-endpoint/" + userId + "/" + username + "/" + clientMark);
    //连接发生错误的回调方法
    ws.onerror = function () {
        console.log("WebSocket连接错误");
    };
    //连接成功建立的回调方法
    ws.onopen = function () {
        console.log("WebSocket连接成功");
    };
    //接收到消息的回调方法
    ws.onmessage = function (event) {
        showMsg(JSON.parse(event.data));
    };
    //连接关闭的回调方法
    ws.onclose = function () {
        console.log("WebSocket连接关闭");
    };
} else {
    alert('当前浏览器 Not support WebSocket');
}

function closeSocket() {
    if (ws == null || ws.readyState == 2 || ws.readyState == 3) {
        return true;
    }
    ws.close();
}

function showMsg(webMsg) {
    switch (webMsg['type']) {
        //公告
        case '1': {
            var noticeHtm = '<div>' + webMsg['contents'] + '</div>';
            $('#noticeList').append(noticeHtm);
            $("#noticeList").scrollTop($("#noticeList")[0].scrollHeight);
            break;
        }
        //点对点发消息
        case '2': {
            var msgHtm = '<div>' + webMsg['contents'] + '</div>';
            $('#msgList').append(msgHtm);
            $("#msgList").scrollTop($("#msgList")[0].scrollHeight);
            break;
        }
        //检查异地登录
        case '3': {
            if (webMsg['clientMark'] != clientMark) {
                closeSocket();
                alert('您的账号在另一处登录');
            }
            break;
        }
        default: {
            alert("WebSocket接收到未知消息...");
            break;
        }
    }
}

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

$("#sendNotice").on("click", function () {
    if (ws == null) {
        alert('WebSocket连接未打开');
        return true;
    }
    if (ws.readyState == 0) {
        alert('WebSocket正在连接中,请稍后再发送消息');
        return true;
    }
    if (ws.readyState == 2) {
        alert('WebSocket连接正在关闭中,无法发送消息');
        return true;
    }
    if (ws.readyState == 3) {
        alert('WebSocket连接已关闭,无法发送消息');
        return true;
    }
    var notice = $("#notice").val();
    if (notice.length > 0) {
        var webMsg = {
            'contents': notice,
            'type': '1'
        };
        ws.send(JSON.stringify(webMsg));
    }
});

$("#sendToUser").on("click", function () {
    if (ws == null) {
        alert('WebSocket连接未打开');
        return true;
    }
    if (ws.readyState == 0) {
        alert('WebSocket正在连接中,请稍后再发送消息');
        return true;
    }
    if (ws.readyState == 2) {
        alert('WebSocket连接正在关闭中,无法发送消息');
        return true;
    }
    if (ws.readyState == 3) {
        alert('WebSocket连接已关闭,无法发送消息');
        return true;
    }
    var toUserId = $("#toUserId").val();
    var toUserMsg = $("#toUserMsg").val();
    if (toUserId.length > 0 && toUserMsg.length > 0) {
        var webMsg = {
            'userId': toUserId,
            'username': username,
            'contents': toUserMsg,
            'type': '2'
        };
        ws.send(JSON.stringify(webMsg));
    }
});
上一篇下一篇

猜你喜欢

热点阅读