• 引入

    • 普通请求-响应方式:例如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. iOS_一个购物车的使用

    这个项目是本人原创:要转载,请说明下:http://www.cnblogs.com/blogwithstudyofwyn/p/5618107.html 项目的地址:https://github.com ...

  2. [转]非OpenVZ下利用谷歌TCP-BBR协议单边加速你的VPS

    前段时间谷歌推出了新的 TCP-BBR 开源算法,可以起到单边加速 TCP 连接的效果,也就是不用客户端的配合,用来替代收费的锐速再合适不过,毕竟开源免费.TCP-BBR 的目的是要尽量跑满带宽,并且 ...

  3. 让eclipse启动时拥有jre

    让eclipse 拥有jre,这样才能启动 eclipse/eclipse.ini 首行加入 -vm /home/liujl/installer/jdk/bin/java

  4. ASP.NET MVC 解决LINQ表达式中的SqlMethods 未找到命名空间问题

    右键项目属性下的引用: 添加引用: 搜索寻找——System.Data.Linq,然后添加成功,即可解决LINQ表达式中的SqlMethods 未找到命名空间问题

  5. 利用npoi把多个DataTable导入Excel多个sheet中

    { 题外拓展:把datatable插入dataset DataTable fuben = new DataTable();//定义的datatablefuben = table.Tables[0].C ...

  6. C++调用C语言的库函数

    在项目中,使用C语言编写了一个socket后台程序tkcofferd,并且为方便客户端的使用,提供了动态库,其中包含socket接口. 现在的需求是使用qt做一个前端界面,用来展示tkcofferd的 ...

  7. 004.Ansible Ad-Hoc命令集

    一 Ad-Hoc使用场景 Ad-Hoc更倾向于解决简单.临时性任务. 1.1 Ad-Hoc基础命令 基本语法: 1 ansible <host-pattern> [options] < ...

  8. 什么是面向切面编程AOP--知识点汇总

           最近在学这方面的内容,读到的这段话我感觉说的很清楚了:这种在运行时,动态地将代码切入到类的指定方法.指定位置上的编程思想就是面向切面的编程. 面向切面编程(AOP是Aspect Orie ...

  9. java课程课后作业05之动手动脑

    一.使用Files. walkFileTree()找出指定文件夹下所有大于指定大小(比如1M)的文件 此代码没有使用walkfiletree,两者的差别在于walkfiletree在遍历文件的时候有一 ...

  10. FileInputStream与FileOutputStream 复制文件例子代码

    try { File sourceFile = new File("C:\\Users\\prize\\Desktop\\Demo1\\盗墓笔记7.txt");//创建源文件 In ...