WebSocket-学习调研

一、什么是WebSocket

WebSocket是一种基于单个TCP的全双工通信协议,它为客户端和服务端之间提供了一种双向的,高效的通信手段。WebSocket在第一次TCP握手后,会建立一个长连接,客户端和服务端可以通过这个长连接实现实时的双向通信。WebSocket协议最初由W3C开发,并于2011年成为标准。

二、WebSocket的优缺点

1、优点

实时性:WebSocket在第一次TCP连接后,会建立一个长连接,客户端和服务端之间可以通过这个长连接直接传输数据,避免了像Http多次请求的消耗,WebSocket是有状态的协议,而Http是无状态的协议。

双向性:WebSocket协议中,允许服务端主动像客户端发送消息,可以避免客户端请求才能发送数据,提高效率。

减少网络负载:由于WebSocket的长连接,可以避免很多Http请求的开销,从而减少负载。

无同源限制:WebSocket相比较Http没有同源限制,因为WebSocket是用于实时通信,浏览器不会阻止其跨域连接建立,但是会发送Origin头部。

与 HTTP 协议有着良好的兼容性:默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

2、缺点

(1)支持程度:由于WebSocket是2011年在兴起的新协议,部分老的浏览器和服务器可能不支持WebSocket协议。

(2)额外开销:WebSocket协议建立后会维护一个长连接,这个长连接需要额外的CPU、内存等开销

(3)安全问题:WebSocket是以实时性优先,像一些跨域连接的安全问题等,都需要应用层自主实现。

三、WebSocket的生命周期

1、连接建立阶段

在这个阶段,WebSocket会建立起连接,有客户端发送一个握手请求,服务端响应一个握手响应。响应之后,两者之间就会建立起长连接。

2、连接开放阶段

在这个阶段客户端和服务端之间已经建立起了连接,可以进行双向通信,客户端发送消息给服务端,服务端发送消息给客户端。

3、连接关闭阶段

此时,双方通信结束,有其中一方发送一个关闭帧,来关闭两者之间的连接,这个发送方可以是客户端也可以是服务端。

4、连接关闭完成阶段

这个时候,一方接受到另一方发送过来的关闭帧,也会关闭连接,通信双方都关闭了连接,WebSocket连接彻底关闭。

WebSocketsh生命周期

四、WebSocket应用场景

1、实时聊天:WebSocket能够提供双向、实时的通信机制,使得实时聊天应用能够快速、高效地发送和接收消息,实现即时通信。

2、实时协作:用于实时协作工具,如协同编辑文档、白板绘画、团队任务管理等,团队成员可以实时地在同一页面上进行互动和实时更新。

3、实时数据推送:用于实时数据推送场景,如股票行情、新闻快讯、实时天气信息等,服务器可以实时将数据推送给客户端,确保数据的及时性和准确性。

4、多人在线游戏:实时的双向通信机制,适用于多人在线游戏应用,使得游戏服务器能够实时地将游戏状态和玩家行为传输给客户端,实现游戏的实时互动。

5、在线客服:WebSocket可以用于在线客服和客户支持系统,实现实时的客户沟通和问题解决,提供更好的用户体验,减少等待时间。

五、WebSocket简单使用(基于SpringBoot)

1、引入依赖

WebSocket依赖

2、编写配置类

java 复制代码
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

WebSocket配置类,帮助SpringBoot扫描所有带有@ServerEndpoint注解的类

3、编写WebSocket服务代码

java 复制代码
@Component
@Slf4j
@ServerEndpoint("/api/pushMessage/{userId}")
public class WebSocketServer {

    /**静态变量,用来记录当前在线连接数*/
    private static final AtomicInteger onlineCount = new AtomicInteger(0);
    /**concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象。*/
    private static final ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
    /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
    private Session session;
    /**接收userId*/
    private String userId;

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.session = session;
        this.userId = userId;
        if (webSocketMap.containsKey(userId)) {
            webSocketMap.remove(userId);
            // 加入map中
            webSocketMap.put(userId, this);
        } else {
            // 加入map中
            webSocketMap.put(userId, this);
            // 在线数加1
            onlineCount.incrementAndGet();
        }
        log.info("用户连接:" + userId + ",当前在线人数为:" + onlineCount);
        sendMessage("连接成功");
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        if (webSocketMap.containsKey(userId)) {
            webSocketMap.remove(userId);
            // 在线人数减1
            onlineCount.decrementAndGet();
        }
        log.info("用户退出:" + userId + ",当前在线人数为:" + onlineCount);
    }

    /**
     * 收到客户端消息后调用的方法
     **/
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("用户消息:" + userId + ",报文:" + message);
        // 解析发送的报文
        JSONObject jsonObject = JSON.parseObject(message);
        // 获取需要转发的用户id
        String toUserId = jsonObject.getString("toUserId");
        // 传送给对应toUserId用户的websocket
        if (StringUtils.isNotBlank(toUserId) && webSocketMap.containsKey(toUserId)) {
            webSocketMap.get(toUserId).sendMessage(message);
        } else {
            log.error("请求的userId:" + toUserId + "不在该服务器上");
        }
    }


    /**
     * 发生异常调用方法
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("用户错误:" + this.userId + ",原因:" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) {
        this.session.getAsyncRemote().sendText(message);
    }

    /**
     *发送自定义消息
     **/
    public static void sendInfo(String message, String userId) {
        log.info("发送消息到:" + userId + ",报文:" + message);
        if (StringUtils.isNotBlank(userId) && webSocketMap.containsKey(userId)) {
            webSocketMap.get(userId).sendMessage(message);
        } else {
            log.error("用户" + userId + ",不在线!");
        }
    }
}

4、测试

用户一号发送数据给二号

用户二号收到数据

用户二号发送数据给用户一号

相关推荐
Tonya432 小时前
测开学习DAY28
学习
Lynnxiaowen2 小时前
今天我们开始学习ansible之playbook的简单运用
linux·运维·学习·云计算·ansible
心无旁骛~2 小时前
MotionTrans: 从人类VR数据学习机器人操作的运动级迁移
学习·机器人·vr
挠到秃头的涛某3 小时前
华为防火墙web配置SSL-在外人员访问内网资源
运维·网络·网络协议·tcp/ip·华为·ssl·防火墙
敲敲了个代码3 小时前
11月3-5年Web前端开发面试需要达到的强度
前端·vue.js·学习·react.js·面试·职场和发展·web
树在风中摇曳3 小时前
TCP连接还在吗?主机拔掉网线后再插上,连接会断开吗?
arm开发·网络协议·tcp/ip
提娜米苏3 小时前
Bash Shell脚本学习——唇读数据集格式修复脚本
开发语言·学习·bash
せいしゅん青春之我4 小时前
【JavaEE初阶】IP协议-IP地址不够用了咋办?
java·服务器·网络·网络协议·tcp/ip·java-ee
我最厉害。,。4 小时前
内网对抗-隧道技术篇&防火墙组策略&HTTP反向&SSH转发&出网穿透&CrossC2&解决方案
网络协议·http·ssh