湖南网站制作公司推荐,电商怎么自学,建筑网站网页设计,交网站建设域名计入什么科目目录 一、项目实现内容二、websocket三、实现过程java后端vue前端源代码 WebSocketServer调用spring容器注意事项扩展 一、项目实现内容
http://localhost:8080/websocket?uid1 http://localhost:8080/websocket?uid2 http://localhost:8080/websocket?uid3 二、websocket … 目录 一、项目实现内容二、websocket三、实现过程java后端vue前端源代码 WebSocketServer调用spring容器注意事项扩展 一、项目实现内容
http://localhost:8080/websocket?uid1 http://localhost:8080/websocket?uid2 http://localhost:8080/websocket?uid3 二、websocket
websocket api介绍 再看这里这个是我看介绍比较好的websocket使用 websocket方法定义 WebSocket.onclose 用于指定连接关闭后的回调函数。 WebSocket.onerror 用于指定连接失败后的回调函数。 WebSocket.onmessage 用于指定当从服务器接受到信息时的回调函数。 WebSocket.onopen 用于指定连接成功后的回调函数。 先是定义websocket的处理逻辑 消息流转过程
三、实现过程 前提这只是一个小demo没用到数据库只是简单的在后端直接返回准备好的用户但是逻辑是没有问题的只要你的用户信息换成查数据库和将发到服务器的消息数据保存一份到数据库就行了。CURD比较简单逻辑明白就行 java后端
Component注册到spring容器交由spring控制ServerEndpoint(/path)是和RequestMapping(/path)差不多类似的若是有ws协议的路上path匹配则交由该对象处理主要是将目前的类定义成一个websocket服务器端, 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端WebSocketServer的加载spring容器之前,后面有客户端连接服务器则将WebSocketServer的session、uid替换成客户端对应的存储在Map中记录起来发送消息还得用到对应的session 之后接收到客户端的消息onMessage内可以通过webSocketMap记录的 WebSocketServer使用session.getBasicRemote().sendText(message);发送消息message
Component
ServerEndpoint(/wechat/{uid})
public class WebSocketServer {/*** 记录在线的用户数*/private static AtomicInteger onlineUserNumnew AtomicInteger(0);/*** 存储连接该服务的用户客户端对应的WebSocketServer uid,WebSocketServer*/private static MapInteger,WebSocketServer webSocketMapnew ConcurrentHashMap();/*** 与某个客户端的连接会话需要通过它来给客户端发送数据*/private Session session;/*** 当前连接进行用户的uid*/private int uid;/*** 连接成功后的回调函数* param uid* param session*/OnOpenpublic void onOpen(PathParam(uid)int uid,Session session){//获取当前的session、uidthis.sessionsession;this.uiduid;//存储客户端对应的websocketif (!webSocketMap.containsKey(uid)){//判断这里还应该查一下数据库但是我这里比较潦草就没做//还未连接过webSocketMap.put(uid,this);//在线人数1onlineUserNum.incrementAndGet();}else{//已经连接过记录新的websocketwebSocketMap.replace(uid,this);}System.out.println(用户id:uid建立连接);}/*** 连接失败后的回调函数* param session* param error*/OnErrorpublic void onError(Session session, Throwable error) {System.out.println(用户:this.uid连接失败,原因:error.getMessage());error.printStackTrace();}/*** 前提成功建立连接* 发送过来的消息按如下的机制推送到接收的客户端* param message* param session*/OnMessagepublic void onMessage(String message,Session session){System.out.println(message);if(message.isEmpty()||messagenull){//消息不正常不处理return;}//初始化消息的格式 json-自己定义的消息体Message fromMessage JSON.parseObject(message,Message.class);if(!webSocketMap.containsKey(fromMessage.getToUid())){System.out.println(要接收的用户不在线暂存数据库等该用户上线在获取);return;}//在线则直接推送数据到接收端客户端WebSocketServer webSocketServer webSocketMap.get(fromMessage.getToUid());webSocketServer.sendMessage(message);}/*** 推送消息到客户端* param message*/public void sendMessage(String message) {try {this.session.getBasicRemote().sendText(message);} catch (IOException e) {e.printStackTrace();}}/*** 连接关闭后的回调函数*/OnClosepublic void onClose(){if (webSocketMap.containsKey(uid)){webSocketMap.remove(uid);//在线人数-1onlineUserNum.decrementAndGet();}}}vue前端
下面介绍进入页面的逻辑可以和前面的图多结合理解
localhost:8080/?uidi 获取用户i的信息进入页面因为没登陆注册就这样用于测试获取在线用户除去本身创建WebSocket对象连接服务器此时服务器记录了和客户端连接时的WebSocketServer就可以通过全局的WebSocket对象发送消息了
templatediv classbgel-container classwechatel-aside width35% styleborder-right: 1px solid #fff!-- 自己 --div classitemel-avatar:size46:srcuser.avatarUrlstylefloat: left; margin-left: 2px/el-avatardiv classname{{ user.nickname}}el-tag stylemargin-left: 5px typesuccess本人/el-tag/div/div!-- 在线用户 --divclassitemv-for(item1, index) in userlist:keyitem1.uidclickselectUser(index)!-- 新数消息 --el-badge:valuenew_message_num[index]:max99:hidden!new_message_num[index] 0stylefloat: left; margin-left: 2pxel-avatar :size46 :srcitem1.avatarUrl/el-avatar/el-badgediv classname{{ item1.nickname }}/div/div/el-asideel-mainel-container classwechat_right!-- 右边顶部 --el-header classheader{{anotherUser ! null anotherUser.uid 0? anotherUser.nickname: 未选择聊天对象}}/el-header!-- 聊天内容 --el-main classshowChatdiv v-foritem2 in messageList[index] :keyitem2.msg!-- 对方发的 --div classleftBox v-ifitem2.FromUid anotherUser.uidspan stylefont-size: 4px{{ item2.time }}/span{{ item2.msg }}/divdiv classmyBr v-ifitem2.FromUid anotherUser.uid/div!-- 自己发的 --div classrightBox v-ifitem2.FromUid user.uidspan stylefont-size: 4px{{ item2.time }}/span{{ item2.msg }}/divdiv classmyBr v-ifitem2.FromUid user.uid/div/div/el-main!-- 输入框 --el-main classinputValuetextarea v-modelinputValue idchat cols26 rows5/textarea!-- 发送按钮 --el-buttonv-ifanotherUser ! null anotherUser.uid 0 inputValue ! typesuccesssizeminiroundidsendclicksenMessage发送/el-button/el-main/el-container/el-main/el-container/div
/templatescript
export default {data() {return {//自己user: {},//要私信的人anotherUser: {},//在线的用户userlist: [],//要私信的人在userlist的索引位置index: 0,//消息队列集合 [本人和第一个人之间的消息集合、本人和第二个人之间的消息集合、...]messageList: [],//新消息个数集合new_message_num: [],//将要发送的内容inputValue: ,//websocketwebsocket: null,};},methods: {//获取自己被分配的信息getYourInfo(uid) {let params new URLSearchParams();this.$axios.post(/user/getYourInfo/ uid, params).then((res) {this.user res.data.data;if (res.data.code 200) {//获取在线用户this.getUserList();}}).catch((err) {console.error(err);});},//获取在线用户getUserList() {let params new URLSearchParams();this.$axios.post(/user/getUserList, params).then((res) {this.userlist res.data.data.filter(//去掉自己(user) user.uid ! this.user.uid);//填充消息数据 messagelist:[[]、[]...] 并且将新消息队列置为0for (let i 0; i this.userlist.length; i) {this.messageList.push([]);this.new_message_num.push(0);}//将当前的客户端和服务端进行连接并定义接收到消息的处理逻辑this.init(this.user.uid);}).catch((err) {console.error(err);});},//选择聊天对象selectUser(index) {this.anotherUser this.userlist[index];this.index index;//将新消息置为0this.new_message_num[index] 0;},//将当前的客户端和服务端进行连接并定义接收到消息的处理逻辑init(uid) {var self this;if (typeof WebSocket undefined) {console.log(您的浏览器不支持WebSocket);return;}//清除之前的记录if (this.websocket ! null) {this.websocket.close();this.websocket null;}//-----------------------连接服务器-----------------------let socketUrl ws://localhost:8088/wechat/ this.user.uid;//开启WebSocket 连接this.websocket new WebSocket(socketUrl);//指定连接成功后的回调函数this.websocket.onopen function () {console.log(websocket已打开);};//指定连接失败后的回调函数this.websocket.onerror function () {console.log(websocket发生了错误);};//指定当从服务器接受到信息时的回调函数this.websocket.onmessage function (msg) {//消息体例如{FromUid:1,ToUid:2,msg:你好,time:00:07:03} message对象let data JSON.parse(msg.data);//添加到对应的消息集合中let index data.FromUid uid ? data.FromUid - 2 : data.FromUid - 1;self.messageList[index].push(data);//新消息数1self.new_message_num[index];};//指定连接关闭后的回调函数this.websocket.onclose function () {console.log(websocket已关闭);};},//发送信息senMessage() {//消息体例如{FromUid:1,ToUid:2,msg:你好,time:00:07:03}let message {FromUid: this.user.uid,ToUid: this.anotherUser.uid,msg: this.inputValue,time: new Date().toLocaleTimeString(),};//将消息插进消息队列显示在前端this.messageList[this.index].push(message);//将消息发送至服务器端再转发到对应的用户this.websocket.send(JSON.stringify(message));//清空一下输入框内容this.inputValue ;},},created() {let uid this.$route.query.uid;if (uid ! undefined) {//获取被分配的用户信息this.getYourInfo(uid);}},
};
/scriptstyle
/*改变滚动条 */
::-webkit-scrollbar {width: 3px;border-radius: 4px;
}::-webkit-scrollbar-track {background-color: inherit;-webkit-border-radius: 4px;-moz-border-radius: 4px;border-radius: 4px;
}::-webkit-scrollbar-thumb {background-color: #c3c9cd;-webkit-border-radius: 4px;-moz-border-radius: 4px;border-radius: 4px;
}
.bg {background: url(https://s1.ax1x.com/2022/06/12/Xgr9u6.jpg) no-repeat top;background-size: cover;background-attachment: fixed;width: 100%;height: 100%;position: fixed;top: 0;left: 0;right: 0;bottom: 0;
}
.wechat {width: 60%;height: 88%;margin: 3% auto;border-radius: 20px;background-color: rgba(245, 237, 237, 0.3);
}
/*聊天框左侧 */
.item {position: relative;width: 94%;height: 50px;margin-bottom: 3%;border-bottom: 1px solid #fff;
}
.item .name {line-height: 50px;float: left;margin-left: 10px;
}
/*聊天框右侧 */.wechat_right {position: relative;width: 100%;height: 100%;
}
.header {text-align: left;height: 50px !important;
}
.showChat {width: 100%;height: 65%;
}
.inputValue {position: relative;margin: 0;padding: 0;width: 100%;height: 50%;
}
.inputValue #chat {font-size: 18px;width: 96%;height: 94%;border-radius: 20px;resize: none;background-color: rgba(245, 237, 237, 0.3);
}
#send {position: absolute;bottom: 12%;right: 6%;
}
/*展示区 */
.leftBox {float: left;max-width: 60%;padding: 8px;position: relative;font-size: 18px;border-radius: 12px;background-color: rgba(40, 208, 250, 0.76);
}
.rightBox {float: right;max-width: 60%;padding: 8px;font-size: 18px;border-radius: 12px;position: relative;background-color: rgba(101, 240, 21, 0.945);
}
.myBr {float: left;width: 100%;height: 20px;
}
.leftBox span {left: 3px;width: 120px;position: absolute;top: -16px;
}
.rightBox span {width: 120px;position: absolute;right: 3px;top: -16px;
}
/style源代码
源代码
WebSocketServer调用spring容器注意事项
WebSocket启动的时候优先于spring容器从而导致在WebSocketServer中调用业务Service会报空指针异常 解决方法静态初始化并提前加载bean
静态初始化
//如需要 MessageService
private static MessageService messageService;提前加载bean
Configuration
public class WebSocketConfig {/*** 注入ServerEndpointExporter,该Bean会自动注册使用ServerEndpoint注解声明的websocket endpoint*/Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}//通过get方法注入beanAutowiredprotected void getMessageService(MessageService ms){WebSocketServer.messageServicems;}
}扩展
群发实现多人聊天实现语音、视屏聊天实现
相信前两个大家如果看明白上面的demo应该能做
给消息设置一个状态后端服务器接收到消息是群发之后就将消息发送给所有的在线用户不在线的先存数据库或者维护一个uid数组这样更灵活定义一个群ID,将消息发送至群内的所有人这个我建议自己查查看
使用netty实现了上面一模一样的功能 nettyspringbootvue聊天室需要了解netty