Websocket

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--

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180721G1BRAK00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券