客户端服务端实现通信通信行程卡显示错误

太平洋在线 101 1
WebSocket通信协议

WebSocket客户端服务端实现通信,单个TCP连接上进行全双工通信的协议。

WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。

Websocket 通过HTTP/1.1 协议的101状态码进行握手。为了创建Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(handshaking)。

客户端服务端实现通信通信行程卡显示错误-第1张图片-平心在线

SpringBoot服务端实现

复制后,完全可以运行起来的工程~

工程结构

客户端服务端实现通信通信行程卡显示错误-第2张图片-平心在线

pom.xml

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.what21.demo</groupId> <artifactId>demo02</artifactId> <version>1.0-SNAPSHOT</version> <!-- 统一版本配置 --> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <spring-boot.version>2.3.9.RELEASE</spring-boot.version> </properties> <!-- 声明SpringBoot依赖版本 --> <dependencyManagement> <dependencies> <!--spring-boot--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <!-- 引入依赖 --> <dependencies> <!-- SpringBoot - Web依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- SpringBoot - 健康监控 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- SpringBoot - WebSocket --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <!-- SpringBoot - 切面 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!-- SpringBoot - 参数校验 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <!-- SpringBoot - 安全框架 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- webscoket客户端 --> <dependency> <groupId>org.java-websocket</groupId> <artifactId>Java-WebSocket</artifactId> <version>1.3.8</version> </dependency> <!-- lombok插件--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> <scope>provided</scope> </dependency> </dependencies></project>

配置类

package com.what21.demo.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Configurationpublic class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); }}package com.what21.demo.config;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.builders.WebSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.config.http.SessionCreationPolicy;@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true)@Configurationpublic class SpringSecurityConfig extends WebSecurityConfigurerAdapter { @Override public void configure(WebSecurity web) throws Exception { // ws://192.168.3.50:8001/webSocket/ // webscoket地址放行(第1种办法) // web.ignoring().mvcMatchers("/web2/**"); web.ignoring().mvcMatchers("/web2/**", "/webSocket/**"); } @Override protected void configure(HttpSecurity http) throws Exception { // ws://192.168.3.50:8001/webSocket/ // 配置路径放行(第2种办法) http.authorizeRequests().antMatchers("/web/**").permitAll(); // http.authorizeRequests().antMatchers("/web/**", "/webSocket/**").permitAll(); // 关闭 http.headers().frameOptions().disable(); // 带权限以及登录用户访问 http.authorizeRequests().anyRequest().authenticated() .and() // 不需要session .cors() .and().csrf().disable() // 不需要session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); }}

WebSocketServer

package com.what21.demo.websocket;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;import javax.websocket.*;import javax.websocket.server.PathParam;import javax.websocket.server.ServerEndpoint;import java.io.IOException;@ServerEndpoint("/webSocket/{sid}")@Component@Slf4jpublic class WebSocketServer { @OnOpen public void onOpen(Session session, @PathParam(value = "sid") String userId) { log.info("有新连接加入客户端服务端实现通信:{},当前在线人数为:{}", session.getId()); // 加入session池 WebSocketUtils.sessionPools.put(userId, session); // 加 WebSocketUtils.addOnlineCount(); log.info(userId + "加入webSocket,当前人数为:" + WebSocketUtils.onlineNumber); try { WebSocketUtils.sendMessage(session, "欢迎" + userId + "加入连接客户端服务端实现通信!"); } catch (IOException e) { e.printStackTrace(); } } @OnMessage public void onMessage(Session session, String message) { log.info("服务端收到客户端[{}]的消息:{}", session.getId(), message); } @OnError public void onError(Session session, Throwable error) { log.error("发生错误"); error.printStackTrace(); } @OnClose public void onClose(Session session, @PathParam(value = "sid") String userId) { log.info("有一连接关闭:{}", session.getId()); WebSocketUtils.sessionPools.remove(userId); WebSocketUtils.subOnlineCount(); System.out.println(userId + "断开webSocket连接,当前人数为" + WebSocketUtils.onlineNumber); }}package com.what21.demo.websocket;import javax.websocket.Session;import java.io.IOException;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.atomic.AtomicInteger;public final class WebSocketUtils { // 记录当前在线连接数,线程安全的 public static AtomicInteger onlineNumber = new AtomicInteger(); // concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。 public static ConcurrentHashMap<String, Session> sessionPools = new ConcurrentHashMap<>(); // ============================================================================// //发送消息 // ============================================================================// public static void sendMessage(Session session, String message) throws IOException { if (session != null) { synchronized (session) { session.getBasicRemote().sendText(message); } } } /** * 加 */ public static void addOnlineCount() { onlineNumber.incrementAndGet(); } /** * 减 */ public static void subOnlineCount() { onlineNumber.decrementAndGet(); } /** * 启动任务 */ public static void startTask(){ WebSocketTask webSocketTask = new WebSocketTask(sessionPools,"系统通知消息~"); Thread webSocketThread = new Thread(webSocketTask); webSocketThread.setDaemon(true); webSocketThread.run(); }}package com.what21.demo.websocket;import com.fasterxml.jackson.databind.ObjectMapper;import sun.util.calendar.LocalGregorianCalendar;import javax.websocket.Session;import java.io.IOException;import java.util.*;import java.util.concurrent.ConcurrentHashMap;public class WebSocketTask implements Runnable { private ConcurrentHashMap<String, Session> sessionPools; private String message; public WebSocketTask(ConcurrentHashMap<String, Session> sessionPools, String message) { this.sessionPools = sessionPools; this.message = message; } @Override public void run() { int count = 1; while (true) { int random = new Random().nextInt(30); try { Thread.sleep(random * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("任务发送消息..."); Set<String> keySet = this.sessionPools.keySet(); for (String key : keySet) { Session session = this.sessionPools.get(key); try { Map<String, Object> dataMap = new HashMap<>(); dataMap.put("count", count); dataMap.put("time", new Date()); dataMap.put("message", message); // Jackson ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(dataMap); // 发送消息 WebSocketUtils.sendMessage(session, json); } catch (IOException e) { e.printStackTrace(); } } count++; } }}

启动及启动任务

package com.what21.demo;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class WebSocketApplication { public static void main(String[] args) { SpringApplication.run(WebSocketApplication.class, args); }}package com.what21.demo.service;import com.what21.demo.websocket.WebSocketUtils;import lombok.extern.slf4j.Slf4j;import org.springframework.boot.ApplicationArguments;import org.springframework.boot.ApplicationRunner;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;@Component("myApplicationRunner")@Order(1)@Slf4jpublic class MyApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { WebSocketUtils.startTask(); }}

文件配置(application.yml)

server: port: 8001 servlet: context-path: /demo02网页客户端实现

通信地址

ws://192.168.3.50:8001/demo02/webSocket/

效果

客户端服务端实现通信通信行程卡显示错误-第3张图片-平心在线

网页代码

<!DOCTYPE html><html><head> <meta charset="utf-8"> <title>websocket通讯</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.1/css/bootstrap.min.css" rel="stylesheet"> <script> let socketUrl = ""; let socket; // 打开WebSocket function openSocket() { socketUrl = $("#socketAddress").val(); if (typeof (WebSocket) == "undefined") { console.log("您的浏览器不支持WebSocket"); } else { console.log("您的浏览器支持WebSocket"); //WebSocket对象 socketUrl = socketUrl + $("#userId").val() socketUrl = socketUrl.replace("https", "ws").replace("http", "ws"); console.log("socketUrl==>", socketUrl); if (socket != null) { socket.close(); socket = null; } socket = new WebSocket(socketUrl); // 打开事件 socket.onopen = function () { console.log("websocket已打开"); $("#sendMsg").val($("#sendMsg").val() + "websocket已打开rn"); }; // 获得消息事件 socket.onmessage = function (msg) { //发现消息进入,开始处理前端触发逻辑 console.log("data", msg.data); $("#sendMsg").val($("#sendMsg").val() + "接收消息:" + msg.data + "rn"); }; // 关闭事件 socket.onclose = function () { console.log("websocket已关闭"); $("#sendMsg").val($("#sendMsg").val() + "websocket已关闭rn"); }; // 发生了错误事件 socket.onerror = function () { console.log("websocket发生了错误"); $("#sendMsg").val($("#sendMsg").val() + "websocket发生了错误rn"); } } } // 发送消息 function sendMessage() { if (typeof (WebSocket) == "undefined") { console.log("您的浏览器不支持WebSocket"); } else { console.log("您的浏览器支持WebSocket"); let msg = '{"toUserId":"' + $("#toUserId").val() + '","contentText":"' + $("#contentText").val() + '"}'; console.log(msg); socket.send(msg); $("#sendMsg").val($("#sendMsg").val() + "发送消息:" + msg + "rn"); } } // 发送消息 function closeSocket() { if (typeof (WebSocket) == "undefined") { console.log("您的浏览器不支持WebSocket"); } else { // 0、CONNECTING,连接尚未建立 // 1、OPEN,WebSocket的链接已经建立 // 2、CLOSING,连接正在关闭 // 3、CLOSED,连接已经关闭或不可用 if (socket.readyState === 1) { socket.close(); console.log("WebSocket连接已关闭"); } } } // 清除消息 function clearMessage() { $("#sendMsg").val(""); } </script></head><body> <div class="container " style="width:600px"> <span> <h1>WebSocket通信</h1> </span> <form action=""> <div class="form-group"> <label for="socketAddress">通信地址:</label> <input id="socketAddress" name="socketAddress" type="text" class="form-control" value="http://192.168.3.50:8001/demo02/webSocket/" /> </div> <div class="form-group"> <label for="userId">当前用户ID:</label> <input id="userId" name="userId" type="text" class="form-control" value="10" /> </div> <div class="form-group"> <label for="toUserId">目标用户ID:</label> <input id="toUserId" name="toUserId" type="text" class="form-control" value="20" /> </div> <div class="form-group"> <label for="toUserIdMsg">发送消息:</label> <input id="contentText" name="contentText" type="text" class="form-control" value="hello websocket" /> </div> <div class="form-group"> <label for="sendMsg">已发送内容:</label> <textarea id="sendMsg" name="sendMsg" class="form-control" style="min-height: 200px;"></textarea> </div> <div class="form-group form-check"> <label class="form-check-label"> <input class="form-check-input" type="checkbox">记住我 </label> </div> <button type="button" class="btn btn-primary" onclick="openSocket();">开启socket</button> <button type="button" class="btn btn-info" onclick="sendMessage();">发送消息</button> <button type="button" class="btn btn-secondary" onclick="clearMessage();">清除消息</button> <button type="button" class="btn btn-danger" onclick="closeSocket();">关闭socket</button> </form> </div></body></html>

标签: 客户端服务端实现通信

发表评论 (已有1条评论)

评论列表

2022-03-31 13:30:47

session, String message) throws IOException { if (session != null) {