• 引入

    • 普通请求-响应方式:例如Servlet中HttpServletRequest和HttpServletResponse相互配合先接受请求、解析数据,再发出响应,处理完成后连接便断开了,没有数据的实时性可言。
    • Ajax轮询:客户端定时发送多次Ajax请求,服务器不断响应,时间频率极小,虽然实时性有了卓越提高,但是大多数的请求是没有意义的。
    • WebSocket长连接:客户端只需要向服务器发送一次Http请求,与服务器建立一个以sessioId标示的channel,便可以与服务器在自己的管道中实时通讯,连接是不断开的。在此介绍一个基于WebSocket的框架GoEasy,非常的方便简单,大家可以用来实现消息推送、实时聊天等功能。

      

  • Demo介绍

    • 基于WebSocket的聊天室,可以发送接受消息并实时查看在线用户。

      

  • Maven依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.atguigu</groupId>
<artifactId>spring-boot-websocket-02</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-websocket-02</name>
<description>Demo project for Spring Boot</description> <properties>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency> <dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>net.sf.ezmorph</groupId>
<artifactId>ezmorph</artifactId>
<version>1.0.3</version>
</dependency> </dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
  • WebSocket配置文件

    • 后台基于SpringBoot非常的方便,只要编写配置类即可,注入的实体Bean为方法名。SpringBoot推荐Thymeleaf进行html渲染,但是老师说Thymeleaf相对于Jsp等其他渲染工具性能较差,咱也不知道为什么。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.socket.server.standard.ServerEndpointExporter; @Configuration
public class MyWebSocketConfig extends WebMvcConfigurerAdapter { @Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
} @Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
}
  • JavaScript

    • 客户端通过js发送socket请求建立连接,连接成功后建立管道。通过onopen、onclose、onmessage等回调函数接收服务器响应的反馈。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>聊天页面</title>
</head>
<body>
<div class="container">
<div class="left">
<div class="top"></div>
<div class="bottom">
<div class="content">
<input type="text" name="content" id="content" value="输入文本内容">
</div>
<input type="button" value="发送" id="send">
</div>
</div>
<div class="right"></div>
</div>
</body>
</html>
<script th:inline="javascript">
window.onload=function(){
var username=[[${username}]];
if("WebSocket" in window){
var webSocket=new WebSocket("ws://10.7.84.48:8080/mywebsocket?username="+username);
//type 0上线 1下线 2聊天信息 3拉取在线用户
webSocket.onopen=function(e){
var data='{"type":"0","username":"'+username+'","content":""}';
webSocket.send(data);
}
window.onbeforeunload=function(){
var data='{"type":"1","username":"'+username+'","content":""}';
webSocket.send(data);
webSocket.close();
}
document.getElementById("send").onclick=function (ev) {
var content=document.getElementById("content").value;
var data='{"type":"2","username":"'+username+'","content":"'+content+'"}';
webSocket.send(data);
document.getElementsByClassName("top")[0]
.innerHTML+="<p><font color='red'>我:&nbsp;</font><font color='#8a2be2'>"+content+"</font></p>";
document.getElementById("content").value="";
var scrollDiv = document.getElementsByClassName('top')[0];
scrollDiv.scrollTop = scrollDiv.scrollHeight;
}
webSocket.onmessage=function (ev) {
var data=ev.data;
var obj=eval('('+data+')');
var type=obj.type;
switch (type) {
case 0:
document.getElementsByClassName("right")[0]
.innerHTML+="<p id="+obj.senSessionId+"><font color='blue'>"+obj.senName+"</font></p>";
break;
case 1:
var id=obj.senSessionId;
var parent=document.getElementsByClassName("right")[0];
var child=document.getElementById(id);
parent.removeChild(child);
break;
case 2:
document.getElementsByClassName("top")[0]
.innerHTML+="<p>"+obj.time.hours+":"+obj.time.minutes+":"+obj.time.seconds+"&nbsp;<font color='#4169e1'>"+obj.senName+":&nbsp;</font><font color='#8a2be2'>"+obj.content+"</font></p>";
var scrollDiv = document.getElementsByClassName('top')[0];
scrollDiv.scrollTop = scrollDiv.scrollHeight;
break;
case 3:
var map=obj.map;
for(var key in map){
document.getElementsByClassName("right")[0]
.innerHTML+="<p id="+key+"><font color='blue'>"+map[key]+"</font></p>";
}
break;
default:
} }
}
}
</script>
<style>
.container{
width: 700px;
height: 500px;
border: 1px solid black;
margin: 0px auto;
}
.left{
width: 75%;
height: 100%;
float: left;
}
.right{
width: 20%;
height: 100%;
float: left;
border-left: 1px solid black;
}
.top{
width: 100%;
height: 75%;
overflow-y: scroll;
}
.bottom{
width: 100%;
height: 25%;
border-top: 1px solid black;
}
.content{
width: 100%;
height: 65%;
border-bottom: 1px solid black;
}
#content{
width: 99%;
height: 92%;
}
#send{
float: right;
width: 70px;
height: 42px;
}
</style>
  • WebSocket服务端代码

import com.atguigu.springbootwebsocket02.bean.Msg;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;
@Component
@ServerEndpoint("/mywebsocket")
public class WebSocketListener {
private static int onlineCount=0; private static synchronized int getOnlineCount(){
return onlineCount;
}
private static synchronized int addOnlineCount(){
return ++onlineCount;
}
private static synchronized int subOnlineCount(){
return --onlineCount;
}
//用于区分每个WebSocket session.getId()
private Session session;
private String username;
private static CopyOnWriteArraySet<WebSocketListener>webSocketListeners
=new CopyOnWriteArraySet<>();
@OnOpen
public void onOpen(Session session){
this.session=session;
webSocketListeners.add(this);
WebSocketListener.addOnlineCount();
try {
this.username= URLDecoder.decode(session.getQueryString().split("=")[1],"utf8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//获取在线用户信息
try {
Map<String,String>map=new HashMap<>();
for(WebSocketListener webSocketListener
:webSocketListeners){
if(!webSocketListener.session.getId().equals(this.session.getId())){
map.put(webSocketListener.session.getId(),webSocketListener.username);
}
}
JSONObject jm=JSONObject.fromObject(map);
JSONObject jsonObject=new JSONObject();
jsonObject.put("type",3);
jsonObject.put("map",jm);
this.session.getBasicRemote().sendText(jsonObject.toString());
}catch (Exception e){
e.printStackTrace();
}
}
@OnClose
public void onClose(Session session){
webSocketListeners.remove(this);
WebSocketListener.subOnlineCount();
}
private void broadcast(String data){
for(WebSocketListener webSocketListener
:webSocketListeners){
try {
if(!webSocketListener.session.getId().equals(this.session.getId())){
webSocketListener.session.getBasicRemote().sendText(data);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
@OnMessage
public void onMessage(String data,Session session){
JSONObject jsonObject=JSONObject.fromObject(data);
Msg msg=new Msg();
msg.setSenName(jsonObject.getString("username"));
msg.setSenSessionId(session.getId());
msg.setType(Integer.parseInt(jsonObject.getString("type")));
msg.setTime(new Date());
msg.setContent(jsonObject.getString("content"));
JSONObject broadcast=JSONObject.fromObject(msg);
broadcast(broadcast.toString());
}
}
  • 其他代码

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<form action="/enter" method="get">
<table border="1px">
<tr>
<td>User:</td>
<td><input type="text" name="username" value="输入用户名"/></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="登录"/></td>
</tr>
</table>
</form>
</body>
</html>
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; @Controller
public class MyWebSocketHandler { @RequestMapping(value = "/enter",method = RequestMethod.GET)
public String enterChat(
Model model,
@RequestParam(name="username",required = false)String username){
model.addAttribute("username",username);
return "index";
}
}
import java.util.Date;

public class Msg {
private String senSessionId;
private String senName;
private String recSessionId="";
private String recName="";
private Date time=new Date();
private String content;
private Integer type; public Integer getType() {
return type;
} public void setType(Integer type) {
this.type = type;
} public String getSenSessionId() {
return senSessionId;
} public void setSenSessionId(String senSessionId) {
this.senSessionId = senSessionId;
} public String getSenName() {
return senName;
} public void setSenName(String senName) {
this.senName = senName;
} public String getRecSessionId() {
return recSessionId;
} public void setRecSessionId(String recSessionId) {
this.recSessionId = recSessionId;
} public String getRecName() {
return recName;
} public void setRecName(String recName) {
this.recName = recName;
} public Date getTime() {
return time;
} public void setTime(Date time) {
this.time = time;
} public String getContent() {
return content;
} public void setContent(String content) {
this.content = content;
} @Override
public String toString() {
return "Msg{" +
"senSessionId='" + senSessionId + '\'' +
", senName='" + senName + '\'' +
", recSessionId='" + recSessionId + '\'' +
", recName='" + recName + '\'' +
", time=" + time +
", content='" + content + '\'' +
", type=" + type +
'}';
}
}

基于WebSocket和SpringBoot的群聊天室的更多相关文章

  1. 基于Websocket开发的仿微信聊天室

    一.运行环境及涉及技术:----------------------------------* Visual Studio 2019* SQL SERVER 2008 R2* .Net FrameWo ...

  2. SpringBoot 搭建简单聊天室

    SpringBoot 搭建简单聊天室(queue 点对点) 1.引用 SpringBoot 搭建 WebSocket 链接 https://www.cnblogs.com/yi1036943655/p ...

  3. php websocket-网页实时聊天之PHP实现websocket(ajax长轮询和websocket都可以时间网络聊天室)

    php websocket-网页实时聊天之PHP实现websocket(ajax长轮询和websocket都可以时间网络聊天室) 一.总结 1.ajax长轮询和websocket都可以时间网络聊天室 ...

  4. Android基于XMPP Smack openfire 开发的聊天室

    Android基于XMPP Smack openfire 开发的聊天室(一)[会议服务.聊天室列表.加入] http://blog.csdn.net/lnb333666/article/details ...

  5. 基于Tomcat7、Java、WebSocket的服务器推送聊天室

    http://blog.csdn.net/leecho571/article/details/9707497 http://blog.fens.me/java-websocket-intro/ jav ...

  6. Tomcat学习总结(4)——基于Tomcat7、Java、WebSocket的服务器推送聊天室

    前言           HTML5 WebSocket实现了服务器与浏览器的双向通讯,双向通讯使服务器消息推送开发更加简单,最常见的就是即时通讯和对信息实时性要求比较高的应用.以前的服务器消息推送大 ...

  7. 如何用WebSocket实现一个简单的聊天室以及单聊功能

    百度百科中这样定义WebSocket:WebSocket协议是基于TCP的一种新的网络协议.它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端.简单的说,We ...

  8. 基于Server-Sent Event的简单在线聊天室

    Web即时通信 所谓Web即时通信,就是说我们可以通过一种机制在网页上立即通知用户一件事情的发生,是不需要用户刷新网页的.Web即时通信的用途有很多,比如实时聊天,即时推送等.如当我们在登陆浏览知乎时 ...

  9. 通过WebSocket实现一个简单的聊天室功能

    WebSocket WebSocket是一个协议,它是是基于TCP的一种新的网络协议,TCP协议是一种持续性的协议,和HTTP不同的是,它可以在服务器端主动向客户端推送消息.通过这个协议,可以在建立一 ...

随机推荐

  1. logstash+elasticsearch+kibana管理日志(安装)

    logstash1.先安装jdk2.wget https://download.elastic.co/logstash/logstash/logstash-2.4.0.tar.gz tar -xzvf ...

  2. android学习笔记48——SQLite

    SQLite SQLite试试一个嵌入式的数据库引擎,专门用于资源有限的设备(如手机.PDA)上适量数据存取. SQLite支持绝大部分SQL92语法,同样允许开发者使用SQL语句操作数据库中的数据, ...

  3. TVS參数具体解释及选型应用

    一.首先了解TVS管的參数,我们以littelfuse的5.0SMDJ系列为例. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGcybGg=/font/ ...

  4. A Mathematical Curiosity

    A Mathematical Curiosity Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65536/32768K (Java/O ...

  5. TCP/IP指纹鉴别 fingerprint

    http://www.freebuf.com/articles/system/30037.html使用TCP/IP协议栈指纹进行远程操作系统辨识 Fyodor <fyodor@insecure. ...

  6. JavaScript -- Form

    -----048-Form.html----- <!DOCTYPE html> <html> <head> <meta http-equiv="co ...

  7. Flask结合Redis消息队列实现电影弹幕

    用到的弹幕播放器插件:dplayer.js(开源) 1.安装Redis 2.安装flask-redis包 pip install flask-redis3.下载dplayer 页面搭建 1.引入资源 ...

  8. Linux内存管理 【转】

    转自:http://blog.chinaunix.net/uid-25909619-id-4491368.html Linux内存管理 摘要:本章首先以应用程序开发者的角度审视Linux的进程内存管理 ...

  9. Ultimate Weirdness of an Array CodeForces - 671C (gcd,线段树)

    大意: 定义一个数列的特征值为两个数gcd的最大值, $f(l,r)$表示数列删除区间$[l,r]$的元素后剩余元素的特征值, 求$\sum_{i=1}^n\sum_{j=i}^n{f(i,j)}$ ...

  10. 标准web浏览器的组件

    浏览器基本上包括如下几个组件 1.HTML.XML.CSS.JavsScript解析器 2.Layout 3.文字和图形渲染 4.图像解码 5.GPU交互 6.网络访问 7.硬件加速