技术标签: spring boot 初入职场 websocket
WebSocket通信过程
客户端构建一个websocket实例,并且为它绑定一个需要连接到的服务器地址,当客户端连接服务端的候,会向服务端发送一个http get报文,告诉服务端需要将通信协议切换到websocket,服务端收到http请求后将通信协议切换到websocket,同时发给客户端一个响应报文,返回的状态码为101,表示同意客户端协议转请求,并转换为websocket协议。以上过程都是利用http通信完成的,称之为websocket协议握手(websocket Protocol handshake),经过握手之后,客户端和服务端就建立了websocket连接,以后的通信走的都是websocket协议了。
<!--webSocket-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
package com.lby.websocket.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @author Liby
* @date 2022-04-25 16:18
* @description:
* @version:
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
因为WebSocket是类似客户端服务端的形式(采用ws协议),那么这里的WebSocketServer其实就相当于一个ws协议的Controller
下面是具体业务代码:
package com.lby.websocket.component;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
import cn.hutool.core.util.StrUtil;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author Liby
* @date 2022-04-25 16:21
* @description:
* @version:
*/
@ServerEndpoint(value = "/websocket/{userId}")
@Component
public class WebSocket {
private final static Logger logger = LogManager.getLogger(WebSocket.class);
/**
* 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的
*/
private static int onlineCount = 0;
/**
* concurrent包的线程安全Map,用来存放每个客户端对应的MyWebSocket对象
*/
private static ConcurrentHashMap<String, WebSocket> webSocketMap = new ConcurrentHashMap<>();
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
private String userId;
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
this.session = session;
this.userId = userId;
//加入map
webSocketMap.put(userId, this);
addOnlineCount(); //在线数加1
logger.info("用户{}连接成功,当前在线人数为{}", userId, getOnlineCount());
try {
sendMessage(String.valueOf(this.session.getQueryString()));
} catch (IOException e) {
logger.error("IO异常");
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
//从map中删除
webSocketMap.remove(userId);
subOnlineCount(); //在线数减1
logger.info("用户{}关闭连接!当前在线人数为{}", userId, getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session) {
logger.info("来自客户端用户:{} 消息:{}",userId, message);
//群发消息
/*for (String item : webSocketMap.keySet()) {
try {
webSocketMap.get(item).sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}*/
}
/**
* 发生错误时调用
*
* @OnError
*/
@OnError
public void onError(Session session, Throwable error) {
logger.error("用户错误:" + this.userId + ",原因:" + error.getMessage());
error.printStackTrace();
}
/**
* 向客户端发送消息
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
//this.session.getAsyncRemote().sendText(message);
}
/**
* 通过userId向客户端发送消息
*/
public void sendMessageByUserId(String userId, String message) throws IOException {
logger.info("服务端发送消息到{},消息:{}",userId,message);
if(StrUtil.isNotBlank(userId)&&webSocketMap.containsKey(userId)){
webSocketMap.get(userId).sendMessage(message);
}else{
logger.error("用户{}不在线",userId);
}
}
/**
* 群发自定义消息
*/
public static void sendInfo(String message) throws IOException {
for (String item : webSocketMap.keySet()) {
try {
webSocketMap.get(item).sendMessage(message);
} catch (IOException e) {
continue;
}
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocket.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocket.onlineCount--;
}
}
package com.lby.websocket.controller;
import com.lby.websocket.component.WebSocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
/**
* @author Liby
* @date 2022-04-26 10:28
* @description:建立WebSocket连接
* @version:
*/
@RestController
@RequestMapping("/webSocket")
public class WebSocketController {
@Autowired
private WebSocket webSocket;
@PostMapping("/sentMessage")
public void sentMessage(String userId,String message){
try {
webSocket.sendMessageByUserId(userId,message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
http://websocket.jsonin.com/
复制三个tab
WebSocket地址分别输入后连接
ws://127.0.0.1:8092/websocket/1
ws://127.0.0.1.132:8092/websocket/2
ws://127.0.0.1.132:8092/websocket/3
1.往客户端发消息
127.0.0.1:8092/webSocket/sentMessage?userId=1&message=请进入视频会议
127.0.0.1:8092/webSocket/sentMessage?userId=2&message=请进入视频会议
127.0.0.1:8092/webSocket/sentMessage?userId=3&message=请进入视频会议
2.客户端收到消息回复“好的”
文章浏览阅读1.5k次。转:https://blog.csdn.net/tnaig/article/details/81139887 https://www.cnblogs.com/huadongw/p/6516637.html 同时附上了评论,感谢精彩评论!!!Ubuntu安装时出现“failed to load ldlinux.c32”U盘启动总是..._vmware failed to load ldlinux.c32
文章浏览阅读1.3k次。----------------------------程序开发篇-------------------------- 0001 linux下调试core文件(bjchenxu) gdb :出错产生core dump的可执行程序。 : core dump的文件名,缺省是“core” 0002 gcc abc.c得到的a.out不能运行(bjchenxu) ./a.out 0003 c++ 编译
文章浏览阅读849次。 _考题火车行驶中遇到玩耍的孩童
文章浏览阅读2.2w次,点赞4次,收藏15次。C++中fopen函数是没有创建文件夹功能的,也就是说如果".\\1\\2\\3\\"这个目录不存在,那么下面的代码是运行报错的。char *fileName=".\\1\\2\\3\\a.txt";FILE *ftest=fopen(fileName,"w");fprintf(ftest,"test\naldf\naldkf\m\n");fclose(ftest);要预防_::access 和 ::mkdir
文章浏览阅读5k次,点赞7次,收藏23次。文章目录XGB 训练的时候添加自定义eval_metric:f1、准确率,并对样本、特征加权训练随机搜索XGB 训练的时候添加自定义eval_metric:f1、准确率,并对样本、特征加权训练以下demo重点说明:eval_metric:用以训练的时候评估,必须要指定验证集,本博文分享自定义准确率、f1verbose:训练的时候对验证集评估是否打印,True和1等价,比如verbose=10,就会打印n_estimators // 10次feature_weights:特征加权训练,给一个sof_xgb eval_metric
文章浏览阅读320次。function H = fhog( I, binSize, nOrients, clip, crop )% Efficiently compute Felzenszwalb's HOG (FHOG) features.%% A fast implementation of the HOG variant used by Felzenszwalb et al.% in their work on ..._fhog matlab
文章浏览阅读1.4w次,点赞5次,收藏34次。键盘允许用户在程序运行时输入数据。本篇文章主要介绍如何将一个带有十二个按键的键盘连接到Arduino开发板以及如何使用库Keypad.h。通常需要键盘来为Arduino开发板提供输入信号,而薄膜键盘是许多应用的经济型解决方案。它们非常薄,可以轻松安装在任何需要的地方。在本篇文章中,我们将演示如何使用12键数字键盘,类似于电话上的键盘。 12键键盘有三列四行。按下按钮会将其中一个行输出短接到..._arduino矩阵键盘输入
文章浏览阅读2.1k次。本文为Anaconda3-5.2.0 Python3.6.5的tensorflow1.12.0安装教程之前的python -m pip --upgradeThenpip install tensorflow-cpu==2.3.0 -i https://pypi.douban.com/simple/自动升级为新版本,没有意义解决方案sudo apt install python3-pip......_ubuntu anaconda3 tensorflow-cpu == 2.3.0
文章浏览阅读6.9k次。Documentcontrols="controls" 是控制按钮autoplay="autoplay" 是自动播放-->_
文章浏览阅读8.4k次,点赞15次,收藏141次。串口通信(Serial Communications)是指外设与计算机间,通过数据线按位进行传输数据的一种通讯方式。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。虽然串口通信传输速度不高,但是程序简单,能实现远距离通信且远距离通信成本较低,通信长度可达1200米。常用的仪器仪表大多都支持串口通信协议。LabVIEW的自带函数库中有现成的串口通信模块,方便快速搭建堪比串口调试助手的软件。今天分享一个STM32F103C8T6工控板与LabVIEW的串口通_labview控制stm32的运行
文章浏览阅读2.1w次,点赞43次,收藏15次。离职了,写点什么吧~_大队长离职该写点什么好
文章浏览阅读9.6k次。关于音频格式有好多种,如常见的mp3压缩格式的音频;PCM音频文件,就是我们从计算机中采集出来未压缩的声音数据;我们可以直接通过写文件的方式不断的将数据写到一个文件中,后缀为.pcm,这个文件就可以通过pcm播放器播放了。pcm没有文件头,全部就是采集的未压缩的音频数据。关于音频有自己的格式,在windows中用WAVEFORMATEX结构体表示。 PCM播放器:Music_pcm测试文件下载