1. 什么是WebSocket?菜鸟对websocket的解释如下

  WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

  WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

  在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

  现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询(我目前接触的线上聊天也确实是基于这种方式实现的)。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

  HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯

  浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。

  当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。

以下 API 用于创建 WebSocket 对象。

var Socket = new WebSocket(url, [protocol] );

WebSocket 属性

属性 描述
Socket.readyState

只读属性 readyState 表示连接状态,可以是以下值:

  • 0 - 表示连接尚未建立。

  • 1 - 表示连接已建立,可以进行通信。

  • 2 - 表示连接正在进行关闭。

  • 3 - 表示连接已经关闭或者连接不能打开。

Socket.bufferedAmount

只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。

WebSocket 事件:

事件 事件处理程序 描述
open Socket.onopen 连接建立时触发
message Socket.onmessage 客户端接收服务端数据时触发
error Socket.onerror 通信发生错误时触发
close Socket.onclose 连接关闭时触发

WebSocket 方法:

方法 描述
Socket.send()

使用连接发送数据

Socket.close()

关闭连接

2. Tomcat中开发简单的WebSocket实例

  在这里实现一个简易的聊天室。

开发环境:JDK7+Tomcat7.0.91(如果连接URL报404建议切换tomcat为高版本,我在7.0.88测试就连接不到后端)

最终效果如下:

代码如下:

后端:

package chat;

import java.io.IOException;
import java.util.HashMap; import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint; @ServerEndpoint("/chat")
public class ChatServer { private boolean first = true; private String name; private Session session; private static final HashMap<String, ChatServer> clientSet = new HashMap<String, ChatServer>(); public ChatServer() {
System.out.println("========ChatServer创建============");
} /*
* 客户端连接时触发该方法
*/
@OnOpen
public void onOpen(Session session) throws IOException {
this.session = session;
} /*
* 客户端断开时触发该方法
*/
@OnClose
public void onClose() {
clientSet.remove(name); String message = String.format("【%s %s】", name, "离开了聊天室!");
broadcast(message);
} /*
* 客户端收到消息时触发该方法
*/
@OnMessage
public void onMessage(String msg) {
if (first) {
name = msg;
clientSet.put(name, this);
String message = String.format("【%s %s】", name, "加入了聊天室!");
// 发送消息
broadcast(message);
first = false;
} else {
broadcast("【" + name + "】" + ":" + msg);
} } // 当客户端通信出现错误时,激发该方法
@OnError
public void onError(Throwable t) throws Throwable {
System.out.println("WebSocket服务端错误 " + t);
} public void broadcast(String msg) {
// 遍历服务器关联的所有客户端
ChatServer client = null;
for (String nickname : clientSet.keySet()) { try {
client = (ChatServer) clientSet.get(nickname);
synchronized (client) {
// 发送消息
client.session.getBasicRemote().sendText(msg);
}
} catch (IOException e) {
System.out.println("聊天错误,向客户端 " + client + " 发送消息出现错误。");
clientSet.remove(name);
try {
client.session.close();
} catch (IOException e1) {
}
String message = String.format("【%s %s】", client.name, "已经被断开了连接。");
broadcast(message);
}
}
} }

前端:

<!DOCTYPE html>
<html> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>简单聊天室</title>
<script type="text/javascript">
var webSocket = null;
if ("WebSocket" in window) {
// 创建WebSocket对象
webSocket = new WebSocket("ws://localhost:86/WebSocket/chat");
} else {
alert("您的浏览器不支持 WebSocket!");
} var sendMsg = function() {
var inputElement = document.getElementById('msg');
if (inputElement.value == '')
alert("输入内容不为空");
else {
if (inputElement.value == "quit" || inputElement.value == "exit") {
webSocket.close();
return;
} // 发送消息
webSocket.send(inputElement.value);
// 清空单行文本框
inputElement.value = "";
}
}
var send = function(event) {
if (event.keyCode == 13) {
sendMsg();
}
}; webSocket.onopen = function() {
n = prompt('请给自己取一个昵称:');
if (n != '' && n != null)
webSocket.send(n);
else
//设置游客登录
webSocket.send("游客" + Math.random() * 100000000000000000); // 此处可以加一个异步请求name是否使用的请求来判断name是否可用 document.getElementById('msg').onkeydown = send;
document.getElementById('sendBn').onclick = sendMsg;
}; // 为onmessage事件绑定监听器,接收消息
webSocket.onmessage = function(event) {
var show = document.getElementById('show')
// 接收、并显示消息
show.innerHTML += new Date() + "<br/>" + event.data + "<br/>";
//让聊天框滚动条始终显示新消息
show.scrollTop = show.scrollHeight;
}; webSocket.onclose = function() {
document.getElementById('msg').onkeydown = null;
document.getElementById('sendBn').onclick = null;
};
</script>
</head>
<body>
<div
style="width: 600px; height: 240px; overflow-y: auto; border: 1px solid #333;"
id="show"></div>
<input type="text" size="80" id="msg" name="msg" placeholder="输入聊天内容" />
<input type="button" value="发送" id="sendBn" name="sendBn" />
</body>
</html>

注意:

  ws://localhost:86/WebSocket/chat 这个地址 是  协议://IP:Port/项目名称/websocket地址

第一次建立Socket连接的时候请求头和响应头如下:

  • Connection 必须设置 Upgrade,表示客户端希望连接升级。
  • Upgrade 字段必须设置 Websocket,表示希望升级到 Websocket 协议。
  • Sec-WebSocket-Key 是随机的字符串,服务器端会用这些数据来构造出一个 SHA-1 的信息摘要。把 “Sec-WebSocket-Key” 加上一个特殊字符串 “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,然后计算 SHA-1 摘要,之后进行 BASE-64 编码,将结果做为 “Sec-WebSocket-Accept” 头的值,返回给客户端。如此操作,可以尽量避免普通 HTTP 请求被误认为 Websocket 协议。
  • Sec-WebSocket-Version 表示支持的 Websocket 版本。RFC6455 要求使用的版本是 13,之前草案的版本均应当弃用。
  • Origin 字段是可选的,通常用来表示在浏览器中发起此 Websocket 连接所在的页面,类似于 Referer。但是,与 Referer 不同的是,Origin 只包含了协议和主机名称。
  • 其他一些定义在 HTTP 协议中的字段,如 Cookie 等,也可以在 Websocket 中使用。

补充:上面是每个连接都会创建1个ChatServer对象,多例模式的对象。Session与HttpSession不一样。

3. SpringBoot中开启WebSocket

  springboor整合websocket也非常简单。如下:

1.  pom.xml增加如下依赖

        <!-- websocket的包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

2.代码如下

配置类:

package cn.qlq.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter; @Configuration
public class WebSocketConfig { @Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
} }

ChatServer.java

package cn.qlq.websocket;

import java.io.IOException;
import java.util.Hashtable; import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint; import org.springframework.stereotype.Component; @ServerEndpoint("/chat")
@Component
public class ChatServer { private boolean first = true; private String name; private Session session; private static final Hashtable<String, ChatServer> clientSet = new Hashtable<String, ChatServer>(); public ChatServer() {
System.out.println("========ChatServer创建============");
} /*
* 客户端连接时触发该方法
*/
@OnOpen
public void onOpen(Session session) throws IOException {
this.session = session;
} /*
* 客户端断开时触发该方法
*/
@OnClose
public void onClose() {
clientSet.remove(name); String message = String.format("【%s %s】", name, "离开了聊天室!");
broadcast(message);
} /*
* 客户端收到消息时触发该方法
*/
@OnMessage
public void onMessage(String msg) {
if (first) {
name = msg;
clientSet.put(name, this);
String message = String.format("【%s %s】", name, "加入了聊天室!");
// 发送消息
broadcast(message);
first = false;
} else {
broadcast("【" + name + "】" + ":" + msg);
} } // 当客户端通信出现错误时,激发该方法
@OnError
public void onError(Throwable t) throws Throwable {
System.out.println("WebSocket服务端错误 " + t);
} /**
* 将此方法设计为静态方法用于服务器主动调用该方法广播消息
*
* @param msg
*/
public static void broadcast(String msg) {
// 遍历服务器关联的所有客户端
ChatServer client = null;
for (String nickname : clientSet.keySet()) { try {
client = (ChatServer) clientSet.get(nickname);
synchronized (client) {
// 发送消息
client.session.getBasicRemote().sendText(msg);
}
} catch (IOException e) {
System.out.println("聊天错误,向客户端 " + client + " 发送消息出现错误。");
clientSet.remove(nickname);
try {
client.session.close();
} catch (IOException e1) {
}
String message = String.format("【%s %s】", client.name, "已经被断开了连接。");
broadcast(message);
}
}
} }

Controller层代码:

package cn.qlq.controller.websocket;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import cn.qlq.websocket.ChatServer; @RequestMapping("/chat")
@Controller
public class ChatController { /**
* 跳转到聊天页面
*
* @return
*/
@RequestMapping("/chatRoom")
public String chatRoom() {
return "websocket/chatRoom";
} /**
* http请求发送消息
*
* @param msg
* @return
*/
@RequestMapping("/sendMsg")
public @ResponseBody String sendMsg(String msg) {
ChatServer.broadcast(msg);
return "success";
} }

前端页面:

<!DOCTYPE html>
<html> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>简单聊天室</title>
<script type="text/javascript">
var webSocket = null;
if ("WebSocket" in window) {
// 创建WebSocket对象
webSocket = new WebSocket("ws://192.168.43.137:8088/chat");
} else {
alert("您的浏览器不支持 WebSocket!");
} var sendMsg = function() {
var inputElement = document.getElementById('msg');
if (inputElement.value == '')
alert("输入内容不为空");
else {
if (inputElement.value == "quit" || inputElement.value == "exit") {
webSocket.close();
return;
} // 发送消息
webSocket.send(inputElement.value);
// 清空单行文本框
inputElement.value = "";
}
}
var send = function(event) {
if (event.keyCode == 13) {
sendMsg();
}
}; webSocket.onopen = function() {
n = prompt('请给自己取一个昵称:');
if (n != '')
webSocket.send(n);
else
//设置游客登录
webSocket.send("游客" + Math.random() * 100000000000000000); // 此处可以加一个异步请求name是否使用的请求来判断name是否可用 document.getElementById('msg').onkeydown = send;
document.getElementById('sendBn').onclick = sendMsg;
}; // 为onmessage事件绑定监听器,接收消息
webSocket.onmessage = function(event) {
var show = document.getElementById('show')
// 接收、并显示消息
show.innerHTML += new Date() + "<br/>" + event.data + "<br/>";
//让聊天框滚动条始终显示新消息
show.scrollTop = show.scrollHeight;
}; webSocket.onclose = function() {
document.getElementById('msg').onkeydown = null;
document.getElementById('sendBn').onclick = null;
};
</script>
</head>
<body>
<div style="width: 600px; height: 240px; overflow-y: auto; border: 1px solid #333;" id="show"></div>
<input type="text" size="80" id="msg" name="msg" placeholder="输入聊天内容" />
<input type="button" value="发送" id="sendBn" name="sendBn" />
</body>
</html>

2.测试

http发送消息:

  经测试,上面ChatServer也是多例模式,每次请求都会创建ChatServer实例对象。

Git地址:https://github.com/qiao-zhi/springboot-ssm

WebSocket的简单认识&SpringBoot整合websocket的更多相关文章

  1. springboot整合websocket原生版

    目录 HTTP缺点 HTTP websocket区别 websocket原理 使用场景 springboot整合websocket 环境准备 客户端连接 加入战队 微信公众号 主题 HTTP请求用于我 ...

  2. SpringBoot整合websocket简单示例

    依赖 <!-- springboot整合websocket --> <dependency> <groupId>org.springframework.boot&l ...

  3. SpringBoot 整合 WebSocket

    SpringBoot 整合 WebSocket(topic广播) 1.什么是WebSocket WebSocket为游览器和服务器提供了双工异步通信的功能,即游览器可以向服务器发送消息,服务器也可以向 ...

  4. Springboot整合Websocket遇到的坑

    Springboot整合Websocket遇到的坑 一.使用Springboot内嵌的tomcat启动websocket 1.添加ServerEndpointExporter配置bean @Confi ...

  5. springboot整合websocket高级版

    目录 sockjs介绍 产生的原因 环境搭建 springboot整合sockjs 使用场景 聊天室开发 点对点通信 群聊 效果 总结 加入战队 微信公众号 上一章节我们说了websocket的优缺点 ...

  6. Springboot整合WebSocket实现网页版聊天,快来围观!

  7. springboot整合websocket实现客户端与服务端通信

    定义  WebSocket是通过单个TCP连接提供全双工(双向通信)通信信道的计算机通信协议.此WebSocket API可在用户的浏览器和服务器之间进行双向通信.用户可以向服务器发送消息并接收事件驱 ...

  8. SpringBoot整合websocket

    1.新增pom依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId& ...

  9. 【Java分享客栈】SpringBoot整合WebSocket+Stomp搭建群聊项目

    前言 前两周经常有大学生小伙伴私信给我,问我可否有偿提供毕设帮助,我说暂时没有这个打算,因为工作实在太忙,现阶段无法投入到这样的领域内,其中有两个小伙伴又问到我websocket该怎么使用,想给自己的 ...

随机推荐

  1. .NET Core 收徒,有缘者,可破瓶颈

    最近感悟天命,偶有所得,故而打算收徒若干,以继吾之传承. 有缘者,可破瓶颈,职场巅峰指日可待. 入门基本要求: 1.工作经验:1年或以上. 2.拜师费用:3999元(RMB). 传承说明: 1.收徒人 ...

  2. 安卓微信对接H5微信支付出现“商家参数有误,请联系商家解决”的问题处理

    最近遇到客户在对接我们微信支付的时候,一些商家反馈在用户支付的过程中会出现报错,出错的截图如下: 查看微信官方文档如下:https://pay.weixin.qq.com/wiki/doc/api/H ...

  3. vue-v-for

    1.v-for遍历数组和对象 <ul> <li v-for="item in array">{{item}}</li><br> &l ...

  4. jmeter5.1分布式压测

    在使用jmeter压测过程中,可能会度遇到内存溢出的错误,这是为什么呢?因为jmeter是java写的应用,java应用jvm堆内存heap受负载机硬件限制,虽然我们可以调整堆内存大小,但是单机无法支 ...

  5. HeadFirst设计模式---简单工厂

    简单工厂的理解 简单工厂不是设计模式的一种,只是代码规范而且.也就是说构造一个披萨工厂出来,按不同味道生产不同的披萨. 类图 抽象披萨 public abstract class AbstractPi ...

  6. C# 测试网络速度例子

    using System.Net.NetworkInformation; namespace PingExample { public partial class Form1 : Form { pub ...

  7. 队列(Quene)

    对multiprocessing中的Quene进行演示 import multiprocessing """ 队列Quene是mutiprocessing 中 的一个类 ...

  8. 笔记4:WEB服务器

    web服务器 1 HTTP协议 http:超文本传输协议,基于tcp的方式,会更稳当更安全.协议就是规定了怎样去请求服务器,服务器如何返回信息.如下图红色方框标记所示: 打开浏览器电商广告原理: 我们 ...

  9. python--微信小程序“跳一跳‘外挂

    参考网站:http://blog.csdn.net/LittleBeautiful/article/details/78955792 0x00:准备工具: Windows 10: 一个安卓真机 pyt ...

  10. 201871010108-高文利《面向对象程序设计(java)》第十五周学习总结

    项目 内容 这个作业属于哪个课程 <任课教师博客主页链接> https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 <作业链接地址> ht ...