Java笔记(WebSocket)

参考文章:

WebSocket 是 HTML5 开始提供的一种浏览器与服务器间进行 全双工通讯 的网络技术,使得服务器可以主动向浏览器发送信息。依靠这种技术可以 实现客户端和服务器端的长连接,双向实时通信
WebSocket 的特点:

  • 事件驱动
  • 异步
  • 使用 ws 或者 wss 协议的客户端
  • 能够实现真正意义上的推送功能

1 浏览器端

1.1 WebSocket 对象

在 Javascrpt 中可以使用 new WebSocket(url) 来创建一个 WebSocket 对象, 它能使浏览器与服务器建立连接。
其中参数 urlwswss 协议的,如:

const socket = new WebSocket("ws://localhost:8080/websocket/commodity/%7B2%7D/%7B3%7D");

1.1.1 WebSocket 对象的方法

WebSocket 对象有两个方法,分别是:send() 以及 close()

  • send(data) 方法向服务端发送数据,在后端监听接受事件中可以获取到该数据源,可以像普通的 AJAX 一样发送一个 JSON 类型的数据,作为即时通讯功能的话,这个 JSON 对象一般包括三个信息,分别是:发送者ID、接受者ID以及发送内容。
  • close() 方法用于关闭 WebSocket 连接。

1.1.2 WebSocket 对象的监听函数

初始化 WebSocket 对象时,还需要定义了几个监听函数,当这些监听事件发生的时候就会触发这些监听函数,分别是:

  • onopen():当连接初始建立时触发
  • onmessage():当 WebSocket 接收到服务器发来的消息的时触发的事件
  • onclose():当连接关闭时触发
  • onerror():当连接发生错误时触发

1.1.3 WebSocket 对象的 readyState 属性

WebSocket 对象的 readyState 属性表示当前连接的状态:

  • 0 (CONNECTING):表示正在与服务器创建连接
  • 1 (OPEN):表示与服务器已经创建了连接
  • 2 (CLOSING):表示正在关闭与服务器的连接
  • 3(CLOSED):表示已经关闭与服务器的连接

2 服务器端

java 端使用 WebSocket 可以使用以下三种形式:

  • 1、使用 Spring 的底层级 WebSocketAPI 实现(实现 TextWebSocketHandler 接口)
  • 2、使用 Spring 高级API 实现(使用 SimpMessagingTemplate 方法)
  • 3、使用 JSR356 定义的 WebSocket 规范实现

个人认为使用 JSP356 的 WebSocket 规范最为简单。

2.1 JSR356定义的 WebSocket 规范

首先引入依赖:

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-websocket</artifactId>
	<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
	<groupId>javax</groupId>
	<artifactId>javaee-api</artifactId>
	<version>7.0</version>
	<scope>provided</scope>
</dependency>

JSR356 的 WebSocket 规范使用 javax.websocket.* 的 API,可以将一个普通 Java 对象(POJO)使用 @ServerEndpoint 注释从而作为 WebSocket 服务器的端点,此时客户端浏览器已经可以对 WebSocket 客户端 API 发起 HTTP 长连接了,如:

@ServerEndpoint(value="/websocket/commodity/{userId}", configurator = SpringConfigurator.class)

注释中的参数 value 表示的是 url 路径与 @RequestMapping 注释中的 value 类似,而他表示的是前端创建 WebSocket 对象需要传入的 ws 协议的路径。当中的 {userId} 参数作为当前客户的识别 ID 号,此时客户端传入的 url 应该为:

ws://[Server端IP或域名]:[Server端口]/项目/websocket/commodity/{userId}

注意:如果在要进行对象注入,就必须加上 configurator = SpringConfigurator.class

2.1.1 代码示例

在添加完 @ServerEndpoint 注释之后,接着就要声明 @OnOpen@OnMessage@OnClose@OnError 注释的方法了。其实对应的就是前端一些事件函数。

通常的,在 @OnOpen 中把当前连接用户使用 Map 的形式将用户编号与其当前连接的 WebSocketSession 对应缓存下来,以便在该用户接受信息的时候使用。
@OnMessage 中接受信息并根据信息中的用户识别号进行信息的发送,@OnClose 中清除所有该断开连接用户的缓存信息。
eg:

package cn.seiei.webSocket;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

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

import com.alibaba.fastjson.JSON;
import org.springframework.web.socket.server.standard.SpringConfigurator;

@ServerEndpoint(value="/websocket/commodity/{fromUserId}/{toUserId}", configurator = SpringConfigurator.class)
public class WebSocketServerByJSR356 {

    // 已经建立链接的对象缓存起来(线性安全)
    private static ConcurrentMap<Integer, WebSocketServerByJSR356> serverMap = new ConcurrentHashMap<Integer, WebSocketServerByJSR356>();

    // 记录当前 WebSocket 的 session 对象
    // 当中 isOpen 方法可以判断该用户是否在线
    // 调用 getBasicRemote().sendText(content) 可以发送消息到客户端
    private Session currentSession;

    /**
     * 用户开始连接 webSocket 事件
     * @PathParam 解释:https://blog.csdn.net/u011410529/article/details/66974974
     * @param session webSocket session 对象
     * @param fromUserId url 传入的用户 ID
     * @param toUserId url 传入的目标用户 ID
     * @throws IOException
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("fromUserId") int fromUserId, @PathParam("toUserId") int toUserId) throws IOException {
        this.currentSession = session;
        serverMap.put(fromUserId, this);//建立链接时,缓存对象,这个 this 就是 WebSocketServer 对象
        System.out.println("UserId:" + fromUserId + " 连接服务器成功。。。");
        System.out.println("session.getRequestURI:" + session.getRequestURI());
        System.out.println("session.getQueryString:" + session.getQueryString());
        System.out.println("session.getRequestParameterMap:" + session.getRequestParameterMap());
    }

    /**
     * 用户关闭 webSocket 连接事件,清除缓存
     * @param session webSocket session 对象
     * @param reason 连接关闭原因
     */
    @OnClose
    public void onClose(Session session, CloseReason reason) {
        System.out.println("用户关闭:" + reason.toString());
        // 如果缓存中有当前用户的缓存(这里的 this 就是 WebSocketServer 对象)
        if (serverMap.containsValue(this)) {
            Iterator<Integer> keys = serverMap.keySet().iterator();
            int userId = 0;
            while(keys.hasNext()) {
                userId = keys.next();
                if (serverMap.get(userId) == this) {
                    serverMap.remove(userId, this);//关闭链接时,删除缓存对象
                }
            }
        }
        this.currentSession = null;
        try {
            session.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 连接过后,发送信息
     * @param json 信息 JSON 对象,当中包含发送者ID,接受者ID 以及发送信息
     */
    @OnMessage
    @SuppressWarnings("unchecked")
    public void onMessage(String json) {
        HashMap<String, String> map =  JSON.parseObject(json, HashMap.class);
        int fromUserId = Integer.parseInt(map.get("fromUserId"));
        int toUserId = Integer.parseInt(map.get("toUserId"));
        String content = map.get("content").toString();
        WebSocketServerByJSR356 server = serverMap.get(toUserId);
        //若存在则用户在线,否在用户不在线
        if (server != null && server.currentSession.isOpen()) {
            if (fromUserId != toUserId) {
                try {
                    // 发送信息
                    server.currentSession.getBasicRemote().sendText(content);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 连接发生错误事件
     * @param t 错误对象
     */
    @OnError
    public void onError(Throwable t) {
        System.out.println("发生错误事件!!");
        t.printStackTrace();
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值