148 lines
5.9 KiB
Markdown
148 lines
5.9 KiB
Markdown
#8.2 WebSocket
|
||
首先我们了解一下什么是WebSocket?WebSocket是HTML5的重要特性,其通信协议实现的是基于浏览器的远程socket,实现了浏览器和服务器全双工通信,许多浏览器(Firefox、Google Chrome和Safari)都已对此做了支持。
|
||
|
||
在WebSocket之前,为了实现即时通信,所用的技术都是轮询,在特定的时间间隔内,由浏览器对服务器发出HTTP Request,然后服务器返回最新的数据给客户端的浏览器,但是这样浏览器需要对服务器不断发出请求,会占用很多带宽。
|
||
|
||
WebSocket使得浏览器和服务器只需要要做一个握手的动作,其用到了一些特殊的报头。然后,浏览器和服务器之间就形成了一条快速通道。连接会保持在活动状态,你可以使用JavaScript来写入和接收数据,就像是在使用一个原始的TCP Socket一样。两者之间就直接可以数据互相传送,它解决了Web实时化的问题,相比传统HTTP有如下好处:
|
||
|
||
- 一个Web客户端只建立一个TCP连接
|
||
- Websocket服务端可以推送(push)数据到web客户端.
|
||
- 有更加轻量级的头,减少数据传送量
|
||
|
||
WebSocket URL的起始输入是ws://或是wss://(在SSL上)。下图展示了WebSocket的通信过程,一个带有特定报头的HTTP握手被发送到了服务器端,接着在服务器端或是客户端就可以通过JavaScript来使用某种套接口(socket),这一套接口可被用来通过事件句柄异步地接收数据。
|
||
|
||

|
||
|
||
##WebSocket原理
|
||
WebSocket的协议颇为简单,在第一次handshake通过以后,便建立连接,其后的通讯数据都是以”\x00″开头,以”\xFF”结尾。在客户端,这个是透明的,WebSocket组件会自动将原始数据“掐头去尾”。
|
||
|
||
在实现WebSocket连线过程中,需要透过浏览器发出WebSocket连线请求,然后服务器发出回应,这个过程通常称为“握手” (handshaking)。请看下面的请求和反馈信息:
|
||
|
||

|
||
|
||
在请求中的"Sec-WebSocket-Key"是随机的,对于整天跟编码打交到的程序员,一眼就可以看出来:这个是一个经过base64编码后的数据。服务器端接收到这个请求之后需要把这个字符串连接上一个固定的字符串:
|
||
|
||
258EAFA5-E914-47DA-95CA-C5AB0DC85B11
|
||
|
||
把Sec-WebSocket-Key:后的字符串,即:f7cb4ezEAl6C3wRaU6JORA== 连接上那一串固定字符串,生成一个这样的字符串:
|
||
|
||
f7cb4ezEAl6C3wRaU6JORA==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
|
||
|
||
对该字符串先用 sha1安全散列算法计算出二进制的值,然后用base64对其进行编码,即可以得到握手后的字符串:
|
||
|
||
rE91AJhfC+6JdVcVXOGJEADEJdQ=
|
||
|
||
生成后,输出Sec-WebSocket-Accept响应头反馈给客户端。
|
||
|
||
##Go实现WebSocket
|
||
Go语言标准包里面没有针对WebSocket的支持,但是在他的net额外库中有这个的支持,首先你可以通过如下的命令获取该代码包:
|
||
|
||
go get code.google.com/p/go.net/websocket
|
||
|
||
WebSocket分为客户端和服务端,我们接下来实现一个简单的例子,用户输入信息,客户端通过WebSocket发送给服务器端,服务器端收到信息之后主动的Push信息到客户端,输出相应的信息,客户端的代码如下:
|
||
|
||
<html>
|
||
<head></head>
|
||
<body>
|
||
<script type="text/javascript">
|
||
var sock = null;
|
||
var wsuri = "ws://127.0.0.1:1234";
|
||
|
||
window.onload = function() {
|
||
|
||
console.log("onload");
|
||
|
||
sock = new WebSocket(wsuri);
|
||
|
||
sock.onopen = function() {
|
||
console.log("connected to " + wsuri);
|
||
}
|
||
|
||
sock.onclose = function(e) {
|
||
console.log("connection closed (" + e.code + ")");
|
||
}
|
||
|
||
sock.onmessage = function(e) {
|
||
console.log("message received: " + e.data);
|
||
}
|
||
};
|
||
|
||
function send() {
|
||
var msg = document.getElementById('message').value;
|
||
sock.send(msg);
|
||
};
|
||
</script>
|
||
<h1>WebSocket Echo Test</h1>
|
||
<form>
|
||
<p>
|
||
Message: <input id="message" type="text" value="Hello, world!">
|
||
</p>
|
||
</form>
|
||
<button onclick="send();">Send Message</button>
|
||
</body>
|
||
</html>
|
||
|
||
|
||
我们可以看到客户端JS很容易就通过`WebSocket`函数建立了一个sock,当客户端握手成功后,会触发WebScoket对象的onopen事件,告诉客户端连接已经成功建立。客户端一共绑定了四个事件。
|
||
|
||
- 1)onopen 建立连接后触发
|
||
- 2)onmessage 收到消息后触发
|
||
- 3)onerror 发生错误时触发
|
||
- 4)onclose 关闭连接时触发
|
||
|
||
我们服务器端的实现如下:
|
||
|
||
package main
|
||
|
||
import (
|
||
"code.google.com/p/go.net/websocket"
|
||
"fmt"
|
||
"log"
|
||
"net/http"
|
||
)
|
||
|
||
func Echo(ws *websocket.Conn) {
|
||
var err error
|
||
|
||
for {
|
||
var reply string
|
||
|
||
if err = websocket.Message.Receive(ws, &reply); err != nil {
|
||
fmt.Println("Can't receive")
|
||
break
|
||
}
|
||
|
||
fmt.Println("Received back from client: " + reply)
|
||
|
||
msg := "Received: " + reply
|
||
fmt.Println("Sending to client: " + msg)
|
||
|
||
if err = websocket.Message.Send(ws, msg); err != nil {
|
||
fmt.Println("Can't send")
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
func main() {
|
||
http.Handle("/", websocket.Handler(Echo))
|
||
|
||
if err := http.ListenAndServe(":1234", nil); err != nil {
|
||
log.Fatal("ListenAndServe:", err)
|
||
}
|
||
}
|
||
|
||
当我们再客户端输入信息之后,在服务器端通过Receive接收数据,然后通过Send发送数据。
|
||
|
||

|
||
|
||
通过上面的例子我们看到客户端和服务器端实现WebSocket非常的方便,Go的源码net分支中已经实现了这个的协议,我们可以直接拿来用,目前随着HTML5的发展,我想未来WebSocket会是Web开发的一个重点,我们需要储备这方面的知识。
|
||
|
||
|
||
## links
|
||
* [目录](<preface.md>)
|
||
* 上一节: [Socket编程](<8.1.md>)
|
||
* 下一节: [REST](<8.3.md>)
|
||
|
||
## LastModified
|
||
* $Id$ |