websocket简单上手
文章目录
1.简介
websocket是一个网络通讯协议,通讯标准是RFC6455
websocket是html5中提供的一种单个tcp连接上进行全双工通讯协议
2.websocket的作用
http协议是无状态,无连接,简单,单项的应用层协议,采用请求响应模式,通信请求只能是客户端主动发起,服务器端进行应答,无法实现服务器端主动向客户端发送消息。
可以使用ajax请求轮询来达到服务器端向客户端发起推送的消息功能,但效率低,资源浪费严重。
基于该背景,websocket技术出现了,该技术允许客户端和服务器端进行全双工通讯,只需要建立一次连接就可以保持连接状态。利用心跳机制实现维护连接状态。
网络中的接收和发送数据都是使用 SOCKET 进行实现。但是如果此套接字已经断开,那发送数据和接收数据的时候就一定会有问题。可是如何判断这个套接字是否还可以使用呢?这个就需要在系统中创建心跳机制。所谓 “心跳” 就是定时发送一个自定义的结构体(心跳包或心跳帧),让对方知道自己 “在线”。以确保链接的有效性。
而所谓的心跳包就是客户端定时发送简单的信息给服务器端告诉它我还在而已。代码就是每隔几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息,如果服务端几分钟内没有收到客户端信息则视客户端断开。
在 WebSocket 协议中定义了 心跳 Ping 和 心跳 Pong 的控制帧:
- 心跳 Ping 帧包含的操作码是 0x9。如果收到了一个心跳 Ping 帧,那么终端必须发送一个心跳 Pong 帧作为回应,除非已经收到了一个关闭帧。否则终端应该尽快回复 Pong 帧。
- 心跳 Pong 帧包含的操作码是 0xA。作为回应发送的 Pong 帧必须完整携带 Ping 帧中传递过来的 “应用数据” 字段。如果终端收到一个 Ping 帧但是没有发送 Pong 帧来回应之前的 Ping 帧,那么终端可以选择仅为最近处理的 Ping 帧发送 Pong 帧。此外,可以自动发送一个 Pong 帧,这用作单向心跳。
3.websocket使用
浏览器和服务器端都必须实现websocket协议建立连接。
3.1websocket客户端
前提:支持html5的浏览器
3.1.1创建客户端websocket对象
//url:指定要连接的url地址,protocol这个参数可选,用于指定可接受的子协议
var Socket=new WebSocket(url,[protocol]);
3.1.2websocket对象中的属性介绍
//制度属性readyState表示的连接状态,其值0表示连接未建立,1表示连接建立,2表示连接正在关闭,3表示连接已关闭或者连接不能开启
Socket.readyState
//只读属性bufferedAmount已被send()放入队列等待传输
Socket.bufferedAmount
3.1.3websocket事件介绍
//建立连接时触发
Socket.onopen
//客户端接受服务端数据时触发
Socket.onmessage
//通讯发生错误时触发
Socket.onerror
//连接关闭时触发
Socket.onclose
3.1.4websocket方法
//发送数据
Socket.send()
//关闭连接
Socket.close()
3.1.5websocket前端简单测试代码
<!DOCTYPE html>
<meta charset="utf-8" />
<title>WebSocket Test</title>
<script language="javascript"type="text/javascript">
var wsUri ="ws://127.0.0.1:8080/myWebsocket";
var websocket = new WebSocket(wsUri);
//用来存放创建的多个连接对象
var websocketarray=new Array();
function testWebSocket() {
websocket.onopen = function(evt) {
onOpen(evt)
};
websocket.onmessage = function(evt) {
onMessage(evt)
};
websocket.onerror = function(evt) {
onError(evt)
};
websocket.onclose = function(evt) {
onClose(evt)
};
}
function onOpen(evt) {
writeToScreen("新建连接");
}
function onClose(evt) {
console.log('websocket 断开: ' + evt.code + ' ' + evt.reason + ' ' + evt.wasClean)
console.log(evt)
writeToScreen("关闭连接");
}
function onMessage(evt) {
writeToScreen('收到消息'+ evt.data);
}
function onError(evt) {
writeToScreen('出现错误 '+ evt.data);
}
function writeToScreen(message) {
console.info(message)
}
function submit(){
//new 一个websocket就是新建了一个连接
websocket = new WebSocket(wsUri);
websocketarray.push(websocket);
testWebSocket();
}
function stop(){
//设置从离当前连接最近一次连接删除
var stopwebsocket=websocketarray.pop();
//关闭该连接
stopwebsocket.close()
//websocketarray.splice(websocketarray.length-1,1);
}
function postmessage(){
var c=document.getElementById("message");
websocket.send(c.value)
}
window.addEventListener("load", testWebSocket, false);
</script>
<body>
<h2>WebSocket Test</h2>
<button onclick="submit()">启动新的连接</button><br>
<button onclick="stop()">主动断开连接</button><br>
<input type="text" id="message" placeholder="写要发送的消息">
<button onclick="postmessage()">发送消息</button><br>
</body>
</html>
3.2websocket服务端
这里有很多方案,比如使用Node(Socket.IO,WebSocket-Node),java,python等,我用的Java,在springboot项目中实现。基本使用注解操作
3.2.1springboot导websocket依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
3.2.2配置websocket环境
@Configuration
//开启websocket
@EnableWebSocket
public class MyWebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpoint() {
return new ServerEndpointExporter();
}
}
3.2.3创建websocket服务类
//有点类似我们经常用的@RequestMapping。比如你的启动端口是 8080,
// 而这个注解的值是 myWebsocket,那我们就可以通过 ws://127.0.0.1:8080/myWebsocket 来连接你的应用
@ServerEndpoint("/myWebsocket")
@Component
public class MyWebSocketService {
private Session session;
//存放每个客户端对应的MyWebSocket对象,就是一个session客户端对应后台一个mywebsocket服务实例
//可以这样理解:我的一个session都是具备触发建立连接,关闭连接,发送消息,接受消息的基本功能
//这些功能的基本实现需要这个服务实例支撑,也就是说客户端服务器端实例是一对一的关系
private static CopyOnWriteArraySet<MyWebSocketService> webSockets = new CopyOnWriteArraySet<>();
//连接成功
//当 websocket 建立连接成功后会触发这个注解修饰的方法,注意它有一个 Session 参数
@OnOpen
public void onOpen(Session session) {
this.session=session;
System.out.println(session.hashCode());
webSockets.add(this);
sendAll("服务器广播消息》》》》》浏览器》》》"+webSockets.size()+"》》》连接成功!!");
System.out.println("新连接成功"+webSockets.size());
}
//连接关闭
//当 websocket 建立的连接断开后会触发这个注解修饰的方法
@OnClose
public void onClose(Session session) {
for (MyWebSocketService s:webSockets
) {
if(s.session==this.session){
webSockets.remove(s);
}
}
System.out.println("新连接关闭"+this.session.hashCode());
}
//收到消息
//当客户端发送消息到服务端时,会触发这个注解修改的方法
@OnMessage
public String onMsg(String text) throws IOException {
System.out.println("服务器收到浏览器消息"+text);
//这个返回的string就是给当前session的浏览器的消息!
return "服务器给浏览器发消息"+text;
}
//出现异常
//当 websocket 建立连接时出现异常会触发这个注解修饰的方法
@OnError
public void onError(Session session, Throwable throwable) {
try {
System.out.println("触发异常");
session.close();
} catch (IOException e) {
System.out.println("发送失败");
e.printStackTrace();
}
}
//群发消息
private void sendAll(String message) {
for (MyWebSocketService myWebSocketService:webSockets) {
myWebSocketService.session.getAsyncRemote().sendText(message);
}
}
}
4.使用websocket测试结果
4.1启动springboot项目,浏览器打开前端测试代码
到此最简单的websocket算是弄通了