第十七章、Spring Boot WebSocket

课时七十二、 Spring Boot WebSocket:概念篇

课程安排

websocket的概念

websocket的原理

websocket的群聊

websocket单聊

长连接的产生

一、Socket简介

Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求。Socket的英文原义是“孔”或“插座”,作为UNIX的进程通信机制。Socket可以实现应用程序间网络通信。

1.1 TCP/IP协议

TCP/IP(全称:Transmission Control Protocol/Internet Protocol)传输控制协议/因特网互联协议,又名网络通讯协议。TCP/IP协议是目前应用最为广泛的协议,是构成Internet国际互联网协议的最为基础的协议,由TCP和IP协议组成:

1、TCP协议:面向连接的,可靠的,基于字节流的传输层通讯协议,负责数据的可靠性传输问题。

2、IP协议:用于报文交换网络的一种面向数据的协议,主要负责给每台网络设备的一网络地址,保证数据传输到正确的目的地。

2.1UDP协议

UDP(全称:User Datagram Protocol)用户数据报协议,特点:无连接、不可靠、基于报文的传输层协议,优点是发送后不用管,速度比TCP快。

二、WebSocket简介与消息推送


B/S架构的系统多使用HTTP协议,HTTP协议的特点

1、无状态协议

2、用于通过Internet发送请求消息和响应消息

3、使用端口接受和发送消息,默认80端口(https)默认是443

HTTP协议连接方式

HTTP协议决定了服务器与客户端之间的连接方式,无法直接实现消息推送,一些变相的解决办法:

2.1 轮询

客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接。

2.2 长轮询

客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。

2.3 Flash Socket

在页面中内嵌入一个使用了Socket类的 Flash 程序JavaScript通过调用此Flash程序提供的Socket接口与服务器端的Socket接口进行通信,JavaScript在收到服务器端传送的信息后控制页面的显示。

2.4 Websocket

WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。依靠这种技术可以实现客户端和服务器端的长连接,双向实时通信。

三、WebSocket客户端

websocket允许通过JavaScript建立与远程服务器的连接,从而实现客户端与服务器间双向的通信。在websocket中有两个方法:

websocket同时还定义了几个监听函数:

websocket还定义了一个readyState属性,这个属性可以返回websocket所处的状态:

websocket的url开头是ws, 如果需要ssl加密可以使用wss,当我们调用websocket的构造方法构建一个websocket对象(new WebSocket(url))的之后,就可以进行即时通信了。

四、WebSocket服务端

JSR356定义了WebSocket的规范,Tomcat7中实现了该标准。JSR356 的 WebSocket 规范使用 javax.websocket.*的 API,可以将一个普通 Java 对象(POJO)使用 @ServerEndpoint 注释作为 WebSocket 服务器的端点。

五、小结

Socket在应用程序间通信被广泛使用,如果需要兼容低版本的浏览器,建议使用反向ajax或长链接实现;如果纯移动端或不需考虑非现代浏览器则可以直接使用websocket。Flash实现推送消息的方法不建议使用,因为依赖插件且手机端支持不好。关于反向ajax也有一些封装好的插件如“Pushlet”。

课时七十三、Spring Boot WebSocket:原理篇

一、websocket与http

首先HTTP有 1.1 和 1.0 之说,也就是所谓的 keep-alive ,把多个HTTP请求合并为一个,但是 Websocket 其实是一个新协议,跟HTTP协议基本没有关系,只是为了兼容现有浏览器的握手规范。

首先Websocket是基于HTTP协议的,或者说借用了HTTP的协议来完成一部分握手。

2.1 Upgrade和Connection

Upgrade: websocket
Connection: Upgrade

这个就是Websocket的核心了,告诉 Apache 、Tomcat、 Nginx 等服务器:发起的是Websocket协议

Sec-WebSocket

Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

Sec-WebSocket-Version 是告诉服务器所使用的 WebSocket Draft (协议版本),在最初的时候,Websocket协议还在 Draft 阶段,各种奇奇怪怪的协议都有,而且还有很多期奇奇怪怪不同的东西,什么Firefox和Chrome用的不是一个版本之类的,当初Websocket协议太多可是一个大难题。

成功返回

然后服务器会返回下列东西,表示已经接受到请求, 成功建立Websocket:

Upgrade: websocket
Connection: Upgrade

告诉客户端即将升级的是 Websocket 协议,而不是mozillasocket,lurnarsocket或者shitsocket。

Sec-WebSocket-Accept 这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key 。

Sec-WebSocket-Protocol 则是表示最终使用的协议。

课时七十四、Spring Boot WebSocket:编码分析

需要编写两端的代码:服务端和客户端

(1)Client:客户端说明

客户端的代码主要是使用H5的WebSocket进行实现,在前端网页中使用WebSocket进行连接服务端,然后建立Socket连接进行通讯。

(2)Server:服务端说明
 

服务端主要是建立多个客户端的关系,进行消息的中转等。客户端成功连接到服务端之后,就可以通过建立的通道进行发送消息到服务端,服务端接收到消息之后在群发给所有的客户端。

(3)客户端和服务端怎么连接?

客户端通过JS中的WebSocket对象进行连接到服务端:

var websocket = new WebSocket("ws://localhost:8080/websocket");


服务端映射出在上面使用的/websocket 端点呢,使用注解@ServerEndpoint即可:

@ServerEndpoint(value = "/websocket")

当客服端有连接请求了,服务端怎么接收请求,使用注解@OnOpen即可:

@OnOpen
public void onOpen(Session session) {
        this.session = session;
 }

(4)客户端和服务端怎么发送消息?

客户端可以使用webSocket提供的send()方法,如下代码:
//获取输入的文本信息进行发送
var message = document.getElementById('text').value;
websocket.send(message);


服务端怎么发送消息呢?主要是使用在成功建立连接的时候,创建的Session对象进行发送,如下代码:
 session.getAsyncRemote().sendText("恭喜您成功连接上WebSocket");

(5)客户端和服务端怎么接收消息?

客户端接收消息消息使用的是websocket的onmessage回调方法,如下代码:

websocket.onmessage = function(event) {

//文本信息直接显示,如果是json信息,需要转换下在显示.

var data = event.data;

document.getElementById('message').innerHTML += data;}

服务端怎么接收到消息,使用注解@OnMessage,如下代码:

@OnMessage

public void onMessage(String message, Session session) {

        System.out.println("来自客户端的消息:" + message);}

(6)客户端和服务端关闭连接处理?

    客户端使用websocket.close()进行关闭连接;
    服务端使用@OnClose注解监听客户端的关闭动作。

(7)客户端和服务端异常处理?

客户端当有异常信息的时候会回调方法:websocket.onerror;服务端使用@OnError注解监听异常信息。

(8)群聊原理(群发消息)

服务端在和客户端建立连接的时候,会创建一个webSocket对象,我们会将每个连接创建的对象进行报错到一个列表中,比如:CopyOnWriteArraySet(这是线程安全的);在要进行群发的时候,编写我们的列表对象进行群发消息。

(9)单聊原理(一对一消息)

 

单聊的时候,就无需遍历列表,而是需要知道发送者和接受者各自的Session对象,这个Session对象怎么获取呢?Session可以获取到sessionId,发送者在发送消息的时候,携带接收消息的sessionId,那么问题就演变成了:发送者怎么知道接受者的sessionId,那就是加入一个在线用户列表即可,在线用户列表中有用户的基本信息,包括sessionId。

课时七十五、Spring Boot WebSocket:群聊

1、服务器

1.1 新建一个工程

spring-boot-websocket

1.2 引入依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.8.RELEASE</version>
    </parent>

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
      </dependency>
      
     <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

1.3 注入ServerEndpointExporter

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

1.4 websocket的具体实现类

主要是实现几个方法

onOpen

onClose

onMessage

1.5 编写/访问控制

@RequestMapping("/")
    public String index(){
        return "index";
    }

1.6 编写启动类

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

2、客户端

客户端的代码主要是放在index.html页面中,在页面中使用H5提供的WebSocket对象,具体如下代码(src/main/resources/templates/index.html):

3、运行测试

运行App.java启动程序,进行测试

课时七十六、Spring Boot WebSocket:群聊-昵称

(1)昵称显示方式1:消息携带

(2)昵称显示方式2:连接传递

(3)昵称显示方式3:使用httpSession

课时七十七、Spring Boot WebSocket:单聊

单聊。这里使用session.getId()中的id作为唯一的消息通道(这里我们称为通讯的频道号), session.getId()是一个递增的数字,从0开始,递增1,2,3… 实际中并不会使用这个id作为标识,这里只是为了讲解方便。

一、服务端调整


这里我们就不能使用简单的文本消息进行消息的发送了,我们使用json进行消息的发送。所以需要先创建一个消息对象,里面包含了消息发送者,消息接受者,消息类型(单聊还是群聊),还是就是消息

二、客户端调整

这里使用了js对象进行消息的传递,这里使用JSON.stringify将json对象转换为json字符串,如下代码:
//发送消息
    function send() {
        //获取输入的文本信息进行发送
        var message = document.getElementById('text').value;
        var toUser = document.getElementById('toUser').value;
        var socketMsg = {msg:message,toUser:toUser};
        if(toUser == ''){
            //群聊.
            socketMsg.type = 0;
        }else{
            //单聊.
            socketMsg.type = 1;
        }
        
        websocket.send(JSON.stringify(socketMsg));
    }

三、新问题的提出

上面虽然可以实现单聊的方式,但是在具体的实际场景中,想要知道对方的频道号好像不是那么容易的哦,那么这个要怎么解决呢?大家在玩QQ群的时候,应该都用过聊天界面中的右边的群成员吧,对头,就是要实现一个在线群成员列表的功能,这样用户在操作的时候就可以直接通过点击某个在线的用户进行聊天了。博主,你会实现嘛?^_^,博主还暂时没有这个计划,其实看懂了上面的代码之后,要实现在线群成员列表也不是很难的事情了。

猜你喜欢

转载自blog.csdn.net/weixin_38492276/article/details/81145146