WebSocket

一. WebSocket概述

  • 在后端服务器与服务器之间,HTTP请求是可以相互发送的,但是在浏览器与服务器之间,HTTP请求只能从浏览器发起,方向固定,不能从服务器往浏览器主动去发起HTTP请求。

问题:服务器无法直接向浏览器发送请求(HTTP协议的缺陷)

常见替代方案:轮询,浏览器给服务器不断地发送请求询问支付结果。

轮询缺陷:

  • 浪费带宽(因为浏览器会不断地给服务器发送请求)
  • 实时性差(并不是服务器知道结果以后,浏览器那边就能立刻知道,要等到浏览器下一次去询问的时候才能知道结果)
  • 服务器压力大(特别是像促销之类的活动,服务器会收到大量的询问订单结果的请求)

1.1 WebSocket协议

  • 2008年提出,2011年成为标准
  • 最早是HTML5新增的协议,现在基本所有的语言都支持
  • 作用:可以在浏览器和服务器之间建立一个全双工的通信通道(就是浏览器既可以往服务器发请求,服务器也可以往浏览器发请求,这就叫一个全双工的通信协议)

1.2 WebSocket实现浏览器与服务器的通信流程 / 过程

 1. 浏览器发起HTTP请求,请求建立WebSocket连接

  • 在浏览器发起的HTTP请求当中,URL是以ws:开头的,而不是之前见到的http或者https开头的。ws是WebSocket的缩写。

 2. 服务器响应同意协议更改

  • 服务器响应同意协议更改,会响应一个101的状态码,切换协议的意思

3.  相互发送数据

  • 绿色箭头代表的是浏览器向服务器发送的消息,红色箭头代表的是服务器向浏览器发送的消息

 

1.3 WebSocket底层原理 

  • WebSocket协议建立在TCP协议基础上的,所以服务器端也容易实现,不同的语言都有支持
  • TCP协议是全双工协议,HTTP协议基于它,但设计成了单向的
  • WebSocket协议是对HTTP协议的一个补充,某一些HTTP做不到的场景WebSocket来补充它
  • WebSocket没有同源限制(同一个IP,同一个端口叫同源,就是浏览器端的地址和服务器端的地址它的IP端口是一致的,这就叫同源)
  • 什么是不同源,就是IP端口不一致的。
  • 现在前后端分离项目经常就是不同源的,前端会起一个端口比如8081,后端会起一个端口比如9091,此时前端去访问后端就会存在这个不同源的一个限制,就是会存在一个跨域异常。
  • 可以通过前端配代理去解决或者上到服务器上可以通过Nginx反向代理去解决这个跨域问题。
  • 因为WebSocket没有同源限制,所以即使你的前端的源和后端的源不一致,也不影响WebSocket请求的发送。

二. Java实现WebSocket的两种方式

使用Spring封装

在Spring里面已经帮我们实现了WebSocket的基本功能,它完成了HTTP协议升级为WebSocket协议的这个过程,封装了通信,以及完成了很多的通信细节的功能。

我们并不是要去实现WebSocket这个功能的后端开发,我们要做的就是在Spring给我们提供的实现了WebSocket功能的基础之上去完成一些业务功能的开发,这些业务的功能可以有两种方式来实现:

  • 一种是比较简单的,基于Java注解的方式
  • 另一种是基于Spring提供的上层封装,它有很多应用级别的封装,我们用它去开发的话,会比较的快捷和方便。

 3.1 基于Java注解实现WebSocket服务器端

 

LomBok依赖可以让我们更方便的去打印日志,同时它还提供一些注解可以快速的给实体类生成Getter、Setter方法。

package com.gch.java;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
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.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
   监听WebSocket客户端地址/myWs谁来连接了WebSocket服务端
 */
@ServerEndpoint("/myWs")
@Component // 声明为一个Bean
@Slf4j
public class WsServerEndpoint {
    // 有多个客户端来连接,应该使用线程安全的CurrentHashMap
    public static Map<String,Session> sessionMap = new ConcurrentHashMap<>();
    // 当WebSocket客户端连接WebSocket服务端的时候,我们需要来声明一下我们的服务端去做哪些事情
    // 连接建立时执行的操作
    @OnOpen
    // 每一个WebSocket连接对于服务端来说都是一个session
    public void onOpen(Session session){
       sessionMap.put(session.getId(),session);
       log.info("websocket is open");
    }
    // 监听通信
    // 当WebSocket客户端浏览器给WebSocket服务器发消息的时候,我服务器做什么事情
    // 收到了客户端消息执行的操作
    @OnMessage
    public String onMessage(String text){
        log.info("收到了一条信息:" + text);
        return "已收到你的消息";
    }
    // 监听到连接关闭时执行的操作
    @OnClose
    public void onClose(Session session){
        sessionMap.remove(session.getId(),session);
        log.info("websocket is close");
    }

    // 每隔多少毫秒去执行一次定时任务给客户端发送xx
    @Scheduled(fixedRate = 2000)
    public void sendMsg() throws IOException {
        for(String key: sessionMap.keySet()){
            sessionMap.get(key).getBasicRemote().sendText("心跳");
        }
    }

    // 现在,Spring框架并不能扫描到我们的服务终端,我们还需要再加一个配置类
    // HTTP加上S之后它是一个更安全的传输
}
package com.gch.java;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * 配置类
 * 注入Spring WebSocket框架里的一个对象叫做ServerEndpointExporter
 */
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}
package com.gch;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling // 开启定时任务
@SpringBootApplication
public class WebsocketQuickstartApplication {

    public static void main(String[] args) {
        SpringApplication.run(WebsocketQuickstartApplication.class, args);
    }

}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ws client</title>
</head>
<body>
</body>

<script>
    let ws = new WebSocket("ws://localhost:8080/myWs")
    ws.onopen = function (){
        ws.send("hello")
    }
    ws.onmessage = function (message) {
        console.log(message.data)
    }
</script>
</html>

 3.3   基于Spring框架实现WebSocket服务器端

Spring框架已经在应用层面去帮我们定义了一些接口和抽象类,我们只要实现这些接口和抽象类,就能够完成一个目录比较清晰的WebSocket的服务器端,并且它里面有很多封装的功能我们可以直接使用。

 

 

 

 

 

 

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Surpass余sheng军

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

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

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

打赏作者

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

抵扣说明:

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

余额充值