基于springboot的websocket聊天室
一、概述
1.Http
#http简介
HTTP是一个应用层协议,无状态的,端口号为80。主要的版本有1.0/1.1/2.0.
#http1.0/1.1/2.0
1.HTTP/1.* 一次请求-响应,建立一个连接,用完关闭;
2.HTTP/1.1 串行化单线程处理,可以同时在同一个tcp链接上发送多个请求,但是只有响应是有顺序的,只有上一个请求完成后,下一个才能响应。一旦有任务处理超时等,后续任务只能被阻塞(线头阻塞);
3.HTTP/2 并行执行。某任务耗时严重,不会影响到任务正常执行
2.WebSocket
#WebSocket简介
Websocket是html5提出的一个协议规范,是为解决客户端与服务端实时通信。本质上是一个基于tcp,先通过HTTP/HTTPS协议发起一条特殊的http请求进行握手后创建一个用于交换数据的TCP连接.WebSocket同HTTP一样也是应用层的协议,但是它是一种双向通信协议。
#WebSocket连接过程
1. 浏览器、服务器建立TCP连接,三次握手。这是通信的基础,传输控制层,若失败后续都不执行。
2. TCP连接成功后,浏览器通过HTTP协议向服务器传送WebSocket支持的版本号等信息。(开始前的HTTP握手)
3. 服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据。
4. 当收到了连接成功的消息后,通过TCP通道进行传输通信。
#WebSocket优势
浏览器和服务器只需要要做一个握手的动作,然后单独建立一条TCP的通信通道进行数据的传送, 在
建立连接之后,双方可以在任意时刻,相互推送信息。同时,服务器与客户端之间交换的头信息很小。
3.Socket
#socket简介
Socket并不是一个协议,而是为了方便使用TCP或UDP而抽象出来的一层,是位于应用层和传输控制层之间的一组接口。
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
#socket作用
当两台主机通信时,必须通过Socket连接,Socket则利用TCP/IP协议建立TCP连接。TCP连接则更依靠于底层的IP协议,IP协议的连接则依赖于链路层等更低层次。
4.WebSocket 和 Http
#相同点
1. 都是一样基于TCP的,都是可靠性传输协议。
2. 都是应用层协议。
#不同点
1. WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息。HTTP是单向的。
2. WebSocket是需要握手进行建立连接的
#联系
WebSocket在建立握手时,数据是通过HTTP传输的。但是建立之后,在真正传输时候是不需要HTTP协议的。
5.WebSocket 和 Socket
#区别
Socket是传输控制层协议,WebSocket是应用层协议。
6.长连接,短连接
#短连接
连接->传输数据->关闭连接
HTTP是无状态的,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。
也可以这样说:短连接是指SOCKET连接后发送后接收完数据后马上断开连接。
#长连接
连接->传输数据->保持连接 -> 传输数据-> ... ->关闭连接。
长连接指建立SOCKET连接后不管是否使用都保持连接,但安全性较差。
7.http和websocket的长连接区别
HTTP1.1通过使用Connection:keep-alive进行长连接,HTTP 1.1默认进行持久连接。在一次 TCP 连接中可以完成多个 HTTP 请求,但是对每个请求仍然要单独发 header,Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。这种长连接是一种“伪链接”
websocket的长连接,是一个真的全双工。长连接第一次tcp链路建立之后,后续数据可以双方都进行发送,不需要发送请求头。
keep-alive双方并没有建立正真的连接会话,服务端可以在任何一次请求完成后关闭。WebSocket 它本身就规定了是正真的、双工的长连接,两边都必须要维持住连接的状态
传统 HTTP 请求响应客户端服务器交互图
WebSocket 模式客户端与服务器的交互图
二、WebSocket请求报文和Http的差别
1.响应头
HTTP/1.1 101
Upgrade: websocket
Connection: upgrade #协议升级成功
Sec-WebSocket-Accept: IiuAbVCofO973oDeSggnpHFjLeU= #服务端处理之后的key
Sec-WebSocket-Extensions: permessage-deflate #扩展协议
Date: Fri, 02 Aug 2019 03:29:14 GMT
2.请求头
Host: xiajibagao.top:8080 #升级协议的服务主机地址端口
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Sec-WebSocket-Version: 13 #当前WebSocket协议版本号
Origin: http://xiajibagao.top:8080
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Key: wJfIzjPgehJrCXmKSvL8cQ== #连接凭证,客户端将这个key发送给服务器,服务器将这个key进行处理,将处理后的key返回给客户端,客户端根据这个key是否正确来判断是否建立连接。
Connection: keep-alive, Upgrade
Cookie: UM_distinctid=16c31bfc8219-0079038beea7fb-4c312c7c-144000-16c31bfc8226a3; CNZZDATA1277674304=827412020-1564198032-%7C1564319195
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket #通知服务器协议升级为WebSocket
三、基于springboot的简单聊天室
1.新建index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>这是一个聊天室</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<style>
#showarea{
border:1px solid darkgrey;
border-radius:10px;
min-height: 350px;
padding: 10px;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<br />
<div class="col-sm-4">
<div class="form-group">
<!--用户名-->
<input type="text" class="form-control" value="匿名" id="username">
<br />
<!--聊天内容-->
<textarea value="输入聊天内容" rows="10" class="form-control" id="sendarea"></textarea>
<br />
<!--提交按钮-->
<button class="btn btn-block btn-success" onclick="submitmsg()">发送</button>
</div>
</div>
<div class="col-sm-8">
<!--消息列表展示区-->
<div id="showarea">
</div>
</div>
</div>
</div>
</body>
</html>
2.页面的js
<script>
//当打开页面时新建WebSocket连接
var host = window.location.host;
var url = "ws://"+host+"/webchat/chat";//访问到后台项目注解所在的类
var ws = new WebSocket(url); //new一个新ws对象,new完即新建立一条“管道”
/**
*ws.onopen方法,当连接建立成功后触发
*/
ws.onopen=function(){
onsole.log("连接成功!");
};
/**
*发送消息给服务器
*/
function submitmsg() {
//获取用户名和要发送的消息
var username = document.getElementById("username").value;
var sendarea = document.getElementById("sendarea").value;
//转换为json字符串
var jsonmsg ={
username:username,
sendarea:sendarea,
time:new Date()
}
//发送消息
ws.send(JSON.stringify(jsonmsg));
}
/**
*从服务器接收消息
*ws.onmessage方法,当后台接受发送信息时触发
*/
ws.onmessage = function(evn){
//转换为json字符串
var jsonobj = eval(JSON.parse(evn.data));
//往页面插入消息
var msg = document.createElement("h4");
context = jsonobj.username+' '+getDate(jsonobj.time)+'<br />'+jsonobj.sendarea
msg.innerHTML=context;
var showareadiv = document.getElementById("showarea");
showareadiv.appendChild(msg);
};
/**
*ws.onclose方法,当窗口关闭,会话结束时触发
*/
ws.onclose = function(){
onsole.log("关闭连接");
};
/*日期转换*/
function getDate(time){
var date = new Date(time);
Year = date.getFullYear();
Month = date.getMonth();
Day = date.getDay();
time = Year+"-"+getZero(Month)+"-"+getZero(Month);
return time;
}
/*日期补零*/
function getZero(num){
if(parseInt(num) < 10 ){
num = "0" + num;
}
return num;
}
</script>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
</head>
<body>
<!-- 点击按钮,提交文本框内内容 -->
<button onclick="send()">open</button>
<input type="text" value="输入点什么" id="textarea">
<script type="text/javascript">
var host = window.location.host; //获取地址
var url = "ws://"+host+"/websocket/echo";//访问到后台项目注解所在的类
var ws = new WebSocket(url);//new一个新ws对象,new完即新建立一条“管道”
//当连接建立成功后触发
ws.onopen=function(){
onsole.log("连接成功!");
};
//当窗口关闭,会话结束时触发
ws.onclose = function(){
onsole.log("关闭连接");
};
//当后台接受发送信息时触发
ws.onmessage = function(evn){
alert(evn.data);
};
//当点击按钮时提交信息
function send() {
var msg = document.getElementById("textarea").value;
ws.send(msg);
}
</script>
</body>
</html>
3.添加依赖
<dependencies>
<!--使用自带的tomcat-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<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-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
</dependencies>
4.Controller
package com.huang.socketserver;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.RestController;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
/**
* Author:huang
* Date:2019-08-02 22:35
* Description:<描述>
*/
//
@ServerEndpoint("/chat")
@RestController
public class ChatSocket {
//定义一个全局变量集合sockets,用户存放每个登录用户的通信管道
private static Set<ChatSocket> sockets = new HashSet<ChatSocket>();
//定义一个全局变量Session,用于存放登录用户
private Session session;
/**
*@OnOpen注解
*注解下的方法会在连接建立时运行
*/
@OnOpen
public void open(Session session){
System.out.println("建立了一个socket通道" + session.getId());
this.session = session;
//将当前连接上的用户session信息全部存到scokets中
sockets.add(this);
}
/**
*@OnMessage注解
*注解下的方法会在前台传来消息时触发
*/
@OnMessage
public void getmes(Session session,String jsonmsg){
broadcast(sockets,jsonmsg);
}
/**
*@OnClose注解
*注解下的方法会在连接关闭时运行
*/
@OnClose
public void close(Session session){
//移除退出登录用户的通信管道
sockets.remove(this);
System.out.println(session.getId()+"退出了会话!");
}
/**
*广播消息
*/
public void broadcast(Set<ChatSocket> sockets , String msg){
//遍历当前所有的连接管道,将通知信息发送给每一个管道
for(ChatSocket socket : sockets){
try {
//通过session发送信息
System.out.println("发送给管道"+socket.session.getId());
socket.session.getBasicRemote().sendText(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.huang.websocket;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
@ServerEndpoint(value = "/echo")
public class echoSocket {
public echoSocket() {
System.out.println("新对象!");
}
/**
*@OnOpen注解
*注解下的方法会在连接建立时运行
*/
@OnOpen
public void open(Session session){
//一个session代表一个通信会话
//通过sessionid区分每一个会话
System.out.println("连接成功");
System.out.println(session.getId());
}
/**
*@OnClose注解
*注解下的方法会在连接关闭时运行
*/
@OnClose
public void close(Session session){
System.out.println("关闭"+session.getId());
}
/**
*@OnMessage注解
*注解下的方法会在前台传来消息是触发
*/
@OnMessage
public void getmes(Session session,String msg){
System.out.println("客户端:"+msg);
try {
//向请求服务器的会话发送消息
session.getBasicRemote().sendText("服务器:你发来的消息是-"+msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
五、运行效果
搞定!
=====================================
参考:https://www.cnblogs.com/ricklz/p/11108320.html
参考:https://www.cnblogs.com/Javi/p/9303020.html
=====================================
基于springboot的websocket聊天室的更多相关文章
- SpringBoot 搭建简单聊天室
SpringBoot 搭建简单聊天室(queue 点对点) 1.引用 SpringBoot 搭建 WebSocket 链接 https://www.cnblogs.com/yi1036943655/p ...
- 使用.NET Core和Vue搭建WebSocket聊天室
博客地址是:https://qinyuanpei.github.io. WebSocket是HTML5标准中的一部分,从Socket这个字眼我们就可以知道,这是一种网络通信协议.WebSocket是 ...
- 基于flask的网页聊天室(四)
基于flask的网页聊天室(四) 前言 接前天的内容,今天完成了消息的处理 具体内容 上次使用了flask_login做用户登录,但是直接访问login_requare装饰的函数会报401错误,这里可 ...
- 用Java构建一个简单的WebSocket聊天室
前言 首先对于一个简单的聊天室,大家应该都有一定的概念了,这里我们省略用户模块的讲解,而是单纯的先说说聊天室的几个功能:自我对话.好友交流.群聊.离线消息等. 今天我们要做的demo就能帮我们做到这一 ...
- websocket聊天室
目录 websocket方法总结 群聊功能 基于websocket聊天室(版本一) websocket方法总结 # 后端 3个 class ChatConsumer(WebsocketConsumer ...
- WebSocket聊天室demo
根据Socket异步聊天室修改成WebSocket聊天室 WebSocket特别的地方是 握手和消息内容的编码.解码(添加了ServerHelper协助处理) ServerHelper: using ...
- Netty入门(一)之webSocket聊天室
一:简介 Netty 是一个提供 asynchronous event-driven (异步事件驱动)的网络应用框架,是一个用以快速开发高性能.高可靠性协议的服务器和客户端. 换句话说,Netty 是 ...
- 基于flask的网页聊天室(三)
基于flask的网页聊天室(三) 前言 继续上一次的内容,今天完成了csrf防御的添加,用户头像的存储以及用户的登录状态 具体内容 首先是添加csrf的防御,为整个app添加防御: from flas ...
- 基于flask的网页聊天室(二)
基于flask的网页聊天室(二) 前言 接上一次的内容继续完善,今天完成的内容不是很多,只是简单的用户注册登录,内容具体如下 具体内容 这次要加入与数据哭交互的操作,所以首先要建立相关表结构,这里使用 ...
随机推荐
- WebGL中深度碰撞方法总结
z-fighting问题是三维渲染中常见的问题,本文根据实际工作中遇到的一些场景,进行了系统的总结 一个实际工作中的问题 当两个面离得太近就会发生深度碰撞问题,比如: 遇到深度检测问题,最重要的是先搞 ...
- ASP.NET Core MVC 之布局(Layout)
1.什么是布局 布局通常包含常见的用户界面元素,例如应用头,导航或菜单元素和页脚,如图: 常见的HTML结构(例如脚本和样式表)也经常被应用程序中许多页面使用.所有这些共享元素都可以在布局中定义,然后 ...
- C#中谁最快:结构还是类?
前言 在内存当道的日子里,无论什么时候都要考虑这些代码是否会影响程序性能呢? 在现在的世界里,几乎不会去考虑用了几百毫秒,可是在特别的场景了,往往这几百毫米确影响了整个项目的快慢. 通过了解这两者之间 ...
- Kotlin学习快速入门(4)——集合使用
List,Set,Map都是集合 List 是一个有序集合,可通过索引(反映元素位置的整数)访问元素.元素可以在 list 中出现多次.列表的一个示例是一句话:有一组字.这些字的顺序很重要并且字可以重 ...
- Appium+python自动化(二十五)- 那些让人抓耳挠腮、揪头发和掉头发的事 - 获取控件ID(超详解)
简介 在前边的第二十二篇文章里,已经分享了通过获取控件的坐标点来获取点击事件的所需要的点击位置,那么还有没有其他方法来获取控件点击事件所需要的点击位置呢?答案是:Yes!因为在不同的大小屏幕的手机上获 ...
- Mobile game forensics
My friend Carrie'd like to know "Garena 传说对决" violates any mobile risks such as insecure d ...
- 解释一下一门语言该有的东东(Javascript)
注释 Js中有两种注释 // 单行注释 /**/ 多行注释 变量 变量就像学校学习的 未知数 如 3 + x = 8 x: 类似变量,在改造一下 x + y = z 当 x=3, y=5, z=8, ...
- 神奇的 SQL 之子查询,细节满满 !
前言 开心一刻 有一天,麻雀遇见一只乌鸦. 麻雀问:你是啥子鸟哟 ? 乌鸦说:我是凤凰. 麻雀说:哪有你龟儿子这么黢黑的凤凰 ? 乌鸦说:你懂个铲铲,老子是烧锅炉的凤凰. 子查询 讲子查询之前,我们先 ...
- DataPipeline丨DataOps理念与设计原则
作者:DataPipeline CEO 陈诚 上周我们探讨了数据的「资产负债表」与「现状」,期间抛给大家一个问题:如果我们制作一个企业的“数据资产负债表”,到底会有多少数据是企业真正的资产? 数据出现 ...
- alluxio源码解析-rpc调用概述(1)
alluxio中几种角色以及角色之间的rpc调用: 作为分布式架构的文件缓存系统,rpc调用必不可少 client作为客户端 master提供thrift rpc的服务,管理以下信息: block信息 ...