Websocket使用手记
websocket是什么?
Websocket最起初是在HTTP协议的基础上发展而来,后独立成为了一个标准。其特点是只要建立一次连接,即可保持服务端与客户端的长连接状态,期间可保持全双工的通讯(实时双方互传)。由于省去了重复建立连接产生的性能和带宽消耗,WebSocket可实现高性能的数据实时传递。
用于什么场景?
可应用在前后端数据需要实时数据互传的情况,比如:
- 聊天系统
- 股票显示
- 实时交互游戏
为什么要选用它?
选用websocket的理由: + 相比起直接建立TCP连接,使用WebSocket可以更便捷,协议内已处理了粘包、压缩、心跳等问题。 + 协议的握手建立在http 1.1,因此在开发过程中可以很好的集成到现有的Restful风格中。 + 协议中的数据传递虽然是websocket的独立协议,但是其传输过程与http请求响应风格相近,开发难度低,学习成本低。 + 协议的本质还是建立一个socket连接。原生支持wss安全访问,底层支持数据压缩、自动检验和消息包自动拆分。 + 客户端应用简单,前端对于websocket的处理,代码简单,开发效率高。
使用过程
服务端
目前Go语言主流的Websocket库有两个:
- 官方golang.org/x/websocket 库
- github.com/gorilla/websocket 库
官方的库目前支持情况不太理想,godoc官方的websocket包页面介绍上推荐使用Gorilla的库。两者对比下,官方的包目前能够实现基本的服务器和客户端连接、收发功能。如果需要快捷开发基本WebSocket功能可使用官方包。需要协议的高级功能的推荐采用Gorilla包。
Gorilla/websocket包,丰富了支持Websocket协议内的数据压缩、心跳检测事件、碎片信息。
程序结构
websocket
- main()
- 注册普通HTTP路由
- 监听HTTP服务
- websocket_handler()
- Upgrade 升级普通HTTP协议为Websocket协议,建立连接
- For循环
- ReadMessage 读取信息
- WriteMessage 写入信息
- ReadMessage 读取信息
- Upgrade 升级普通HTTP协议为Websocket协议,建立连接
- 注册普通HTTP路由
服务端的编写并不复杂,与普通HTTP服务编写相近,先编写一个Handler处理函数,再在主程序中注册路由,开启服务,不同的主要在处理函数中。由于普通HTTP的响应都是无状态,即请求后响应即可退出协程,但Websocket需要协程一直在跑,而且句柄固定,因此我们需要在Websocket的处理函数中加入循环,不让它关闭。
- 客户端主动断开连接
- 服务端接收信息失败
- 服务端发送信息失败
- 心跳检查失败
客户端
javascript
参考websocket.org的示例代码,
注意:
- 默认的示例代码不支持心跳
- 默认代码不支持压缩
示例代码:
<!DOCTYPE html>
<meta charset="utf-8" />
<title>WebSocket Test</title>
<script language="javascript" type="text/javascript">
var wsUri = "ws://echo.websocket.org/"; //填入本机websocket绑定的地址
var output;
function init()
{
output = document.getElementById("output");
testWebSocket();
}
function testWebSocket()
{
websocket = new WebSocket(wsUri);
websocket.onopen = function(evt) { onOpen(evt) };
websocket.onclose = function(evt) { onClose(evt) };
websocket.onmessage = function(evt) { onMessage(evt) };
websocket.onerror = function(evt) { onError(evt) };
}
function onOpen(evt)
{
writeToScreen("CONNECTED");
doSend("WebSocket rocks");
}
function onClose(evt)
{
writeToScreen("DISCONNECTED");
}
function onMessage(evt)
{
writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>');
websocket.close();
}
function onError(evt)
{
writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
}
function doSend(message)
{
writeToScreen("SENT: " + message);
websocket.send(message);
}
function writeToScreen(message)
{
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML = message;
output.appendChild(pre);
}
window.addEventListener("load", init, false);
</script>
<h2>WebSocket Test</h2>
<div id="output"></div>
golang
相关资料
- Websocket协议详情: https://tools.ietf.org/html/rfc6455
- Websocket协议程序实现: https://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/
- Websocket原理介绍: https://www.zhihu.com/question/20215561 Ovear的回答
- HTML5 WebSocket Api: https://www.w3.org/TR/2011/WD-websockets-20110419/