WebSocket实现在线聊天

前两天在公司接到一个需求,使用
WebSocket
实现微信扫码登陆,当时了解了一下WebSocket
,都说WebSocket
可以实现在线聊天,所以我自己也写了一个。(发个帖子证明我还活着😂)
简单介绍下什么是WebSocket
。
我们的都知道,前端向后端发起请求一般都是使用的Http
协议,但是呢Http
协议有一个不好的地方那就是,只能由客户端主动发起请求,服务器收到客户端的请求后才会返回结果数据。这种单向请求的特点,就造就了一个问题,如果服务器端数据发生了变化,客户端就很难获知。所以一般都是使用轮询的方式,不断的向后端发起请求查询是否有数据变化。但是轮询的效率非常低,严重的浪费资源。所以在这种情况下,就出现了WebSocket
协议的请求。WebSocket
协议的最大特点就是,只要客户端和服务器端建立了连接,服务器端可以主动向客户端推送消息,客户端也可以向服务器端发送消息。本文章就是要基于这个特点实现在线聊天。
环境
服务器端
•Java1.8•Maven•SpringBoot
客户端:
•Vue(直接用原生Html配合JavaScript也可以,我使用Vue是为了使用双向绑定)
服务器的搭建
添加依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--WebSocket--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
注入Bean对象
Bean
对象的注入用法就不用介绍了吧,直接在启动类中添加以下代码就好了。此处说明一下为什么要注入此对象,
SpringBoot
的一大特点就是可以使用注解来简化配置,所以在注入这个对象之后,就可以使用WebSocket
的注解。
/*** 用于扫描带有ServerEndpoint注解成为WebSocket* @return ServerEndpointExporter*/@Beanpublic ServerEndpointExporter serverEndpointExporter () {return new ServerEndpointExporter();}
配置Socket
通过这一个类实现Socket请求。
package cn.yanghuisen.websocketdemo.socket;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;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/*** @author Y*/@Slf4j@Component@ServerEndpoint("/socket/chat/{userName}")public class WebSocket {/*** 所有的客户端*/public static Map<String,Session> sessions = new HashMap<>();/*** 建立连接时的回调* @param session session(客户端)* @param userName 用户名*/@OnOpenpublic void onOpen(Session session,@PathParam("userName") String userName) {log.info("{},进入聊天室",userName);if (sessions.containsKey(userName)){session.getAsyncRemote().sendText("用户名已经存在");return;}sessions.put(userName,session);senMessage(String.format("%s:加入群聊",userName));}/*** 断开连接时的回调* @param userName 用户名*/@OnClosepublic void onClose(Session session,@PathParam("userName") String userName) throws IOException {session.close();log.info("{},断开连接",userName);sessions.remove(userName);senMessage(String.format("%s:离开群聊",userName));}/*** 发生错误时的回调* @param session session(客户端)* @param throwable 错误*/@OnErrorpublic void onError(Session session, Throwable throwable,@PathParam("userName") String userName) throws IOException {log.info("{},出现错误", userName);session.close();sessions.remove(userName);}/*** 收到消息时的回调* @param message 消息* @param userName 用户名*/@OnMessagepublic void onMessage(String message,@PathParam("userName") String userName) {log.info("{}:{}",userName,message);senMessage(String.format("%s:%s",userName,message));}/*** 发送给指定客户端* @param message 消息* @param session session(客户端)*/public void senMessage(String message,Session session) {// 发送消息session.getAsyncRemote().sendText(message);}/*** 发送给所有的客户端* @param message 消息*/public void senMessage(String message) {sessions.forEach((k,v) -> {if (v.isOpen()) {// 发送消息v.getAsyncRemote().sendText(message);}});}}
•@OnOpen:此注解使用在方法上,当服务器端和客户端建立起连接时会回调配置了该注解的方法。•@OnClose:此注解使用在方法上,当服务器端和客户端断开连接时会回调配置了该注解的方法。•@OnError:此注解使用在方法上,当服务器端发生了异常时会回调配置了该注解的方法。•@OnMessage:此注解使用在方法上,当服务器端收到客户端发送的消息时会回调配置了该注解的方法。•@ServerEndpoint:类似于@RequestMapping
注解,用于声明该请求是一个WebSocket协议的请求。
客户端的搭建
<template><div id="app"><div class="left"><input type="text" v-model="userName"> <button @click="add">加入</button><br><br><br><textareacols="30"style="height: 50px"v-model="message"></textarea><br><button class="send" @click="sendMessage">发送</button><button class="reset" @click="message = null">重置</button><button class="close" @click="close">断开</button></div><div class="right"><p>{{isOpenMessage}}</p><p v-for="(message,index) in messageList" :key="index"> {{ message }}</p></div></div></template><script>export default {name: 'App',data () {return {userName: null,url: 'ws://localhost:8888/socket/chat/',ws: null,isOpenMessage: '暂未连接到服务器',messageList: [],message: null}},created () {this.isSocket()},methods: {// 加入聊天室add() {// 创建WebSocket连接this.ws = new WebSocket(this.url+this.userName);// 建立连接成功回调this.ws.onopen = () =>{this.isOpenMessage = '服务器连接成功'}// 客户端收到服务器端消息时回调this.ws.onmessage = (message) => {this.messageList.push(message.data)console.log(message.data);}},// 判断是否支持WebSocketisSocket() {if (!('WebSocket' in window)) {alert('该浏览器不支持WebSocket')return false}return true},sendMessage() {this.ws.send(this.message)},close() {this.ws.close()}}}</script><style>.send, .reset, .close {margin: 10px;}.left, .right {width: 300px;float: left}</style>
•new WebSocket(this.url+this.userName):建立客户端和服务器端的连接,建立成功后,前端会回调onopen的方法。•onopen:连接建立成功后回调的方法。•onmessage:客户端收到服务器端推送的消息时回调的方法。•this.ws.send(this.message):服务器端发送消息。•this.ws.close():断开服务器端和客户端的连接。
客户端中有些没有写到比如onerror
和onclose
等,感兴趣的可以自己加上试试。
End
整个下来还是非常简单的,毕竟 我是一个啥也不会的程序员/(ㄒoㄒ)/~~,学的太多掉头发。




