欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

WebSocket 从入门到精通 -- Spring boot服务端客户端 -- HTML客户端

程序员文章站 2022-03-07 11:28:32
这两天打算学习一下WebSocket,但是网上一搜索全都是聊天室实战。WebSocket -- 从入门到精通基础讲解Spring Boot 服务端Spring Boot 客户端HTML 客户端基础示例代码Spring Boot服务端maven引入依赖直接copy源码Spring Boot 客户端maven引入依赖创建一个类,继承WebSocketClient添加一个带参构造重写下面四个方法直接copy源码再自定义一个类,创建一个static静态方法返回一个WebSocketClient对象直接copy....

注意:学习本文章一定要打开自己的开发工具,代码中有详细的解释。电脑不在身边建议先收藏,方便日后观看。最后祝大家技术突飞猛进,早日拿到心仪的offer。

基础讲解

Spring Boot 服务端

这里我们需要先介绍几个注解

注解名称 说明
@ServerEndpoint 指定的客户端请求的URI
@OnOpen 建立连接成功调用
@OnClose 关闭连接时调用
@OnMessage 收到客户端消息后调用的方法
@OnError 发生错误触发

Spring Boot 客户端

监听事件与服务器端一样,不在陈述。

创建步骤:

  1. 自定义一个类继承WebSocketClient
  2. 声明一个带参的构造方法(参数为ServerEndpoint指定的URI)
    例如:ws://localhost:8080/websocketdemo
  3. 将参数通过super(serverUri)传给父类的构造方法;
  4. 请看下方详细代码

HTML 客户端

//创建WebSocket对象
var websocket = new WebSocket("ws://localhost:8080/websocketdemo");

四大监听参数

事件名称 调用方式 触发监听事件
open websocket.onopen 连接建立时触发
message websocket.onmessage 客户端接收服务端数据时触发
error websocket.onerror 通信发生错误时触发
close websocket.onclose 连接关闭时触发

请求方法

方法 说明
websocket.send() 像服务端发送数据
websocket.close() 关闭连接

基础示例代码

Spring Boot服务端

maven引入依赖

<!--WebSocket核心依赖包-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
    <groupId>org.java-websocket</groupId>
    <artifactId>Java-WebSocket</artifactId>
    <version>1.4.1</version>
    <scope>compile</scope>
</dependency>

这里主要就是四大监听参数,以及设置URI地址。

直接copy源码

import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;

//这里的服务地址为:ws://localhost:8080/websocketdemo
@ServerEndpoint("/websocketdemo")
@Component
public class MyDemoServer {

    /**
     * 百度搜索:程序员小哲
     * 建立连接成功调用
     * @param session
     */
    @OnOpen
    public void onOpen(Session session) {
        System.out.println("连接成功!");
    }

    /**
     * 百度搜索:程序员小哲
     * 关闭连接时调用
     * @param session
     */
    @OnClose
    public void onClose(Session session) {
        System.out.println("关闭连接!");
    }

    /**
     * 百度搜索:程序员小哲
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(Session session,String message) {
        System.out.println("接收消息:"+message);
    }

    /**
     * 百度搜索:程序员小哲
     * @param session
     * @param throwable
     */
    @OnError
    public void onError(Session session, Throwable throwable) {
        System.out.println("连接异常:{}"+throwable);
    }

}

Spring Boot 客户端

稍微复杂,可以使用HTML客户端

maven引入依赖

<!--WebSocket核心依赖包-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
    <groupId>org.java-websocket</groupId>
    <artifactId>Java-WebSocket</artifactId>
    <version>1.4.1</version>
    <scope>compile</scope>
</dependency>

创建一个类,继承WebSocketClient

添加一个带参构造

WebSocket 从入门到精通 -- Spring boot服务端客户端 -- HTML客户端

重写下面四个方法

WebSocket 从入门到精通 -- Spring boot服务端客户端 -- HTML客户端

直接copy源码

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;

public class MyWebSocketClient extends WebSocketClient {

    public MyWebSocketClient(URI serverUri) {
        super(serverUri);
    }

    @Override
    public void onOpen(ServerHandshake serverHandshake) {
        System.out.println("连接成功!");
    }

    @Override
    public void onMessage(String s) {
        System.out.println("接收到服务端数据:"+s);
    }

    @Override
    public void onClose(int i, String s, boolean b) {
        System.out.println("关闭连接"+s);
    }

    @Override
    public void onError(Exception e) {
        System.out.println("发生异常"+e);
    }

}

再自定义一个类,创建一个static静态方法返回一个WebSocketClient对象

直接copy源码

这里就是创建了连接服务器的方法。

import com.xiaozhe.websocket.MyWebSocketClient;
import org.java_websocket.client.WebSocketClient;

import java.net.URI;
import java.net.URISyntaxException;

public class WebSocketDemo {

    public static WebSocketClient webSocketClient() {
        try {
            MyWebSocketClient webSocketClient = new MyWebSocketClient(new URI("ws://localhost:8080/websocketdemo"));
            webSocketClient.connect();
            return webSocketClient;
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return null;
    }
}

编写一个Collections类,向服务端发送消息

我们可以使用浏览器url方式访问,也可以通过postman等测试工具来进行访问。
url : localhost:8080/sendfirst

import com.xiaozhe.bo.FirstClient;
import org.java_websocket.client.WebSocketClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class WebSocketController {

    WebSocketClient webSocketClient = WebSocketDemo.webSocketClient();

    @RequestMapping("sendfirst")
    public void sendfirst() {
        webSocketClient.send("百度搜索:程序员小哲");
    }

}

HTML 客户端

直接copy源码

简单的h5客户端代码,可以创建连接,断开连接,向服务端发送数据。

<!DOCTYPE HTML>
<html>
<head>
    <title>WebSocketDemo</title>
</head>

<body>
<button onclick="oppen()">创建链接</button>
<button onclick="closeWebSocket()">断开连接</button>
<hr>
<input id="text" type="text" />
<button onclick="send()">向服务器发送数据</button>
<div id="message"></div>
</body>
 
<script type="text/javascript">
    var websocket = null;
 
	//创建连接
    function oppen(){
		//判断当前浏览器是否支持WebSocket
		if('WebSocket' in window){
			//连接WebSocket节点
			websocket = new WebSocket("ws://localhost:8080/websocketdemo");
		}
		else{
			alert('当前浏览器不支持websocket');
		}
	 
		//连接发生错误的回调方法
		websocket.onerror = function(){
			setMessageInnerHTML("错误!");
		};
	 
		//连接成功建立的回调方法
		websocket.onopen = function(event){
			setMessageInnerHTML("连接成功!");
		}
	 
		//接收到消息的回调方法
		websocket.onmessage = function(event){
			setMessageInnerHTML(event.data);
		}
	 
		//连接关闭的回调方法
		websocket.onclose = function(){
			setMessageInnerHTML("连接关闭!");
		}
		//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
		window.onbeforeunload = function(){
			websocket.close();
		}
	 
		//将消息显示在网页上
		function setMessageInnerHTML(innerHTML){
			document.getElementById('message').innerHTML += innerHTML + '<br/>';
		}
    }
 
    //关闭连接
    function closeWebSocket(){
        websocket.close();
    }
 
    //发送消息
    function send(){
        var message = document.getElementById('text').value;
        websocket.send(message);
    }
</script>
</html>

进阶示例代码

之前的例子是实现单个客户端向服务端发送信息,但是如果服务端向指定的客户端发消息应该怎么做呢。那么请看下面的代码:
建议大家将代码复制到IDE中,便于查看,代码中有详细注释便于理解。

Spring Boot服务端

编写服务端代码

这里我们在主要的四大监听方法之外加了一些属性和方法。大家可以新建一个Spring boot项目来进行下面的项目。

import com.alibaba.fastjson.JSONObject;
import com.example.demo.zo.Xiao;
import com.example.demo.zo.Zhe;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint("/websocketdemoxiaozhe")
@Component
public class MyServer {

    //将所有客户端对象储存到Map集合中
    private static Map<String, MyServer> clients = new ConcurrentHashMap<String, MyServer>();
    //会话
    private Session session;
    //会话唯一标识,使用cid来确定唯一的
    private String cid;

    /**
     * 百度搜索:程序员小哲
     * 建立连接成功调用
     * @param session
     */
    @OnOpen
    public void onOpen(Session session) {
        System.out.println("连接成功!");
    }

    /**
     * 百度搜索:程序员小哲
     * 关闭连接时调用
     * @param session
     */
    @OnClose
    public void onClose(Session session) {
        System.out.println("关闭连接!");
    }

    /**
     * 百度搜索:程序员小哲
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(Session session,String message) {
        // message是一个json
        // 将message进行反序列化
        // 详见我的另一篇博客:https://blog.csdn.net/xiaozhezhe0470/article/details/110652012
        JSONObject jsonObject = JSONObject.parseObject(message);
        Zhe zhe = JSONObject.toJavaObject(jsonObject, Zhe.class);
        //由于我们是在创建之后才提交cid,所以我们需要在获取提交的信息的时候进行判断,查看是哪一次的请求
        if (zhe.getHi() != null){//第二次之后的请求
            System.out.println(zhe.getHi());
        }else {//第一次请求
            Xiao xiao = JSONObject.toJavaObject(jsonObject, Xiao.class);
            System.out.println(xiao);
            this.cid = xiao.getCid();
            this.session = session;
            clients.put(cid,this);
            try {
                //将信息返回到指定用户的页面中
                sendMessageTo("恭喜"+xiao.getWork()+xiao.getCname()+"注册成功!您的账号为:"+cid,cid);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 百度搜索:程序员小哲
     * @param session
     * @param throwable
     */
    @OnError
    public void onError(Session session, Throwable throwable) {
        System.out.println("连接异常:{}"+throwable);
    }

    /**
     * 百度搜索:程序员小哲
     * 向指定的客户端发送数据
     * @param message 推送的信息
     * @param cid 客户id
     */
    public void sendMessageTo(String message, String cid) throws IOException {
        MyServer myWebSocket = clients.get(cid);
        myWebSocket.session.getAsyncRemote().sendText(message);
    }

    /**
     * 百度搜索:程序员小哲
     * 给所有用户进行推送信息
     * @param message 推送的信息
     */
    public void sendMessageAll(String message) throws IOException {
        for (MyServer item : clients.values()) {
            item.session.getAsyncRemote().sendText(message);
        }
    }

}

向客户端推送请求

在这里我们新加了一个基础代码里面没有的类,就是给指定的用户推送消息。

import com.example.demo.config.MyServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

@RestController
public class ServiceToClientDemo {

    @Autowired
    public MyServer myServer;

    /**
     * 给指定的用户推送信息
     * @param cid 用户注册后弹出的cid
     * @param message 想要推送给用户的信息
     */
    @GetMapping("/sendToClient")
    public void sendTo(String cid,String message) throws IOException {
        myServer.sendMessageTo(message,cid);
    }

    /**
     * 给所有用户推送信息
     * @param msg 想要推送给用户的信息
     */
    @GetMapping("/sendAllClient")
    public void sendAll(String msg) throws IOException {
        myServer.sendMessageAll(msg);
    }

}

所用实体类

引入依赖

使用实体类之前需要引入@Data的依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

第一次请求实体类

import lombok.Data;

@Data
public class Xiao {

    private String cid;
    private String work;
    private String cname;

}

第二次请求实体类

import lombok.Data;

@Data
public class Zhe {

    private String hi;

}

HTML客户端

与基础代码相比,多了一个登陆认证。这里需要注意的是,我们需要先创建连接,然后登陆认证,最后在向服务端发送数据,这里没有加验证,不按照顺序来会报错的。
再有一个就是我在两个文本框里面加了默认的JSON大家直接点击即可,方便大家操作。

<!DOCTYPE HTML>
<html>
<head>
    <title>Test My WebSocket</title>
</head>
 
<body>
<button onclick="oppen()">创建连接</button>
<button onclick="closeWebSocket()">断开连接</button>
<hr>
<input  id="text01" type="text" value="{ 	'cid': '713', 	'cname': '小哲', 	'work': '程序员' 	}"/>
<button onclick="first()">登陆认证</button>
<hr>
<input  id="text" type="text" value="{ 	'hi': '你好呀'  }" />
<button onclick="send()">向服务器发送数据</button>
<div id="message"></div>
</body>
 
<script type="text/javascript">
    var websocket = null;
 
	//创建连接
    function oppen(){
		//判断当前浏览器是否支持WebSocket
		if('WebSocket' in window){
			//var message01 = document.getElementById('text01').value;
			//连接WebSocket节点
			websocket = new WebSocket("ws://localhost:8080/websocketdemoxiaozhe");
		}
		else{
			alert('Not support websocket')
		}
	 
		//连接发生错误的回调方法
		websocket.onerror = function(){
			setMessageInnerHTML("error");
		};
	 
		//连接成功建立的回调方法
		websocket.onopen = function(event){
			setMessageInnerHTML("open");
		}
	 
		//接收到消息的回调方法
		websocket.onmessage = function(event){
			setMessageInnerHTML(event.data);
		}
	 
		//连接关闭的回调方法
		websocket.onclose = function(){
			setMessageInnerHTML("close");
		}
	 
		//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
		window.onbeforeunload = function(){
			websocket.close();
		}
	 
		//将消息显示在网页上
		function setMessageInnerHTML(innerHTML){
			document.getElementById('message').innerHTML += innerHTML + '<br/>';
		}
    }
 
    //关闭连接
    function closeWebSocket(){
        websocket.close();
    }
	
	//登陆账号
	function first(){
		var message01 = document.getElementById('text01').value;
		websocket.send(message01);
	}
 
    //发送消息
    function send(){
        var message = document.getElementById('text').value;
        websocket.send(message);
    }
</script>
</html>

超级进阶示例代码

大家有没有觉得每次创建连接在进行注册非常的麻烦,有没有一种办法可以直接在连接的时候就进行注册呢?那么请看下面的代码。

Spring Boot服务端代码

改动之处

一、 @ServerEndpoint("/websocketdemoxiaozhe01/{cid}")
在这里我们在后面加上了一个参数,就是我们的唯一标识cid。
二、 public void onOpen(@PathParam(“cid”) String cid, Session session) {…}
我们在onOpen方法里面多加了一个参数,注意第一个参数一定要用@PathParam修饰,不然项目跑不起来
然后就没有什么不一样了。直接上代码copy到开发工具上。

maven引入依赖

<!--WebSocket核心依赖包-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
    <groupId>org.java-websocket</groupId>
    <artifactId>Java-WebSocket</artifactId>
    <version>1.4.1</version>
    <scope>compile</scope>
</dependency>

开启服务端代码

import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint("/websocketdemoxiaozhe01/{cid}")
@Component
public class MyServerDemo {

    //将当前对象储存到Map集合中
    private static Map<String, MyServerDemo> clients = new ConcurrentHashMap<String, MyServerDemo>();
    //会话
    private Session session;
    //会话唯一标识
    private String cid;

    /**
     * 百度搜索:程序员小哲
     * 建立连接成功调用
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam("cid") String cid, Session session) {
        System.out.println("连接成功!");
        this.cid = cid;
        this.session = session;
        clients.put(cid,this);
        try {
            //将信息返回到指定用户的页面中
            sendMessageTo("恭喜注册成功!您的账号为:"+cid,cid);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 百度搜索:程序员小哲
     * 关闭连接时调用
     * @param session
     */
    @OnClose
    public void onClose(Session session) {
        System.out.println("关闭连接!");
    }

    /**
     * 百度搜索:程序员小哲
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(Session session,String message) {
        System.out.println(message);
    }

    /**
     * 百度搜索:程序员小哲
     * @param session
     * @param throwable
     */
    @OnError
    public void onError(Session session, Throwable throwable) {
        System.out.println("连接异常:{}"+throwable);
    }

    /**
     * 百度搜索:程序员小哲
     * 向指定的客户端发送数据
     * @param message 推送的信息
     * @param cid 客户id
     */
    public void sendMessageTo(String message, String cid) throws IOException {
        MyServerDemo myWebSocket = clients.get(cid);
        myWebSocket.session.getAsyncRemote().sendText(message);
    }

    /**
     * 百度搜索:程序员小哲
     * 给所有用户进行推送信息
     * @param message 推送的信息
     */
    public void sendMessageAll(String message) throws IOException {
        for (MyServerDemo item : clients.values()) {
            item.session.getAsyncRemote().sendText(message);
        }
    }

}

向客户端推送请求

示例url:http://localhost:8080/sendToClient01?cid=111&message=你好
示例url:http://localhost:8080/sendAllClient01?message=你们好

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

@RestController
public class ServiceToClientDemoDemo {

    @Autowired
    public MyServerDemo myServerDemo;

    /**
     * 给指定的用户推送信息
     * @param cid 用户注册后弹出的cid
     * @param message 想要推送给用户的信息
     */
    @GetMapping("/sendToClient01")
    public void sendTo(String cid,String message) throws IOException {
        myServerDemo.sendMessageTo(message,cid);
    }

    /**
     * 给所有用户推送信息
     * @param msg 想要推送给用户的信息
     */
    @GetMapping("/sendAllClient01")
    public void sendAll(String msg) throws IOException {
        myServerDemo.sendMessageAll(msg);
    }

}

H5 客户端代码

H5客户端唯一的区别就是在创建连接的时候将cid一起传到了后台。
websocket = new WebSocket(“ws://localhost:8080/websocketdemoxiaozhe01/” + message01);

<!DOCTYPE HTML>
<html>
<head>
    <title>Test My WebSocket</title>
</head>
 
<body>
websocketid
<input  id="text01" type="text" value="111"/>
<button onclick="oppen()">创建链接</button>
<hr>
<input  id="text" type="text" />
<button onclick="send()">向服务器发送数据</button>
<button onclick="closeWebSocket()">断开连接</button>
<div id="message"></div>
</body>
 
<script type="text/javascript">
    var websocket = null;

	//创建连接
    function oppen(){
		//判断当前浏览器是否支持WebSocket
		if('WebSocket' in window){
			var message01 = document.getElementById('text01').value;
			//连接WebSocket节点
			websocket = new WebSocket("ws://localhost:8080/websocketdemoxiaozhe01/" + message01);
		}
		else{
			alert('Not support websocket')
		}
	 
		//连接发生错误的回调方法
		websocket.onerror = function(){
			setMessageInnerHTML("error");
		};
	 
		//连接成功建立的回调方法
		websocket.onopen = function(event){
			setMessageInnerHTML("open");
		}
	 
		//接收到消息的回调方法
		websocket.onmessage = function(event){
			setMessageInnerHTML(event.data);
		}
	 
		//连接关闭的回调方法
		websocket.onclose = function(){
			setMessageInnerHTML("close");
		}
		//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
		window.onbeforeunload = function(){
			websocket.close();
		}
	 
		//将消息显示在网页上
		function setMessageInnerHTML(innerHTML){
			document.getElementById('message').innerHTML += innerHTML + '<br/>';
		}
    }
 
    //关闭连接
    function closeWebSocket(){
        websocket.close();
    }
 
    //发送消息
    function send(){
        var message = document.getElementById('text').value;
        websocket.send(message);
    }
</script>
</html>

彩蛋

  • 有问题可以扫码评论,我会一一进行回复。

WebSocket 从入门到精通 -- Spring boot服务端客户端 -- HTML客户端

本文地址:https://blog.csdn.net/xiaozhezhe0470/article/details/110621623