当前位置: 首页> 汽车> 报价 > 金华百度seo_网络安全厂家排名_2024年最新时事新闻_短信营销

金华百度seo_网络安全厂家排名_2024年最新时事新闻_短信营销

时间:2025/7/11 7:59:16来源:https://blog.csdn.net/fyh2944163240/article/details/142792514 浏览次数: 0次
金华百度seo_网络安全厂家排名_2024年最新时事新闻_短信营销

55 WebSocket

参考资料

  1. WebSocket

  2. SpringBoot使用WebSocket

  3. SpringBoot 集成WebSocket详解

前言

  • WebSocket是HTML5下一种新的协议(websocket协议本质上是一个基于tcp的协议)
  • 它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的
  • WebSocket是一个持久化的协议
  • WebSocket约定了一个通信的规范,通过一个握手的机制,客户端和服务器之间能建立一个类似tcp的连接,从而方便它们之间的通信
  • 在WebSocket出现之前,web交互一般是基于http协议的短连接或者长连接
  • WebSocket是一种全新的协议,不属于HTTP无状态协议,协议名为"ws"

WebSocket和HTTP在这里插入图片描述

相同点

  • 都是基于TCP协议即都是可靠性传输协议;
  • 都是应用层协议;

不同点

  1. WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息
  2. HTTP是单向的
  3. WebSocket是需要浏览器和服务器握手进行建立连接的
  4. 而HTTP是浏览器发起向服务器的连接,服务器预先并不知道这个连接

WebSocket在建立握手时,数据是通过HTTP传输的。但是建立之后,在真正传输时候是不需要HTTP协议的

总结(总体过程):

  • 首先,客户端发起http请求,经过3次握手后,建立起TCP连接;http请求里存放WebSocket支持的版本号等信息,如:Upgrade、Connection、WebSocket-Version等;
  • 然后,服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据;
  • 最后,客户端收到连接成功的消息后,开始借助于TCP传输信道进行全双工通信。

WebSocket解决的问题

HTTP存在的问题

如果是基于HTTP协议,在我们开发的时候,如果后端服务器想要给客户端发送信息是无法实现的,我们只能等客户端发送请求后才能把该信息发送给客户端。

这是因为HTTP协议是无状态的,每当一次会话完成后,服务端都不知道下一次的客户端是谁,需要每次知道对方是谁,才进行相应的响应,因此本身对于实时通讯就是一种极大的障碍。

http协议采用一次请求,一次响应,每次请求和响应就携带有大量的header头,对于实时通讯来说,解析请求头也是需要一定的时间,因此,效率也更低下。

最重要的是,需要客户端主动发,服务端被动发,也就是一次请求,一次响应,不能实现主动发送。

long poll(长轮询)

HTTP为了解决这个问题提出来一种技术,那就是long poll(长轮询),基于HTTP的特性,简单点说,就是客户端发起长轮询,如果服务端的数据没有发生变更,会 hold 住请求,直到服务端的数据发生变化,或者等待一定时间超时才会返回。返回后,客户端又会立即再次发起下一次长轮询。

很显然这样会占用很多的资源,如果用户量很大,很多请求都在等待,这需要很大的并发量。

Ajax轮询

同样也是为了解决这个问题,提出了Ajax轮询,即让前端页面每隔一段时间就向后端发送一个请求。

  • 总的来看,Ajax轮询存在的问题:
  1. 推送延迟。
  2. 服务端压力。配置一般不会发生变化,频繁的轮询会给服务端造成很大的压力。
  3. 推送延迟和服务端压力无法中和。降低轮询的间隔,延迟降低,压力增加;增加轮询的间隔,压力降低,延迟增高
websocket的改进

一旦WebSocket连接建立后,后续数据都以帧序列的形式传输。在客户端断开WebSocket连接或Server端中断连接前,不需要客户端和服务端重新发起连接请求。在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实现了“真·长链接”,实时性优势明显。

WebSocket建立连接是通过TCP的三次握手,其接下来的通信也是通过升级HTTP来达到效果的。

在这里插入图片描述

连接建立过程

WebSocket协议的建立连接过程如下:

  • 客户端发送一个HTTP请求到服务器,请求中包括希望升级为WebSocket协议的信息,即在请求头中包含Upgrade字段,值为"websocket";
  • 服务器收到请求后,会返回一个HTTP 101状态码,表示同意升级为WebSocket协议,同时在响应头中添加Upgrade字段和Connection字段,告知客户端已升级为WebSocket协议;
  • 客户端收到服务器的响应后,会通过TCP通道进行传输通信。

在SpringBoot中实现WebSocket实时通信

为了加深对WebSocket的了解以及它的使用,为此用一个简单的小例子来使用WebSocket,前端基于vue2,后端基于SpringBoot、WebSocket来简单的做一个好友聊天的功能页面。

具体页面情况如下:

在这里插入图片描述

WebSocket

下面就一步一步来介绍其实现。

WebSocket依赖注入问题

在 Spring Boot WebSocket 中,依赖注入可能会出现问题,尤其是在 WebSocket 端点类中使用 @Autowired 注解时。原因主要与 WebSocket 的生命周期管理和 Spring 的管理机制之间的差异有关。

WebSocket 实例由 Web 容器管理而非 Spring 容器。

解决方案:

package com.fang.screw.chat.config;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;import javax.websocket.server.ServerEndpointConfig;/*** @FileName CustomSpringConfigurator* @Description 这段代码的主要作用是将Spring框架与WebSocket集成,使得WebSocket端点能够通过Spring的依赖注入机制获取Spring管理的Bean。* 具体来说,它通过实现ServerEndpointConfig.Configurator接口和ApplicationContextAware接口,将Spring的ApplicationContext* 注入到WebSocket配置中,从而允许WebSocket端点使用Spring的依赖注入功能。* @Author yaoHui* @date 2024-10-08**/
public class CustomSpringConfigurator extends ServerEndpointConfig.Configurator implements ApplicationContextAware {private static volatile BeanFactory context;@Overridepublic  <T> T getEndpointInstance(Class<T> clazz) {return context.getBean(clazz);}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {CustomSpringConfigurator.context = applicationContext;}}
@Component
@Slf4j
@ServerEndpoint("/websocket/{myUserId}")
public class WebSocket {private static MessageMapper messageMapper;@Autowiredpublic void setMessageMapper(MessageMapper messageMapper){WebSocket.messageMapper = messageMapper;}}

前端

更详细的代码请点击查看
点击查看

<template><div id="app"><div class="main1">.</div><div class="main"><div class="contact"><div class="top"><div class="left"><img class="avatar" src="" alt="" /></div><div class="right">{{ user.username }}</div><!-- 添加好友按钮 --><button @click="showModal = true" class="add-friend-button">Add Friend</button><!-- 弹出的小页面(模态框) --><div v-if="showModal" class="modal"><div class="modal-content"><div class="modal-header"><h2>Add a Friend</h2><span @click="showModal = false" class="close">&times;</span></div><!-- 搜索框 --><div class="search-section"><input v-model="searchQuery" type="text" placeholder="Search by username or phone number"class="search-input" @keyup.enter="searchFriends" /></div><!-- 搜索结果展示 --><div class="friend-list"><div v-for="friend in filteredFriends" :key="friend.id" class="friend-item"><div class="friend-avatar"><img :src="friend.avatar" :alt="friend.username" /></div><div class="friend-info"><div class="friend-name">{{ friend.username }}</div><button @click="addFriend(friend)" class="add-button">Add</button></div></div></div></div></div></div><div v-if="friends.length" class="bottom"><div v-for="(friend, i) in friends" class="friend" :class="{activeColor: isActive(i)}"@click="selectFriend(friend)"><div class="left"><img class="avatar" src="" alt="" /></div><div class="right">{{ friend.userBName }}</div></div></div><div v-else class="info"><div class="msg">还没有好友~~~</div></div></div><div v-if="selectedFriend" class="dialog"><div class="top1"><div class="name">{{ selectedFriend.userBName }}</div></div><div class="middle" @mouseover="over" @mouseout="out"><div v-if="msgList.length"><div v-for="msg in msgList"><div class="msg":style="msg.sendUser === selectedFriend.userB ? 'flex-direction: row;' : 'flex-direction: row-reverse;'"><div class="avatar"><img alt="" src="" /></div><div v-if="msg.sendUser === selectedFriend.userB" style="flex: 13;"><div class="bubble-msg-left" style="margin-right: 75px;">{{ msg.message }}</div></div><div v-else style="flex: 13;"><div class="bubble-msg-right" style="margin-left: 75px;">{{ msg.message }}</div></div></div></div></div></div><div class="line"></div><div class="bottom"><label><textarea class="messageText" maxlength="256" v-model="msg" :placeholder="hint"@keydown.enter="sendMsg($event)"></textarea></label><button class="send" :class="{emptyText: isEmptyText}" title="按下 ENTER 发送" @click="sendMsg()">发送</button></div></div><div v-else class="info"><div class="msg">找个好友聊天吧~~~</div></div></div></div></template><script>export default {data () {return {showModal: false,   // 控制模态框的显示与隐藏searchQuery: '',    // 搜索框的输入内容msg: '',friends: [],selectedFriend: null,chatHistory: [],newMessage: '',websocket: null,hint: '',bubbleMsg: '',interval: null,isEmptyText: true,msgList: [],filteredFriends: [] // 搜索到的好友};},computed: {user () {return JSON.parse(localStorage.getItem('currentUser'))}},watch: {msgList () {const mid = document.querySelector('.middle')this.$nextTick(() => {mid && (mid.scrollTop = mid.scrollHeight)document.querySelector('.messageText').focus()})},msg () {this.isEmptyText = !this.msg}},methods: {searchFriends () {// 模拟通过用户名或电话号码搜索好友this.$http.get(this.$constant.baseURL + "/upm/user/getUserByUserNameOrEmail/" + this.searchQuery).then((res) => {if (!this.$common.isEmpty(res.data)) {this.filteredFriends = res.data;}}).catch((error) => {this.$message({message: error.message,type: "error"});});},addFriend (friend) {alert(`Friend request sent to ${friend.username}`);this.$http.post(this.$constant.baseURL + "/chat/addFriend", {"id": friend.id}).then((res) => {alert("添加成功");}).catch((error) => {this.$message({message: error.message,type: "error"});});// 执行添加好友的逻辑},over () {this.setColor('#c9c7c7')},out () {this.setColor('#0000')},setColor (color) {document.documentElement.style.setProperty('--scroll-color', `${color}`)},getFriends () {this.$http.get(this.$constant.baseURL + "/chat/getFriendsListById").then((res) => {if (!this.$common.isEmpty(res.data)) {this.friends = res.data}}).catch((error) => {this.$message({message: error.message,type: "error"});});},selectFriend (friend) {this.selectedFriend = friend;this.loadChatHistory(friend);},loadChatHistory (friend) {this.$http.post(this.$constant.baseURL + "/chat/message/getMessage", friend).then((res) => {if (!this.$common.isEmpty(res.data)) {this.msgList = res.data;}}).catch((error) => {this.$message({message: error.message,type: "error"});});},sendMsg (e) {if (e) {e.preventDefault()}if (!this.msg) {this.hint = '信息不可为空!'return}let entity = {sendUser: JSON.parse(localStorage.getItem('currentUser')).id,receiveUser: this.selectedFriend.userB,message: this.msg.trim(),// time: new Date()}this.websocket.send(JSON.stringify(entity))var ha = JSON.stringify(entity);this.msgList.push(entity);this.msg = ''this.hint = ''},sendMessage () {if (this.newMessage.trim() === '') return;const message = { content: this.newMessage, isMine: true };this.chatHistory.push(message);let entity = {sendUser: JSON.parse(localStorage.getItem('currentUser')).id,receiveUser: this.selectedFriend.userB,message: this.newMessage.trim(),// time: new Date()}this.websocket.send(JSON.stringify(entity)); // Send message through WebSocketthis.newMessage = '';},setupWebSocket () {this.websocket = new WebSocket(`ws://localhost:56281/websocket/${JSON.parse(localStorage.getItem('currentUser')).id}`);this.websocket.onmessage = (event) => {const receivedMessage = JSON.parse(event.data);this.msgList.push(receivedMessage);};},setContact (index) {this.active = indexdelete this.friendList[index].passwordthis.$emit('set-contact', this.friendList[index])},isActive (index) {return this.active === index}},mounted () {this.setupWebSocket();this.getFriends();},};
</script><style scoped>.contact {width: 360px;height: 100%;float: left;border-right: #d0d0d0 1px solid;}.top {width: 100%;height: 80px;display: flex;align-items: center;border-bottom: #e0dfdf 1px solid;}.activeColor {background-color: #c9cbcb;}.top .left {flex: 1;text-align: center;}.avatar {width: 48px;height: 48px;border-radius: 4px;}.top .right {flex: 3;color: black;/* 设置字体颜色为黑色 */}.friend {width: 360px;height: 60px;line-height: 60px;display: flex;align-items: center;border-bottom: #faf7f7 1px solid;}.friend .left {flex: 1;margin-top: 24px;text-align: center;}.friend .right {flex: 3;color: #575454;font-size: 14px;color: black;/* 设置字体颜色为黑色 */}.friend .avatar {width: 36px;height: 36px;}.info {margin-top: 230px;}.info .msg {text-align: center;}#app {width: 100%;height: 100%;background-size: cover;background-image: url("../assets/img/chat-bg.jpg");}.main {width: 1080px;height: 648px;margin-top: 72px;margin-left: auto;margin-right: auto;border-radius: 5px;background-color: #efeded;border: #d0d0d0 1px solid;box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);/* 新增的 Flexbox 样式 */display: flex;/* 启用 Flexbox 布局 */flex-direction: row;/* 水平方向排列子元素 */justify-content: space-between;/* 子元素间的距离自动分配,或根据需要调整 */}:root {--scroll-color: #0000;}.dialog {width: 719px;height: 100%;float: right;}.name {position: relative;top: 22px;left: 25px;}.info {width: 719px;height: 100%;display: flex;align-items: center;}.info .msg {flex: 1;text-align: center;}.top1 {width: 100%;height: 60px;border-bottom: #d0d0d0 1px solid;}.top1::after {content: " ";float: right;position: relative;top: 40px;border: 4px solid #0000;border-top-color: #8e9292;}.middle {height: 432px;overflow: auto;padding: 10px;margin: 6px 0 11px 0;}.middle::-webkit-scrollbar {width: 8px;height: 1px;}.middle::-webkit-scrollbar-thumb {border-radius: 8px;background-color: var(--scroll-color);}.middle::-webkit-scrollbar-track {background: #efeded;border-radius: 4px;}.middle .msg {display: flex;}.avatar {margin: 8px;flex: 1;}.avatar img {width: 36px;height: 36px;border-radius: 4px;}.bubble-msg-left,.bubble-msg-right {padding: 10px;font-size: 14px;margin-top: 10px;line-height: 24px;border-radius: 5px;width: fit-content;line-break: anywhere;}.bubble-msg-left {float: left;color: black;margin-left: -12px;text-indent: -0.5em;background-color: white;}.bubble-msg-right {float: right;color: white;background-color: #1e6ee1;}.bubble-msg-right::before {content: " ";float: right;position: relative;left: 18px;border: 4px solid #0000;border-left-color: #1e6ee1;}.bubble-msg-left::before {content: " ";float: left;position: relative;left: -18px;border: 4px solid #0000;border-right-color: white;}.line {width: 100%;height: 0;position: relative;top: -6px;border-top: #d0d0d0 1px solid;}.dialog .bottom {padding-left: 10px;padding-right: 25px;}.messageText {position: relative;margin-right: 2px;font: 14px/1.5 Helvetica, Arial, Tahoma, 微软雅黑;width: 100%;height: 106px;outline: none;background: #efeded;border: 0 none;overflow-y: auto;-webkit-box-sizing: border-box;box-sizing: border-box;resize: none;vertical-align: middle;display: inline-block;}.dialog .bottom::after {content: " ";float: right;position: relative;top: -121px;left: 75px;border: 4px solid #0000;border-bottom-color: #8e9292;}.send {float: right;position: relative;top: -20px;left: 10px;background-color: #51a5e6;border: #87ceeb;color: #fff;font-size: 12px;width: 50px;height: 22px;border-radius: 3px;}.send:focus {outline: none;}.emptyText {background-color: #d0d0d0;}.name {color: black;/* 设置字体颜色为黑色 */}/* 主体按钮样式 */.add-friend-button {padding: 10px 20px;background-color: #007bff;color: white;font-size: 18px;border: none;border-radius: 20px;cursor: pointer;transition: background-color 0.3s;}.add-friend-button:hover {background-color: #0056b3;}/* 模态框背景 */.modal {position: fixed;top: 0;left: 0;width: 100%;height: 100%;background-color: rgba(0, 0, 0, 0.5);display: flex;justify-content: center;align-items: center;}/* 模态框内容 */.modal-content {background-color: #fff;padding: 20px;border-radius: 15px;width: 400px;max-width: 100%;box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.2);}/* 模态框头部 */.modal-header {display: flex;justify-content: space-between;align-items: center;margin-bottom: 20px;}.modal-header h2 {margin: 0;font-size: 24px;font-family: 'KaiTi', serif;}.close {cursor: pointer;font-size: 24px;}/* 搜索框样式 */.search-input {width: 100%;padding: 10px;border: 2px solid #ccc;border-radius: 10px;font-size: 18px;margin-bottom: 20px;}/* 好友列表样式 */.friend-list {display: flex;flex-direction: column;}/* 单个好友项 */.friend-item {display: flex;align-items: center;margin-bottom: 15px;padding: 10px;border: 1px solid #ccc;border-radius: 10px;}.friend-avatar img {width: 50px;height: 50px;border-radius: 50%;margin-right: 15px;}.friend-info {display: flex;justify-content: space-between;align-items: center;width: 100%;}.friend-name {font-size: 18px;font-family: 'KaiTi', serif;}/* 添加好友按钮 */.add-button {padding: 8px 15px;background-color: #28a745;color: white;border: none;border-radius: 10px;cursor: pointer;font-size: 16px;transition: background-color 0.3s;}.add-button:hover {background-color: #218838;}
</style>

后端

更详细的代码请点击查看
点击

@Component
@Slf4j
@ServerEndpoint("/websocket/{myUserId}")
public class WebSocket {/*** 与客户端的连接会话,需要通过他来给客户端发消息*/private Session session;/*** 当前用户ID*/private Integer userId;/***  concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*  虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。*/private static final CopyOnWriteArraySet<WebSocket> webSockets =new CopyOnWriteArraySet<>();/***用来存在线连接用户信息*/private static final ConcurrentHashMap<Integer,Session> sessionPool = new ConcurrentHashMap<Integer,Session>();private static MessageMapper messageMapper;@Autowiredpublic void setMessageMapper(MessageMapper messageMapper){WebSocket.messageMapper = messageMapper;}/*** 连接成功方法* @param session 连接会话* @param userId 用户编号*/@OnOpenpublic void onOpen(Session session , @PathParam("myUserId") Integer userId){try {this.session = session;this.userId = userId;webSockets.add(this);sessionPool.put(userId, session);log.info("【websocket消息】 用户:" + userId + " 加入连接...");} catch (Exception e) {log.error("---------------WebSocket连接异常---------------");}}/*** 关闭连接*/@OnClosepublic void onClose(){try {webSockets.remove(this);sessionPool.remove(this.userId);log.info("【websocket消息】 用户:"+ this.userId + " 断开连接...");} catch (Exception e) {log.error("---------------WebSocket断开异常---------------");}}@OnMessagepublic void onMessage(String body){try {//将Body解析MessageEntityDecode messageEntityDecode = new MessageEntityDecode();MessageVO messageVO = messageEntityDecode.decode(body);log.info(messageVO.toString());sendOneMessage(messageVO.getReceiveUser(),messageVO.getMessage());MessagePO messagePO = messageVO.transformPO();messageMapper.insert(messagePO);//            }} catch (Exception e) {log.error("---------------WebSocket消息异常---------------");e.printStackTrace();}}/*** 单点消息* @param userId* @param message*/public void sendOneMessage(Integer userId, String message) {Session session = sessionPool.get(userId);if (session != null&&session.isOpen()) {try {log.info("【websocket消息】 单点消息:"+message);MessageVO messageVO = new MessageVO();messageVO.setMessage(message);messageVO.setSendUser(this.userId);messageVO.setReceiveUser(userId);MessageEntityEncode messageEntityEncode = new MessageEntityEncode();session.getAsyncRemote().sendText(messageEntityEncode.encode(messageVO));} catch (Exception e) {log.error("---------------WebSocket单点消息发送异常---------------");}}}
}
关键字:金华百度seo_网络安全厂家排名_2024年最新时事新闻_短信营销

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: