SpringBoot+Vue实现Websocket通信及Java实现客户端

目录

使用场景

原理和知识点

Java代码实现

服务端实现

Java实现客户端

Vue代码实现

总结


使用场景

希望做一个能够替代轮询的大屏后台系统,可以向多个客户端主动推送更改数据,而非轮询请求。

原理和知识点

WebSocket理解

看完让你彻底理解 WebSocket 原理

Http、Socket、WebSocket之间联系与区别

WebSocket介绍和Socket的区别

Websocket原理及基本属性和方法

上面的博客讲了一些基本的概念原理和理解,例子也比较容易理解,有时间可自行查看,不想看直接向下看代码。

说一下个人的理解,仅供参考(与Http作比较):

  • WebSocket类似于Http,都是一种可靠性传输协议,就是用来传输通信的。

  • 但是Http是短连接,每次连接完成后都需要断开,第二次连接时需要重新请求;而WebSocket则是连接一次后,便可以持久通信的,这也是两者最大的区别。

  • 同时Http只支持请求-回复,服务端不能主动向客户端发送消息,而WebSocket则可以双向通信(全双工)

  • 总的来说就是,WebSocket可以用来通信,一次连接后即可实现持久性双向通信。


Java代码实现

服务端实现

知识点了解后,还不来两行代码过过瘾?废话不多说,上码!

万事先依赖:

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

首先,我们先配置WebSocketConfig,配置ServerEndpointExporter去查找带有@ServerEndPoint注解的服务类。

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

然后,编写WebSocketServer服务类,添加上注解@ServerEndPoint(“/自定义地址”)(表明这是WebSocket的服务类)和@Component(注入容器)。

同时需要声明以下监听方法:

  • @OnOpen,当有客户端连接时触发

  • @OnClose,当有客户端关闭时触发

  • @OnMessage,当收到客户端消息时触发

  • @OnError,当通信发生错误时触发

@Component
@ServerEndpoint("/websocket/{userName}")
public class WebSocketServer {
​
    private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);
    //在线客户端数量
    private static int  onlineCount = 0;
    //Map用来存储已连接的客户端信息
    private static ConcurrentHashMap<String, SocketDomain> websocketMap = new ConcurrentHashMap<>();
    //当前连接客户端的Session信息
    private Session session;
    //当前客户端名称
    private String userName="";
    
    @OnOpen
    public void onOpen(Session session, @PathParam("userName") String userName){
        if(!websocketMap.containsKey(userName)){
            WebSocketServer.onlineCount++;
        }
        this.session = session;
        this.userName = userName;
        SocketDomain socketDomain = new SocketDomain();
        socketDomain.setSession(session);
        socketDomain.setUri(session.getRequestURI().toString());
        websocketMap.put(userName, socketDomain);
        logger.info("用户连接:"+ userName + ",人数:"+onlineCount);
        try {
            sendMessage("连接成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @OnClose
    public void onClose(){
        if(websocketMap.containsKey(userName)){
            websocketMap.remove(userName);
            onlineCount--;
            logger.info("用户关闭:"+ userName + ",人数:"+onlineCount);
        }
    }
​
    @OnMessage
    public void onMessage(String message,Session session){
        if(StringUtil.isNotEmpty(message)){
            logger.info("收到用户消息:"+userName+",报文:"+message);
        }
    }
​
​
​
    //给当前客户端发消息
    private void sendMessage(String obj) {
        synchronized (session) {
            this.session.getAsyncRemote().sendText(obj);
        }
    }
​
​
​
    //给指定客户端发送消息,通过UserName找到Session发送
    private void sendMessageTo(String userName,String obj){
        SocketDomain socketDomain = websocketMap.get(userName);
        try {
            if(socketDomain !=null){
                socketDomain.getSession().getAsyncRemote().sendText(obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }
    //给除了当前客户端的其他客户端发消息
    private void sendMessageToAllExpectSelf(String message, Session fromSession) {
        for(Map.Entry<String, SocketDomain> client : websocketMap.entrySet()){
            Session toSeesion = client.getValue().getSession();
            if( !toSeesion.getId().equals(fromSession.getId())&&toSeesion.isOpen()){
                toSeesion.getAsyncRemote().sendText(message);
                logger.info("服务端发送消息给"+client.getKey()+":"+message);
            }
        }
    }
    //给包括当前客户端的全部客户端发送消息
    private void sendMessageToAll(String message){
        for(Map.Entry<String, SocketDomain> client : websocketMap.entrySet()){
            Session toSeesion = client.getValue().getSession();
            if(toSeesion.isOpen()){
                toSeesion.getAsyncRemote().sendText(message);
                logger.info("服务端发送消息给"+client.getKey()+":"+message);
            }
        }
    }
    //给外部调用的方法接口
    public void sendAll(String Message){
        sendMessageToAll(Message);
    }
​
​
}
​​​​​​​@Data
public class SocketDomain {
​
    private Session session;
​
    private String uri;
}

以上,服务端就写好了。WebSocket在线测试地址可以通过这个网站来测试自己的服务是否正常。

服务端OK后,就是实现客户端了,分别是java版本和Vue版本。Html和Vue差不多,可以自行编写和查找。


Java实现客户端

还是那句话,万事先依赖:

<dependency>
    <groupId>org.java-websocket</groupId>
    <artifactId>Java-WebSocket</artifactId>
    <version>1.4.0</version>
</dependency>

然后,编写客户端:

//继承WebSocketClient,重写监听方法和构造方法
public class MyWebSocketClient extends WebSocketClient {
    private static final Logger logger = LoggerFactory.getLogger(MyWebSocketClient.class);
​
    public MyWebSocketClient(URI serverUri){
        super(serverUri);
    }
    @Override
    public void onOpen(ServerHandshake serverHandshake) {
        logger.info("客户端和ws服务已经连接上了");
    }
​
    @Override
    public void onMessage(String s) {
        logger.info("收到服务器消息:"+ s);
    }
​
    @Override
    public void onClose(int i, String s, boolean b) {
        logger.info("客户端和服务端连接关闭");
        logger.info(String.valueOf(i));
        logger.info(s);
        logger.info(String.valueOf(b));
    }
​
    @Override
    public void onError(Exception e) {
        logger.info("通信发生错误");
    }
}

很简单,客户端已经写完了,但是总的让客户端和服务端连起来,所以,写个测试类:

这里有两点需要注意,很多人没有myWebSocketClient.connect();这一句,但是我自己测试发现,没有这一句连接是无法连接上的,于是我去构造函数里看了下,果真没发现有连接的方法,所以这个地方我添加了connect方法。(还有一个ConnectBlocking,里面包含了connect方法并返回布尔类型状态,所以效果一样,close方法相似)

还有一个地方需要注意,在客户端连接时,可能会存在延迟,如果客户端还没有连接上时就直接send,是会报错的,所以,我们先需要确保与服务端连接上再进行发送。

@RestController
public class WebSocketController {
​
    @GetMapping("/websocket/main")
    public void main() throws URISyntaxException, InterruptedException {
        MyWebSocketClient myWebSocketClient    = new MyWebSocketClient(new URI("ws://上面自定义的服务端地址"));
        //主动与服务端连接
        myWebSocketClient.connect();
        //确保与服务端连接上,再进行下一步
        while(!myWebSocketClient.getReadyState().equals(ReadyState.OPEN)){
            System.out.println("连接中。。。");
        }
        //给服务端发消息
        myWebSocketClient.send("我是Java客户端");
        //        关闭客户端
        //        myWebSocketClient.close();
        //        Thread.sleep(3000);
        //        System.out.println(myWebSocketClient.getReadyState());
    }
}

然后启动服务,发个消息试试吧。

客户端日志:

连接中。。。 2022-01-08 14:54:49.924 INFO 18124 --- [ctReadThread-37] c.e.t.Controller.MyWebSocketClient       : 客户端和ws服务已经连接上了 2022-01-08 14:54:49.925 INFO 18124 --- [ctReadThread-37] c.e.t.Controller.MyWebSocketClient       : 收到服务器消息:连接成功

服务端日志:

2022-01-08 14:54:49.921 INFO 6636 --- [io-8087-exec-10] c.e.testdemo2.WebSocket.WebSocketServer : 用户连接:main,人数:2 2022-01-08 14:54:49.926 INFO 6636 --- [nio-8087-exec-2] c.e.testdemo2.WebSocket.WebSocketServer : 收到用户消息:main,报文:我是Java客户端

以上,连接且通信成功!

Vue代码实现

Vue实现客户端

可以先看WebSocket API,写的也挺清楚的,具体代码直接贴了!

<template>
<button @click="webclick">给后台发送消息</button>
</template>
​
<script>
export default {
    name: "web2",
    data() {
        return {
            ws: null
        }
    },
    created() {
        //连接WebSocket服务端,然后初始化监听事件
        this.ws = new WebSocket("ws://localhost:8087/websocket/test");
        this.wsInit();
​
    },
    methods: {
        wsInit() {
            
            this.ws.onopen = () => {
                this.ws.send("服务已连接");
                console.log(this.ws.readyState)
            }
            this.ws.onclose = () => {
                console.log("服务器关闭")
                console.log(this.ws.readyState)
            }
            this.ws.onmessage = (message) => {
                console.log("收到服务器消息")
                console.log(message)
                console.log(this.ws.readyState)
            }
            this.ws.onerror = (error) => {
                console.log("报错了")
                console.log(error)
                console.log(this.ws.readyState)
            }
            
        }
    }
}
</script>

输出日志:

 

这种写法是比较简单的写法,还有一个稍微复杂的写法,添加监听事件,功能相似,但是更能让人理解。可以进入WebSocket类里查看或者可看springboot+vue实现Websocket这篇博客,我上面的服务端和vue客户端都是借鉴的这个博客写的。

总结

多个客户端和服务器

先看日志:

服务端发送日志:

Java客户端接受日志:

Vue客户端接收日志:

 

再看vue客户端发送日志:

再看服务端接收日志消息:

java客户端效果类似,不再重复看了!


以上总结:WebSocket支持客户端和服务端双向通信;服务端不仅可以指定发送给某一个客户端,可以群发给客户端,实现广播效果。而且连接后,可以持久性通信,不论是对于聊天通信等场景还是替换前端轮询请求都是一种很好的解决方案

最后,再推荐一个不错的代码博客WebSocket群聊(HTML前端写的简单界面,可以了解一下)。

🙉愿猿圆缘,愿猿圆愿🙈

 

  • 2
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Vue(前端框架)和Spring Boot(后端框架)结合起来实现websocket双工通信的步骤如下: 1. 首先,在Vue项目中安装`vue-native-websocket`插件。这个插件能够帮助我们在Vue中使用websocket。 2. 在Vue项目的根目录下创建一个文件,例如`webSocket.js`,在这个文件中,引入`vue-native-websocket`插件,并配置websocket服务的地址和端口号。 3. 在Vue项目的入口文件(例如`main.js`)中,引入`webSocket.js`文件,并将websocket插件注册到Vue实例中。 4. 在Vue组件中,使用`this.$socket`来访问websocket对象。可以使用`this.$socket.send()`方法发送消息给后端。 5. 在Spring Boot项目中,添加`spring-boot-starter-websocket`的依赖。 6. 创建一个继承自`WebSocketConfigurer`接口的类,并实现其中的`registerWebSocketHandlers`方法。在该方法中,注册一个`WebSocketHandler`来处理前端与后端之间的websocket连接和消息传递逻辑。 7. 在`WebSocketHandler`中,重写`handleTextMessage`方法来处理接收到的文本消息。可以在这个方法中进行消息的处理逻辑,例如广播消息给所有连接的客户端。 8. 在Spring Boot的配置类(例如`Application.java`)中,添加`@EnableWebSocket`来启用websocket支持。 9. 启动Spring Boot项目,并运行Vue项目。此时,前端可以使用websocket连接到后端,并进行双工通信。前端可以通过`this.$socket.send()`方法发送消息给后端,后端可以通过`WebSocketHandler`接收处理并响应消息给前端。 以上就是使用Vue和Spring Boot来实现websocket双工通信的基本步骤。通过这种方式,前端和后端可以实时地进行双向通信,方便实现一些实时推送、聊天室等功能。 ### 回答2: Vue和Spring Boot结合实现WebSocket双工通信的步骤如下: 1. 在Vue项目中安装Vue-socket.io插件,可以在Vue项目的根目录下运行以下命令进行安装: ``` npm install --save vue-socket.io ``` 2. 在Vue项目的main.js文件中引入Vue-socket.io插件,并配置socket连接: ```javascript import VueSocketIO from 'vue-socket.io' import SocketIO from 'socket.io-client' Vue.use(new VueSocketIO({ debug: true, connection: SocketIO('http://localhost:8080'), // 这里的地址需要修改为后端的IP地址和端口号 })) ``` 3. 在Vue组件中使用WebSocket进行通信,例如,在Vue组件的created钩子中: ```javascript created() { this.$socket.emit('register', { userId: 123 }) // 发送注册消息给后端 this.$socket.on('message', (data) => { // 监听后端发送的消息 console.log('收到消息:', data) }) } ``` 4. 在Spring Boot中编写WebSocket后端控制器,处理前端发送的消息,并实现双工通信,例如: ```java @Controller public class WebSocketController { @Autowired private SimpMessagingTemplate messagingTemplate; @MessageMapping("/register") public void registerUser(@Payload Map<String, Long> payload) { // 处理注册逻辑,例如保存用户ID等 Long userId = payload.get("userId"); // 广播消息给所有连接的用户 messagingTemplate.convertAndSend("/topic/message", "用户 " + userId + " 加入了聊天室"); } // 其他接口处理逻辑... } ``` 5. 在Spring Boot中配置WebSocket相关的bean,例如,在配置类中添加以下配置: ```java @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/topic"); // 消息代理前缀 registry.setApplicationDestinationPrefixes("/app"); // 应用消息前缀 } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS(); } } ``` 以上是Vue和Spring Boot实现WebSocket双工通信的基本步骤,当前端发送消息到后端时,后端可以处理并向所有连接的客户端发送广播消息,实现实时的双工通信。 ### 回答3: Vue和Spring Boot均提供了支持WebSocket的功能,通过结合Vue和Spring Boot,我们可以实现WebSocket双工通信。 首先,在Vue项目中使用Vue提供的WebSocket API建立与后端的WebSocket连接,可以使用Vue的mounted生命周期钩子函数来实现这一步骤。通过WebSocket连接后,我们可以使用VueWebSocket对象来发送数据给后端,同时监听后端的数据。 在后端,我们使用Spring Boot提供的WebSocket支持来处理前端的请求。首先,在Spring Boot的配置文件中,我们需要开启WebSocket功能。然后,我们可以通过创建一个WebSocketHandler类来处理前端的请求和发送消息给前端。在WebSocketHandler中,我们可以实现OnOpen、OnMessage、OnClose等方法来处理前端的连接、接收消息和关闭连接。在接收到消息后,我们可以编写相应的业务逻辑,如处理前端发送的数据,然后将处理结果发送给前端。 通过上述步骤,我们实现Vue和Spring Boot之间的WebSocket双工通信。前端可以通过VueWebSocket对象与后端进行实时的双向通信,后端可以处理前端的请求并发送相应的消息给前端。这使得实时的数据交互和通信成为可能,为应用程序添加了更多实时性和交互性。 需要注意的是,在实现WebSocket通信时,我们需要确保Vue和Spring Boot的版本兼容,并且正确配置相关的依赖和配置文件。同时,我们还需要考虑到安全性和性能等方面的因素,如认证和授权、连接数限制等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值