- 2024-12-06 11:17:01
- 7294 热度
- 0 评论
开头还是说点废话吧。WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
从GitEE上搞了一个项目下来,然后精简和调整,尽量把代码调整到一看就懂。
项目就是普通的SpringBoot,没有用的东西全部移除,只需要一张表,当然也可以不用登陆,这样更精简。
核心代码就是ServerEndpoint的处理类:
package boot.spring.service; import java.io.IOException; import java.util.Date; import java.util.concurrent.ConcurrentHashMap; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import org.springframework.stereotype.Component; import com.alibaba.fastjson.JSON; import boot.spring.po.Message; @ServerEndpoint("/webSocket/{username}") @Component public class WebSocketServer { // concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。 private static ConcurrentHashMap<String, Session> sessionPools = new ConcurrentHashMap<>(); // 建立连接成功调用 @OnOpen public void onOpen(Session session, @PathParam(value = "username") String userName) { // 链接建立,存储链接对象 sessionPools.put(userName, session); // 广播上线消息 Message msg = new Message(); msg.setFrom("系统消息"); msg.setDate(new Date()); msg.setTo("0"); msg.setText(userName); broadcast(JSON.toJSONString(msg, true)); } // 关闭连接时调用 @OnClose public void onClose(@PathParam(value = "username") String userName) { sessionPools.remove(userName); // 广播下线消息 Message msg = new Message(); msg.setFrom("系统消息"); msg.setDate(new Date()); msg.setTo("-2"); msg.setText(userName); broadcast(JSON.toJSONString(msg, true)); } // 收到客户端信息后,根据接收人的username把消息推下去或者群发 // to=-1群发消息 @OnMessage public void onMessage(String message) throws IOException { Message msg = JSON.parseObject(message, Message.class); msg.setDate(new Date()); if (msg.getTo().equals("-1")) { broadcast(JSON.toJSONString(msg, true)); // -1群发 } else { sendInfo(msg.getTo(), JSON.toJSONString(msg, true)); } } // 错误时调用 @OnError public void onError(Session session, Throwable throwable) { throwable.printStackTrace(); } // 给指定用户发送信息 public void sendInfo(String userName, String message) { Session session = sessionPools.get(userName); try { sendMessage(session, message); } catch (Exception e) { e.printStackTrace(); } } // 群发消息 public void broadcast(String message) { for (Session session : sessionPools.values()) { try { sendMessage(session, message); } catch (Exception e) { e.printStackTrace(); continue; } } } // 发送消息 public void sendMessage(Session session, String message) throws IOException { if (session != null) { synchronized (session) { session.getBasicRemote().sendText(message); } } } public static ConcurrentHashMap<String, Session> getSessionPools() { return sessionPools; } }
聊天室页面代码:
<!DOCTYPE> <html> <head> <title>聊天室</title> <link rel="stylesheet" href="./css/bootstrap.min.css" /> <script src="./js/jquery-1.12.3.min.js"></script> <script src="./js/bootstrap.min.js"></script> <style> body { margin-top: 5px; } </style> </head> <body> <div class="container"> <div class="row"> <div class="col-md-3"> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">当前登录用户</h3> </div> <div class="panel-body"> <div class="list-group"> <a href="#" class="list-group-item">你好,<span id="user"></span></a> <a href="logout" class="list-group-item">退出</a> </div> </div> </div> <div class="panel panel-primary" id="online"> <div class="panel-heading"> <h3 class="panel-title">其他在线用户</h3> </div> <div class="panel-body"> <div class="list-group" id="users"></div> </div> </div> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">消息发送</h3> </div> <div class="panel-body"> <input type="text" class="form-control" id="msg" /><br> <button id="broadcast" type="button" class="btn btn-primary">发送</button> <button id="send" type="button" class="btn btn-primary">私聊发送</button> </div> </div> <div class="col-md-9"> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title" id="talktitle"></h3> </div> <div class="panel-body"> <div class="well" id="log-container" style="height:400px;overflow-y:scroll"></div> </div> </div> </div> </div> </div> </div> <script> var username; var uid; $(document).ready(function() { // 指定websocket路径 var websocket; $.get("/currentuser", function(data) { username = data.username; uid = data.uid; $("#user").html(username); if ('WebSocket' in window) { // 浏览器支持 WebSocket websocket = new WebSocket("ws://localhost:8080/webSocket/" + username); // 打开一个 web socket } websocket.onmessage = function(event) { var data = JSON.parse(event.data); if (data.to == 0) { //上线消息 if (data.text != username) { $("#users").append('<a href="#" onclick="talk(this)" class="list-group-item">' + data.text + '</a>'); $("#log-container").append("<div class='bg-info'><label class='text-danger'>" + data.from + " " + data.date + "</label><div class='text-success'>" + data.text + "上线了" + "</div></div><br>"); scrollToBottom(); } } else if (data.to == -2) { //下线消息 if (data.text != username) { $("#users > a").remove(":contains('" + data.text + "')"); $("#log-container").append("<div class='bg-info'><label class='text-danger'>" + data.from + " " + data.date + "</label><div class='text-success'>" + data.text + "下线了" + "</div></div><br>"); scrollToBottom(); } } else { // 普通消息 $("#log-container").append("<div class='bg-info'><label class='text-danger'>" + data.from + " " + data.date + "</label><div class='text-success'>" + data.text + "</div></div><br>"); scrollToBottom(); } }; // 加载当前用户 $.post("/onlineusers?currentuser=" + username, function(data) { for (var i = 0; i < data.length; i++) { $("#users").append('<a href="#" onclick="talk(this)" class="list-group-item">' + data[i] + '</a>'); } }); }); // 发送 $("#broadcast").click(function() { var data = {}; data["from"] = username; data["to"] = -1; data["text"] = $("#msg").val(); websocket.send(JSON.stringify(data)); // 使用 send() 方法发送数据 $("#msg").val(""); }); // 私聊发送 $("#send").click(function() { if ($("body").data("to") == undefined) { alert("请选择聊天对象"); return false; } var data = {}; data["from"] = username; data["to"] = $("body").data("to"); data["text"] = $("#msg").val(); websocket.send(JSON.stringify(data)); // 使用 send() 方法发送数据 // 单独发送给某人的,不会广播,所以自己这里要手动加上 $("#log-container").append("<div class='bg-success'><label class='text-info'>我 " + new Date().format("yyyy-MM-dd hh:mm:ss") + "</label><div class='text-info'>" + $("#myinfo").val() + "</div></div><br>"); scrollToBottom(); $("#msg").val(""); }); }); function talk(a) { $("#talktitle").text("与" + a.innerHTML + "的聊天"); $("body").data("to", a.innerHTML); } // 滚动条滚动到最低部 function scrollToBottom() { var div = document.getElementById('log-container'); div.scrollTop = div.scrollHeight; } Date.prototype.format = function(fmt) { var o = { "M+" : this.getMonth() + 1, //月份 "d+" : this.getDate(), //日 "h+" : this.getHours(), //小时 "m+" : this.getMinutes(), //分 "s+" : this.getSeconds(), //秒 "q+" : Math.floor((this.getMonth() + 3) / 3), //季度 "S" : this.getMilliseconds() //毫秒 }; if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); } for (var k in o) { if (new RegExp("(" + k + ")").test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); } } return fmt; } </script> </body> </html>
登陆效果图如下,用aa,bb,cc登陆:
三个人依次进入聊天室后效果如下,可以群发消息和点击登陆的用户私聊
私聊时效果如下
代码下载:
END
0 评论
留下评论
热门标签
- Spring(403)
- Boot(208)
- Spring Boot(187)
- Spring Cloud(82)
- Java(82)
- Cloud(82)
- Security(60)
- Spring Security(54)
- Boot2(51)
- Spring Boot2(51)
- Redis(31)
- SQL(29)
- Mysql(25)
- IDE(24)
- Dalston(24)
- MVC(22)
- JDBC(22)
- IDEA(22)
- mongoDB(22)
- Web(21)
- CLI(20)
- SpringMVC(19)
- Alibaba(19)
- Docker(17)
- SpringBoot(17)
- Git(16)
- Eclipse(16)
- Vue(16)
- ORA(15)
- JPA(15)
- Apache(15)
- Linux(14)
- HTTP(14)
- Mybatis(14)
- Oracle(14)
- jdk(14)
- Tomcat(14)
- Pro(13)
- XML(13)
- JdbcTemplate(13)
- OAuth(13)
- Nacos(13)
- Data(12)
- JSON(12)
- OAuth2(12)
- Myeclipse(11)
- stream(11)
- int(11)
- not(10)
- Bug(10)
- ast(9)
- maven(9)
- Map(9)
- Hystrix(9)
- Swagger(8)
- APP(8)
- Bit(8)
- API(8)
- session(8)
- Window(8)
- HTML(7)
- Github(7)
- JavaMail(7)
- Cache(7)
- File(7)
- mail(7)
- IntelliJ(7)
- windows(7)
- too(7)
- RabbitMQ(6)
- and(6)
- star(6)
- Excel(6)
- Log4J(6)
- pushlet(6)
- apt(6)
- read(6)
- Freemarker(6)
- WebFlux(6)
- JSP(6)
- Bean(6)
- error(6)
- nginx(6)
- Server(6)
- jar(6)
- ueditor(6)
- ehcache(6)
- UDP(6)
- JWT(5)
- rdquo(5)
- PHP(5)
- Struts(5)
- string(5)
- Syntaxhighlighter(5)
- script(5)
- Tool(5)
- Controller(5)
- swagger2(5)
- ldquo(5)
- input(5)