即时通讯的业务场景和需求
即时通信(Instant Messaging,简称IM)是一个允许两人或多人使用网络实时的传递文字消息、文件、语音与视频交流。 即时通讯技术应用于需要实时收发消息的业务场景。
现在各种各样的即时通讯软件也层出不穷:
-
客服系统
招商银行客服中心 -
直播互动
-
社交APP
-
智能硬件,物联网
2 短连接和长连接
即时通讯使用的是长连接,这里我们介绍一下短连接和长连接。
2.1 短连接
客户端和服务器每进行一次通讯,就建立一次连接,通讯结束就中断连接。
HTTP是一个简单的请求-响应协议,它通常运行在TCP之上。HTTP/1.0使用的TCP默认是短连接。
2.2 长连接
是指在建立连接后可以连续多次发送数据,直到双方断开连接。
HTTP从1.1版本起,底层的TCP使用的长连接。
使用长连接的HTTP协议,会在响应头加入代码:Connection:keep-alive
2.3 短连接和长连接的区别
2.3.1 通讯流程
短连接:创建连接 -> 传输数据 -> 关闭连接 长连接:创建连接 -> 传输数据 -> 保持连接 -> 传输数据 -> …… -> 关闭连接
2.3.2 适用场景
短连接:并发量大,数据交互不频繁情况
长连接:数据交互频繁,点对点的通讯
2.3.3 通讯方式
方式 | 说明 |
---|---|
短连接 | 我跟你发信息,必须等到你回复我或者等了一会等不下去了,就结束通讯了 |
长连接 | 我跟你发信息,一直保持通讯,在保持通讯这个时段,我去做其他事情的当中你回复我了,我能立刻你回复了我什么,然后可以回应或者不回应,继续做事 |
3 websocket协议
一、WebSocket是什么?
-
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
- 何谓全双工:全双工(Full Duplex)是通讯传输的一个术语。双方在通信时允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合。全双工指可以同时进行信号的双向传输。指A→B的同时B→A,就像是双向车道。
- 单工就就像是汽车的单行道,是在只允许甲方向乙方传送信息,而乙方不能向甲方传送 。
-
参考资料:https://baike.baidu.com/item/%E5%85%A8%E5%8F%8C%E5%B7%A5/310007?fr=aladdin
-
在 WebSocket中,浏览器和服务器只需要完成一次握手,就可以创建持久性的连接,并进行双向数据传输。
-
在推送功能的实现技术上,相比使用Ajax 定时轮询的方式(setInterval),WebSocket 更节省服务器资源和带宽。
-
服务器向客户端发送数据的功能是websocket协议的典型使用场景
二、为什么需要WebSocket?
我们知道,传统的HTTP协议是无状态的,每次请求(request)都要由客户端(如 浏览器)主动发起,服务端进行处理后返回response结果,而服务端很难主动向客户端发送数据;这种客户端是主动方,服务端是被动方的传统Web模式 对于信息变化不频繁的Web应用来说造成的麻烦较小,而对于涉及实时信息的Web应用却带来了很大的不便。
因此,随着HTML5的诞生,一种新的通信协议应运而生—WebSocket,他最大的特点就是服务端可以主动向客户端推送消息,客户端也可以主动向服务端发送消息,实现了真正的平等。
**举个例子:**如带有即时通信、实时数据、订阅推送等功能的应用。在WebSocket规范提出之前,开发人员若要实现这些实时性较强的功能,经常会使用折衷的解决方法:ajax轮询(最原始的实现实时Web应用的解决方案)
**ajax轮询的原理:**ajax轮询的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。明显地,这种方法会导致过多不必要的请求,浪费流量和服务器资源。
场景再现:
客户端:有没有新信息(Request)
服务端:没有(Response)
客户端:有没有新信息(Request)
服务端:没有。。(Response)
客户端:有没有新信息(Request)
服务端:你好烦啊,没有啊。。(Response)
客户端:有没有新消息(Request)
服务端:有啦,给你。(Response)
客户端:有没有新消息(Request)
服务端:。。。。。没。。。。没。。。没有(Response) ---- loop
websocket的原理:当服务器完成协议升级后(HTTP->Websocket),服务端就可以主动推送信息给客户端啦。
场景再现:
客户端:我要建立Websocket协议,需要的服务:chat,Websocket协议版本:17(HTTP Request)
服务端:确认,已升级为Websocket协议(HTTP Protocols Switched)
客户端:麻烦你有信息的时候推送给我噢。。
服务端:好的,有的时候会告诉你的。
服务端:balabalabalabala
服务端:哈哈哈哈哈啊哈哈哈哈
服务端:笑死我了哈哈哈哈哈哈哈
三、WebSocket其他特点如下:
(1)建立在 TCP 协议之上,服务器端的实现比较容易。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
四、创建WebSocket对象
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。当你获取 WebSocket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。
// url, 指定连接的 URL
// protocol 是可选的,指定可接受的子协议。
let Socket = new WebSocket(url, [protocol] );
五、WebSocket属性
假设我们创建了一个Socket对象
属性 描述
Socket.readyState
只读属性 readyState 表示连接状态,可以是以下值:
0 - 表示连接尚未建立。
1 - 表示连接已建立,可以进行通信。
2 - 表示连接正在进行关闭。
3 - 表示连接已经关闭或者连接不能打开。
Socket.bufferedAmount 只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。
六、WebSocket事件
假设我们创建了一个Socket对象
事件 | 事件处理程序 | 描述 |
---|---|---|
open | Socket.onopen | 连接建立时触发 |
message | Socket.onmessage | 客户端接收服务端数据时触发 |
error | Socket.onerror | 通信发生错误时触发 |
close | Socket.onclose | 连接关闭时触发 |
七、WebSocket方法
假设我们创建了一个Socket对象
方法 | 描述 |
---|---|
Socket.send() | 使用连接发送数据 |
Socket.close() | 关闭连接 |
八、WebSocket实例
JavaScript
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<style>
</style>
<body>
<input id="text" type="text" />
<button onclick="send()">发送消息</button>
<button onclick="closeWebSocket()">关闭WebSocket连接</button>
<div id="message"></div>
<script>
let websocket = null;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:8080/websocket");
} else {
alert('当前浏览器不支持websocket!')
}
//连接发生错误的回调方法
websocket.onerror = function () {
console.log("WebSocket连接发生错误");
};
//连接成功建立的回调方法
websocket.onopen = function () {
console.log("WebSocket连接成功");
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
document.getElementById('message').innerHTML += event.data + '<br/>';
}
//连接关闭的回调方法
websocket.onclose = function () {
console.log("WebSocket连接关闭");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
closeWebSocket();
}
//关闭WebSocket连接
function closeWebSocket() {
websocket.close();
}
//发送消息
function send() {
let message = document.getElementById('text').value;
websocket.send(message);
}
</script>
</body>
</html>
Vue
<template>
<div>
<el-input v-model="params" clearable/>
<el-button type="primary" @click="send">发消息</el-button>
</div>
</template>
<script>
export default {
data() {
return {
params: '',
path: "ws://localhost:8080/websocket",
socket: ""
}
},
mounted() {
// 初始化
this.init()
},
destroyed() {
// 销毁监听
this.socket.onclose = this.close
},
methods: {
init: function () {
if (typeof (WebSocket) === "undefined") {
console.log("您的浏览器不支持socket")
} else {
// 实例化socket
this.socket = new WebSocket(this.path)
// 监听socket连接成功回调
this.socket.onopen = this.open
// 监听socket连接失败回调
this.socket.onerror = this.error
// 监听后台返回的socket消息
this.socket.onmessage = this.getMessage
}
},
open: function () {
console.log("socket连接成功")
},
error: function () {
console.log("连接错误")
},
getMessage: function (msg) {
console.log(msg.data)
},
send: function () {
this.socket.send(params)
},
close: function () {
console.log("socket已经关闭")
}
}
}
</script>