WebSocket

一. WebSocket概述

WebSocket_第1张图片

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

WebSocket_第2张图片

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

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

轮询缺陷:

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

1.1 WebSocket协议

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

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

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

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

WebSocket_第3张图片 2. 服务器响应同意协议更改

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

WebSocket_第4张图片

3.  相互发送数据

WebSocket_第5张图片

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

 WebSocket_第6张图片

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提供的上层封装,它有很多应用级别的封装,我们用它去开发的话,会比较的快捷和方便。

WebSocket_第7张图片

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

 WebSocket_第8张图片

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 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);
    }

}



    
    ws client





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

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

WebSocket_第9张图片

 

 

 

 

 

 

你可能感兴趣的:(websocket,网络协议,网络)