小言_互联网的博客

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

406人阅读  评论(0)

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

添加一个带参构造

重写下面四个方法

直接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>

彩蛋

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


转载:https://blog.csdn.net/xiaozhezhe0470/article/details/110621623
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场