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、测试

用户一号发送数据给二号

用户二号收到数据

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

相关推荐
吠品8 小时前
企业信任基石OV SSL证书
网络协议·https·ssl
盐焗西兰花9 小时前
鸿蒙学习实战之路-Reader Kit修改翻页方式字体大小及行间距最佳实践
学习·华为·harmonyos
暖馒9 小时前
Modbus应用层协议的深度剖析
网络·网络协议·c#·wpf·智能硬件
QiZhang | UESTC9 小时前
学习日记day76
学习
久邦科技9 小时前
20个免费电子书下载网站,实现电子书自由(2025持续更新)
学习
Gain_chance10 小时前
34-学习笔记尚硅谷数仓搭建-DWS层最近一日汇总表建表语句汇总
数据仓库·hive·笔记·学习·datagrip
Gain_chance11 小时前
36-学习笔记尚硅谷数仓搭建-DWS层数据装载脚本
大数据·数据仓库·笔记·学习
XH华11 小时前
备战蓝桥杯,第九章:结构体和类
学习·蓝桥杯
Gain_chance12 小时前
35-学习笔记尚硅谷数仓搭建-DWS层最近n日汇总表及历史至今汇总表建表语句
数据库·数据仓库·hive·笔记·学习
杨了个杨898212 小时前
memcached部署
qt·websocket·memcached