Websocket是一种基于TCP的全双工的通信协议。详细规范于2011年定义在RFC6455中。虽然Websocket借助于HTTP协议进行握手建立连接,但随后的数据交互是直接基于TCP进行的。
Websocket是利用HTTP协议头中的Upgrade来进行握手,例如client开始的request header如下:
GET ws://websocket.example.com/ HTTP/1.1
Origin: http://example.com
Connection: Upgrade
Upgrade: websocket
Server的response header如下:
HTTP/1.1 101 WebSocket Protocol Handshake
Connection: Upgrade
Upgrade: WebSocket
众所周知,HTTP的URI标示(scheme)是http和https,相对应的Websocket的URI scheme则是ws和wss。
相对于HTTP协议来说,Websocket的优势在于:
1、HTTP协议是单向的,都是由client先发起request,server端无法直接向client端发送数据。而Websocket则是双向的(bi-directional),建立连接(建立连接的过程必须是client发起的)之后,client和server都可以主动发送数据。
注:HTTP/2中,server是可以主动向client push数据的。但那并不是直接push到client application。这个不在本文的讨论范围。
2、每个HTTP request/response都带有一个HTTP header以及cookie数据,而Websocket在连接建立之后的数据交互过程则是直接基于TCP,没有HTTP协议这些overhead。这里并不是说Websocket没有header,只是说Websocket的overhead要小得多。
3、由于Server可以直接向client发送数据,以及小得多的overhead,所以websocket更适合于实时的数据交互,具有更低的网络延时。
在Websocket的官网上(如下)有一个echo的例子,演示了如何实现用JavaScript如何实现Websocket的客户端。
http://websocket.org/echo.html
该例子是直接连接官网的Websocket服务器,这里我们对这个例子稍作修改,把服务器的地址替换成我们自己的本地的服务器,修改后的client代码如下:
WebSocket Test
var wsUri = "ws://locahost:8080/"; //注:这是我们自己的本地服务器
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('RESPONSE: ' + evt.data+'');
websocket.close();
}
function onError(evt)
{
writeToScreen('ERROR: ' + evt.data);
}
function doSend(message)
{
writeToScreen("SENT: " + message);
websocket.send(message);
}
function writeToScreen(message)
{
var pre = document.createElement("p");
pre.innerHTML = message;
output.appendChild(pre);
}
window.addEventListener("load", init, false);
WebSocket Test
上面的代码非常简单易懂,稍微有点经验应该都能明白,所以就不啰嗦解释了。下面是用golang实现的一个简单的本地websocket服务器的代码,
package main
import (
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{} // use default options
func echo(w http.ResponseWriter, r *http.Request) {
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print("upgrade:", err)
return
}
defer c.Close()
for {
mt, message, err := c.ReadMessage()
if err != nil {
log.Println("read:", err)
break
}
log.Printf("recv: %s", message)
err = c.WriteMessage(mt, message)
if err != nil {
log.Println("write:", err)
break
}
}
}
func main() {
http.HandleFunc("/", echo)
log.Fatal(http.ListenAndServe("localhost:8080", nil))
}
运行上面的代码的步骤:
1、直接将上面的client代码保存为websocket.html,将server代码保存为server.go;
2、启动websocket server;
命令:go run server.go
3、然后直接在浏览器打开websocket.html,就会看到浏览器上有如下输出:
WebSocket Test
CONNECTED
SENT: WebSocket rocks
RESPONSE: WebSocket rocks
DISCONNECTED
还有一点值得注意,使用Websocket还涉及都穿越代理服务器的问题。通常来说,使用TLS时,穿越代理的成功率更高。有些反向代理服务器(reverse proxy)不支持Websocket,这时一个可选的方案就是自己动手扩展反向代理,使其支持Websocket。关于穿越代理(Proxy traversal)的话题,将来等深入调研之后也许会另行总结。
--END--
领取专属 10元无门槛券
私享最新 技术干货