webSocket原理及其案例

常见的消息推送方式

1:轮询方式

浏览器以指定的时间间隔向服务器发出HTTP请求,服务器实现试试返回数据给浏览器

缺点:数据有延时、服务器压力较大。

2:长轮询

浏览器发出ajax(异步)请求,服务器接收端接收到请求后,会阻塞请求直到有数据或者超时才返回。

缺点:

3:SSE服务器发送事件

SSE在服务器和客户端之间打开一个单向通道

服务端响应的不再是一次性的数据包,而是Text/event-stream类型的数据流信息

服务器有数据变更时将数据流式传输到客户端

4:webSocket

基于TCP连接上及逆行全双工通信的协议

全双工:允许数据在两个方向上同时传输。

半双工:允许数据在两个方向上传输,但是同一个时间段内只允许一个方向上的传输。

websocket API

客户端【浏览器】API

websocket对象提供的方法

send() 通过websocket对象调用该方法发送数据给服务端

案例

服务端API

Tomcat从7.0.5才支持websocket

Java WebSocket应用由一系列的Endpoint组成,Endpoint是一个Java对象,代表WebSocket链接的一端,对于服务端,我们可以视为处理具体websocket消息的接口

定义Endpoint两种方式:

编程式,继承类javax.websocket.Endpoint并实现其方法。

注解式,定义一个POJO,并添加@ServiceEndpoint相关注解。

Endpoint实例在WebSocket握手时创建,并在客户端于服务端链接过程中有效,最后在链接关闭时结束。在Endpoint接口中明确定义了与其生命周期相关的方法,规范实现者确保生命周期的各个阶段调用示例的相关方法,生命周期方法如下:

方法描述注解
onOpen()当开启一个新的会话时调用,该方法是客户端与服务端握手成功后调用的方法@OnOpen
onClose()当会话关闭时调用@OnClose
onError()当连接过程异常时调用@OnError

两个问题

服务端如何接收客户端发送的数据呢?

1:编程式

通过添加MessageHandler消息处理器来接收消息

2:注解式

在定义Endpoint时,通过@OnMessage注解指定接收消息的方法

服务端如何推送数据给客户端呢?

发送消息则由RemoteEndpoint完成,其实例由Session维护。

发送消息有两种方式

1:通过session.getBasicRemote获取同步消息发送的实例,然后调用其sendXxx()方法发送消息

2:通过session.getAsyncRemote获取异步消息发送实例,然后调用其sendXxx()方法发送消息

例子

代码实现

引入依赖资源

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

编写配置类,扫描添加有@ServerEndpoint注解的Bean

@Configuration
public class FileManageWebSocketConfigMessage {
    /**
     * 注入ServerEndpointExporter,自动注册使用@ServerEndpoint注解的
     * @return
     */
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

编写配置类,获取HttpSession对象

public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator {
    public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
        //强转
        HttpSession httpSession =(HttpSession) request.getHttpSession();
        //将httpSession对象存储到配置对象中
        config.getUserProperties().put(HttpSession.class.getName(),httpSession);
    }

使用

在@ServerEndpoint注解种引入配置器

@ServerEndpoint(value="/chat",configurator = GetHttpSessionConfigurator.class)

案例代码

webSocket.util

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

@Component
@ServerEndpoint("/webSocket/{uId}")
@Slf4j
public class WebSocketServerUtil {

    private Session session;
    private static CopyOnWriteArraySet<WebSocketServerUtil > webSocketSet = new CopyOnWriteArraySet<>();
    private static ConcurrentHashMap<Long,WebSocketServerUtil > webSocketMap  = new ConcurrentHashMap<>();
    private Long uId = null;


    @OnOpen
    public void onOpen(Session session, @PathParam("uId") Long uId){
        this.session = session;
        this.uId = uId;
        if(webSocketMap .containsKey(uId)){
            webSocketMap .remove(uId);
            webSocketMap .put(uId,this);
        }else{
            webSocketMap .put(uId,this);
            webSocketSet.add(this);
        }

        log.info("【websocket消息】有新的连接,总数:{}",webSocketMap.size());
    }

    @OnClose
    public void onClose(){
        if(webSocketMap.containsKey(uId)){
            webSocketMap.remove(uId);
            //从set中删除
            webSocketSet.remove(this);
        }
        log.info("【websocket消息】连接断开,总数:{}",webSocketSet.size());
    }

    @OnMessage
    public void onMessage(String message){
        log.info("【websocket消息】收到客户端发来的消息:{}",message);
    }

    public void sendMessage(String message){
        try {
            this.session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    /**
     * 发送自定义消息
     * */
    public static void sendInfo(String message,Long uId) throws Exception {

        //log.info("发送消息到:"+uId+",报文:"+message);

            if(webSocketMap.containsKey(uId)){
                webSocketMap.get(uId).sendMessage(message);
            }else{
                log.error("用户"+uId+",不在线!");
                throw new Exception("连接已关闭,请刷新页面后重试");
            }

    }
}

调用Util方法

        Long uId = new Long("1");
		Map msgMap = new HashMap();
		msgMap.put("step",1);
		msgMap.put("type",2);
		msgMap.put("msg","hello");
		WebSocketServerUtil.sendInfo(JsonUtil.toJson(msgMap),uId);

前端JS代码

/**
 * 初始化websocket连接
 */
function initWebSocket() {
	let uId = 1;
	var websocket = null;
	if('WebSocket' in window) {
		websocket = new WebSocket("ws://localhost:8009/webSocket"+uId );
	} else {
		alert("该浏览器不支持websocket!");
	}
	websocket.onopen = function(event) {
		console.log("建立连接");
		websocket.send('Hello WebSockets!');
	}
	websocket.onclose = function(event) {
		console.log('连接关闭')
		reconnect(); //尝试重连websocket
	}
	//建立通信后,监听到后端的数据传递
	websocket.onmessage = function(event) {
		let data = JSON.parse(event.data);
		//业务处理....
		if(data.step == 1){
		   alert(data.msg);
		}
	}
	websocket.onerror = function() {
		// notify.warn("websocket通信发生错误!");
		// initWebSocket()
	}
	window.onbeforeunload = function() {
		websocket.close();
	}
// 重连
function reconnect() {
	console.log("正在重连");
	// 进行重连
	setTimeout(function () {
		initWebSocket();
	}, 1000);
}

  • 18
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。它通过在客户端和服务器之间建立长连接,实现了双向通信。下面是 WebSocket 的工作原理: 1. 握手阶段:客户端发起 WebSocket 连接请求,发送一个特定的 HTTP 请求,包含协议升级请求头(Upgrade: websocket),服务器收到请求后进行协议升级确认。 2. 建立连接:服务器通过响应 HTTP 101 状态码表示升级成功,建立了一个持久连接。此时,WebSocket 连接由 HTTP 升级为 WebSocket 协议。 3. 数据传输:建立连接后,客户端和服务器可以在任意时间内相互发送消息。双方可以同时发送和接收消息,实现实时的双向通信。 4. 连接关闭:当某一方决定关闭连接时,发送一个关闭帧表示断开连接。另一方收到关闭帧后也关闭连接。 WebSocket 使用了一个称为 "数据帧" 的结构来传输数据。数据帧由一个标识位(用于标识帧的类型)、掩码标志位(用于数据加密)、数据长度字段和实际数据组成。 相比传统的 HTTP 请求-响应模式,WebSocket 具有以下优势: - 实时性:能够实现实时的双向通信,使得服务器能够主动向客户端推送数据。 - 减少通信量:WebSocket 连接建立后,可以发送少量的控制信息来维持连接,避免了重复建立和断开连接的开销。 - 更少的延迟:WebSocket 使用单个 TCP 连接,避免了 HTTP 的连接建立和关闭的开销,减少了通信的延迟。 希望以上解答对你有所帮助!如有更多问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值