WebSocket

【*】概念

WebSocket 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信
  • 全双工和单工的区别
1.全双工:通信允许数据在两个方向上同时传输,它在能力上相当
于两个单工通信方式的结合。全双工指可以同时(瞬时)进行信号的双向传输(A→B且B→A)。指
A→B的同时B→A,是瞬时同步的。
2.单工、半双工(Half Duplex),所谓半双工就是指一个时间段内只有一个动作发生,举个简单例子,
一条窄窄的马路,同时只能有一辆车通过,当目前有两辆车对开,这种情况下就只能一辆先过,等到头
儿后另一辆再开,这个例子就形象的说明了半双工的原理。早期的对讲机、以及早期集线器等设备都是
基于半双工的产品。随着技术的不断进步,半双工会逐渐退出历史舞台。
  • httpwebsocket的区别
1.http协议是短连接,因为请求之后,都会关闭连接,下次重新请求数据,需要再次打开链接。
2.WebSocket协议是一种长链接,只需要通过一次请求来初始化链接,然后所有的请求和响应都是通过这个TCP链接
进行通讯。

【*】WebSocket依赖

        <!--添加websocket依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.java-websocket</groupId>
            <artifactId>Java-WebSocket</artifactId>
            <version>1.3.8</version>
        </dependency>

【*】websocket的相关注解说明

  • @ServerEndpoint("/websocket/{uid}")
    • 申明这是一个websocket服务
    • 需要指定访问该服务的地址,在地址中可以指定参数,需要通过{}进行占位
  • @OnOpen
    • 用法:public void onOpen(Session session, @PathParam("uid") String uid) throws IOException{}
    • 该方法将在建立连接后执行,会传入session对象,就是客户端与服务端建立的长连接通道
    • 通过@PathParam获取url申明中的参数
  • @OnClose
    • 用法:public void onClose() {}
    • 该方法是在连接关闭后执行
  • @OnMessage
    • 用法:public void onMessage(String message, Session session) throws IOException {}
    • 该方法用于接收客户端发来的消息
    • message:发来的消息数据
    • session:会话对象(也是通道)
  • 发送消息到客户端
    • 用法:session.getBasicRemote().sendText("你好");
    • 通过session进行发送。

【*】WebSocket在线测试地址

        http://www.websocket-test.com/

【*】SpringBoot整合WebSocket实现同一账号只能一个设备登录功能

  • 使用WebSocket核心类
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

@Component//项目启动,会自动扫描当前的类,将当前的类注入Spring容器
public class OneLoginHandshakeInterceptor implements HandshakeInterceptor {

    /**
     * 握手之前,若返回false,则不建立链接
     *
     * @param request
     * @param response
     * @param wsHandler
     * @param attributes
     * @return
     * @throws Exception
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse
            response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws
            Exception {
        //将用户id放入socket处理器的会话(WebSocketSession)中
        HttpServletRequest httpRequest = ((ServletServerHttpRequest)request).getServletRequest();
        String requestURL = ((ServletServerHttpRequest) request).getServletRequest().getRequestURL().toString();
        String userId = requestURL.substring(requestURL.lastIndexOf('/')+1, requestURL.length());
        attributes.put("userId", userId);
        return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse
            response, WebSocketHandler wsHandler, Exception exception) {
    }
}
  • WebSocket配置类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration//SpringBoot项目启动时就会扫描此类,并将此类注册到Spring容器
@EnableWebSocket//开始WebSocket功能
public class OneLoginWebSocketConfig implements WebSocketConfigurer {

    @Autowired
    private OneLoginWsHandler oneLoginWsHandler;//

    @Autowired
    private OneLoginHandshakeInterceptor myHandshakeInterceptor;//注入拦截器

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(this.oneLoginWsHandler, "/ws/{userId}")//访问ws的请求路径,例如ws/10.1.1.226/ws/123
                .setAllowedOrigins("*")//允许跨域,不配置不能访问
                .addInterceptors(this.myHandshakeInterceptor);//将ws的拦截器,添加到ws
    }
}
  • WebSocket拦截器
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

@Component
public class OneLoginHandshakeInterceptor implements HandshakeInterceptor {

    /**
     * 握手之前,若返回false,则不建立链接
     *
     * @param request
     * @param response
     * @param wsHandler
     * @param attributes
     * @return
     * @throws Exception
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse
            response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws
            Exception {
        //将用户id放入socket处理器的会话(WebSocketSession)中
        HttpServletRequest httpRequest = ((ServletServerHttpRequest)request).getServletRequest();
        String requestURL = ((ServletServerHttpRequest) request).getServletRequest().getRequestURL().toString();
        String userId = requestURL.substring(requestURL.lastIndexOf('/')+1, requestURL.length());
        attributes.put("userId", userId);
        return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse
            response, WebSocketHandler wsHandler, Exception exception) {
    }
}
  • 前端连接WS的HTML界面(连接WebSocket,)
<!DOCTYPE html>
<html>

<head>
    <meta name="viewport" http-equiv="Content-Type" content="text/html" charset="UTF-8"/>
    <title>WebSocket 客户端</title>
</head>

<body>
<div>
    WSParameter<input type="text" id="ws_value"/><br/><!--仅用来识别用户身份使用-->
    <input type="button" id="btnConnection" value="connect" /><br/>
    <input type="button" id="btnClose" value="close" /><br/>
    <input type="button" id="btnSend" value="send" /><br/>
</div>
<script src="js/jquery-1.11.1.min.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" type="text/javascript" charset="utf-8">
    var socket;

    if(typeof(WebSocket) == "undefined") {
        alert("您的浏览器不支持WebSocket");
    }

    $("#btnConnection").click(function() {
        //实现化WebSocket对象,指定要连接的服务器地址与端口
        var temp = $('#ws_value').val();
        alert(temp);
        socket = new WebSocket("ws://10.1.1.226:8081/ws/" + temp);
        //打开事件
        socket.onopen = function() {
            alert("Socket 已打开");
        };
        //获得消息事件
        socket.onmessage = function(msg) {
            alert(msg.data);
        };
        socket.onclose = function(event) {
            // 别处登录
            if(event.code == 4001){
                alert("当前账号已在别处登录");
                location.replace("login.html");
            }
            // 正常退出
            if(event.code == 4000){
                location.replace("login.html");
            }
            // 用户信息失效
            if(event.code == 4002){
                location.replace("login.html");
            }
        }
        //发生了错误事件
        socket.onerror = function() {
            alert("发生了错误");
        }
    });

    //发送消息
    $("#btnSend").click(function() {
        socket.send("这是来自客户端的消息" + location.href + new Date());
    });

    //关闭
    $("#btnClose").click(function() {
        socket.close();
    });
</script>
</body>

</html>
  • 登录界面(并没有什么卵用,仅用来账号在其他设备登录,当前登录被登出时使用)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>这是登录界面</title>
</head>
<body>
    <form action="http://10.1.1.226:8081/staff_archives/login2" method="get">
        用户id:<input type="text" name="userId" /></p>
        用户名: <input type="text" name="userName" /></p>
        密码: <input type="text" name="password" /></p>
        <input type="submit" value="登录" />
    </form>
</body>
</html>
  • 实验

①两个Parameter都输入123,代表时同一个用户,在不同的设备登录

②点击第一个连接成功,点击第二个连接成功,第一个界面显示账号在其他地方登陆

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SuperLBY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值