一、概述

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+'&nbsp;&nbsp;'+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聊天室的更多相关文章

  1. SpringBoot 搭建简单聊天室

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

  2. 使用.NET Core和Vue搭建WebSocket聊天室

    博客地址是:https://qinyuanpei.github.io.  WebSocket是HTML5标准中的一部分,从Socket这个字眼我们就可以知道,这是一种网络通信协议.WebSocket是 ...

  3. 基于flask的网页聊天室(四)

    基于flask的网页聊天室(四) 前言 接前天的内容,今天完成了消息的处理 具体内容 上次使用了flask_login做用户登录,但是直接访问login_requare装饰的函数会报401错误,这里可 ...

  4. 用Java构建一个简单的WebSocket聊天室

    前言 首先对于一个简单的聊天室,大家应该都有一定的概念了,这里我们省略用户模块的讲解,而是单纯的先说说聊天室的几个功能:自我对话.好友交流.群聊.离线消息等. 今天我们要做的demo就能帮我们做到这一 ...

  5. websocket聊天室

    目录 websocket方法总结 群聊功能 基于websocket聊天室(版本一) websocket方法总结 # 后端 3个 class ChatConsumer(WebsocketConsumer ...

  6. WebSocket聊天室demo

    根据Socket异步聊天室修改成WebSocket聊天室 WebSocket特别的地方是 握手和消息内容的编码.解码(添加了ServerHelper协助处理) ServerHelper: using ...

  7. Netty入门(一)之webSocket聊天室

    一:简介 Netty 是一个提供 asynchronous event-driven (异步事件驱动)的网络应用框架,是一个用以快速开发高性能.高可靠性协议的服务器和客户端. 换句话说,Netty 是 ...

  8. 基于flask的网页聊天室(三)

    基于flask的网页聊天室(三) 前言 继续上一次的内容,今天完成了csrf防御的添加,用户头像的存储以及用户的登录状态 具体内容 首先是添加csrf的防御,为整个app添加防御: from flas ...

  9. 基于flask的网页聊天室(二)

    基于flask的网页聊天室(二) 前言 接上一次的内容继续完善,今天完成的内容不是很多,只是简单的用户注册登录,内容具体如下 具体内容 这次要加入与数据哭交互的操作,所以首先要建立相关表结构,这里使用 ...

随机推荐

  1. 配置 IDEA 远程连接应用服务器

    当调试 Web 应用时,经常需要使用 ide 远程连接,来进行 debug 调试.使用 Springboot 内置服务器和使用 Tomcat 服务器是常见的应用部署方式,可以用不同的配置方式来启动远程 ...

  2. zookeeper集群搭建及常用场景实现

    本文完整源码地址 基于zookeeper的常用用法.分布式锁.分布式队列及leader选举实现 https://github.com/killianxu/zookeeper_example zooke ...

  3. .net持续集成单元测试篇之单元测试简介以及在visual studio中配置Nunit使用环境

    系列目录 单元测试及测试驱动开发简介 什么是单元测试 单元测试是一段自动化的代码,这段代码调用被测试的工作单元,之后对这个单元的单个最终结果的某些假设进行检验.单元测试几乎都是用单元测试框架编写的.单 ...

  4. X-Admin&ABP框架开发-系统日志

    网站正常运行中有时出现异常在所难免,查看系统运行日志分析问题并能够根据错误信息快速解决问题尤为重要,ABP对于系统运行日志这块已经做了很好的处理,默认采用的Log4Net已经足够满足开发过程中的需要了 ...

  5. 林大妈的JavaScript基础知识(三):JavaScript编程(4)数组

    数组,是一段线性分配的,具有非常高性能的数据结构.简单地说,数组以连续的空间存储,通过整数地计算偏移量访问其中的元素,将读取修改的时间复杂度降低至O(1),我们称之为猝发式存取.是不是非常期待?没错, ...

  6. 33行代码爬取妹子图片(bs4+urllib)

    from bs4 import BeautifulSoupimport urllib2import urllibimport lxmlimport os def get_imgs(): image_c ...

  7. gdb调试和编译后运行结果不一致

    今天在看代码时,遇到这么一段代码,我但是用g++编译了,运行发现有Segmentation fault. 然后就用gdb跟进去看看,可是gdb却正常执行了.不知道什么原因. #include < ...

  8. Go中的fmt几种输出的区别和格式化方式

    在日常使用fmt包的过程中,各种眼花缭乱的print是否让你莫名的不知所措呢,更让你茫然的是各种格式化的占位符..简直就是噩梦.今天就让我们来征服格式化输出,做一个会输出的Goer. fmt.Prin ...

  9. java io读取性能对比

    背景 从最早bio的只支持阻塞的bio(同步阻塞) 到默认阻塞支持非阻塞nio(同步非阻塞+同步阻塞)(此时加入mmap类) 再到aio(异步非阻塞) 虽然这些api改变了调用模式,但真正执行效率上是 ...

  10. 吉特日化MES-工业生产盲区

    工业生产的几大盲区 1  重硬件忽略软件 : 目前只要提到智能化,大家都是想到的是一大堆自动执行的设备,什么机器人,输送线,人脸识别摄像头等,在一成套的系统中可能硬件几百万上千万,软件可以是几万几千几 ...