websocket简单实现分为以下几个步骤:添加websocket库、编写后台代码、编写前端代码。
添加websocket库
在maven中添加websocket库的代码如下所示:
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
注:九风有次没写<scope>字段,前端后台都会报错,大家记得加上就行。
WebSocket 客户端的实现
一,WebSocket 客户端的简单示例
WebSocket 的用法比较简单,下面是一个网页脚本的例子:
var ws = new WebSocket("wss://echo.websocket.org"); ws.onopen = function(evt) { console.log("Connection open ..."); ws.send("Hello WebSockets!"); }; ws.onmessage = function(evt) { console.log( "Received Message: " + evt.data); ws.close(); }; ws.onclose = function(evt) { console.log("Connection closed."); };
二,WebSocket 客户端的 API
- WebSocket 构造函数
WebSocket 对象作为一个构造函数,用于新建 WebSocket 实例
var ws = new WebSocket('ws://localhost:8080');
执行上面语句之后,客户端就会与服务器进行连接。
- webSocket.readyState
readyState
属性返回实例对象的当前状态,共有四种。
- CONNECTING:值为0,表示正在连接。
- OPEN:值为1,表示连接成功,可以通信了。
- CLOSING:值为2,表示连接正在关闭。
- CLOSED:值为3,表示连接已经关闭,或者打开连接失败。
下面是一个示例:
switch (ws.readyState) {
case WebSocket.CONNECTING:
// do something
break;
case WebSocket.OPEN:
// do something
break;
case WebSocket.CLOSING:
// do something
break;
case WebSocket.CLOSED:
// do something
break;
default:
// this never happens
break;
}
- webSocket.onopen
实例对象的onopen
属性,用于指定连接成功后的回调函数。
ws.onopen = function () {
ws.send('Hello Server!');
}
如果要指定多个回调函数,可以使用addEventListener
方法。
ws.addEventListener('open', function (event) {
ws.send('Hello Server!');
});
- webSocket.onclose
实例对象的onclose
属性,用于指定连接关闭后的回调函数。
ws.onclose = function(event) {
var code = event.code;
var reason = event.reason;
var wasClean = event.wasClean;
// handle close event
};ws.addEventListener("close", function(event) {
var code = event.code;
var reason = event.reason;
var wasClean = event.wasClean;
// handle close event
});
- webSocket.onmessage
实例对象的onmessage
属性,用于指定收到服务器数据后的回调函数。
ws.onmessage = function(event) {
var data = event.data;
// 处理数据
};ws.addEventListener("message", function(event) {
var data = event.data;
// 处理数据
});
注意,服务器数据可能是文本,也可能是二进制数据(blob
对象或Arraybuffer
对象)。
ws.onmessage = function(event){
// 动态判断收到的数据类型
if(typeof event.data === String) {
console.log("Received data string");
}// 动态判断收到的数据类型
if(event.data instanceof ArrayBuffer){
var buffer = event.data;
console.log("Received arraybuffer");
}
}
除了动态判断收到的数据类型,也可以使用binaryType
属性,显式指定收到的二进制数据类型。
// 收到的是 blob 数据
ws.binaryType = "blob";
ws.onmessage = function(e) {
console.log(e.data.size);
};// 收到的是 ArrayBuffer 数据
ws.binaryType = "arraybuffer";
ws.onmessage = function(e) {
console.log(e.data.byteLength);
};
- webSocket.send()
实例对象的send()
方法用于向服务器发送数据。
1,发送文本的例子:
ws.send('your message');
2,发送 Blob 对象的例子:
var file = document
.querySelector('input[type="file"]')
.files[0];
ws.send(file);
3,发送 ArrayBuffer 对象的例子:
// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
ws.send(binary.buffer);
- webSocket.bufferedAmount
实例对象的bufferedAmount
属性,表示还有多少字节的二进制数据没有发送出去。它可以用来判断发送是否结束。
var data = new ArrayBuffer(10000000);
socket.send(data);if (socket.bufferedAmount === 0) {
// 发送完毕
} else {
// 发送还没结束
}
- webSocket.onerror
实例对象的onerror
属性,用于指定报错时的回调函数。
socket.onerror = function(event) {
// handle error event
};socket.addEventListener("error", function(event) {
// handle error event
});
实际应用前端代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
websocket Demo---- user000 <br />
<input id="text" type="text" />
<button onclick="send()"> Send </button>
<button onclick="closeWebSocket()"> Close </button>
<div id="message"> </div>
<script type="text/javascript">
//判断当前浏览器是否支持WebSocket
if('WebSocket' in window){
websocket = new WebSocket("ws://localhost:8080/Demo/websocketTest/user000");
console.log("link success")
}else{
alert('Not support websocket')
}
//连接发生错误的回调方法
websocket.onerror = function(){
setMessageInnerHTML("error");
};
//连接成功建立的回调方法
websocket.onopen = function(event){
setMessageInnerHTML("open");
}
console.log("-----")
//接收到消息的回调方法
websocket.onmessage = function(event){
setMessageInnerHTML(event.data);
}
//连接关闭的回调方法
websocket.onclose = function(){
setMessageInnerHTML("close");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function(){
websocket.close();
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML){
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//关闭连接
function closeWebSocket(){
websocket.close();
}
//发送消息
function send(){
var message = document.getElementById('text').value;
websocket.send(message);
}
</script>
</body>
</html>
WebSocket 服务端的实现
后台实现websocket有两种方式:使用继承类、使用注解;注解方式比较方便,一下代码中使用注解方式来进行演示。
- @ServerEndpoint
声明websocket地址类似Spring MVC中的@controller注解类似,websocket使用@ServerEndpoint来进行声明接口:@ServerEndpoint(value="/websocket/{paraName}") ; 其中 “ { } ”用来表示带参数的连接,如果需要获取{}中的参数在参数列表中增加:@PathParam("paraName") Integer userId 。
1,@OnOpen
public void onOpen(Session session) throws IOException{ }-------有连接时的触发函数。 我们可以在用户连接时记录用户的连接带的参数,只需在参数列表中增加参数:@PathParam("paraName") String paraName。
扩展:
其中OnOpen发生的时候,即有链接过来的时候,可以把当前WebSocket Server丢在ServerManager里管理起来,这样Tomcat才知道总共有哪些Server, 方便以后进行群发,具体案例http://how2j.cn/k/websocket/websocket-develop/1628.html#nowhere
2,@OnClose
public void onClose(){ }------连接关闭时的调用方法。
3,@OnMessage
public void onMessage(String message, Session session) { }-------收到消息时调用的函数,其中Session是每个websocket特有的数据成员
4,Session----每个Session代表了两个web socket断点的会话;当websocket握手成功后,websocket就会提供一个打开的Session,可以通过这个Session来对另一个端点发送数据;如果Session关闭后发送数据将会报错。
5,Session.getBasicRemote().sendText("message")-------向该Session连接的用户发送字符串数据。
6,@OnError
public void onError(Session session, Throwable error) { }--------发生意外错误时调用的函数。
实际应用后端代码如下:
import java.io.IOException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @Class: Test
* @Description: 简单websocket demo
* @author 九风萍舟
*/
@ServerEndpoint(value="/websocketTest/{userId}")
public class Test {
private Logger logger = LoggerFactory.getLogger(Test.class);
private static String userId;
//连接时执行
@OnOpen
public void onOpen(@PathParam("userId") String userId,Session session) throws IOException{
this.userId = userId;
logger.debug("新连接:{}",userId);
}
//关闭时执行
@OnClose
public void onClose(){
logger.debug("连接:{} 关闭",this.userId);
}
//收到消息时执行
@OnMessage
public void onMessage(String message, Session session) throws IOException {
logger.debug("收到用户{}的消息{}",this.userId,message);
session.getBasicRemote().sendText("收到 "+this.userId+" 的消息 "); //回复用户
}
//连接错误时执行
@OnError
public void onError(Session session, Throwable error){
logger.debug("用户id为:{}的连接发送错误",this.userId);
error.printStackTrace();
}
}
ServerEndpoint报错: 原因是不能自动检测 ServerEndpoint 的包,解决方法:复制 import javax.websocket.server.ServerEndpoint;
到文件程序 import 区域即可。
测试运行
在Chrome上打开前端代码后,马上就建立了连接,大家可以使用F12查看下建立连接的请求与响应,可以对比前面关于协议建立的部分进行学习。
建立连接后,想后台发送数据后,同时可以看到后台返回的信息:
前端测试
在后台可以看到连接的建立和收到的数据:
后台测试
对于其他功能功能大家可以自己测测。
总结
websocket特别适合于需要实时数据传送的场景,比轮询方式效率高很多。
扩展:一款非常特别的 WebSocket 服务器:Websocketd。
它的最大特点,就是后台脚本不限语言,标准输入(stdin)就是 WebSocket 的输入,标准输出(stdout)就是 WebSocket 的输出。具体使用介绍http://www.ruanyifeng.com/blog/2017/05/websocket.html
参考来源于:
http://www.ruanyifeng.com/blog/2017/05/websocket.html
https://www.jianshu.com/p/d79bf8174196
https://www.java-mindmap.com/channel/7
http://how2j.cn/k/websocket/websocket-develop/1628.html#nowhere