在上一篇文章当中介绍了一下websocket是什么和产生原因以及和http协议的区别,我们今天来讨论一下websocket中的客户端和服务端程序。
1.websocket中的API
我们看一下就websocket JavaScript定义的接口:
[Constructor(in DOMString url, optional in DOMString protocol)]
interface WebSocket {
readonly attribute DOMString URL;
// ready state
const unsigned short CONNECTING = 0;
const unsigned short OPEN = 1;
const unsigned short CLOSED = 2;
readonly attribute unsigned short readyState;
readonly attribute unsigned long bufferedAmount;
// networking
attribute Function onopen;
attribute Function onmessage;
attribute Function onclose;
boolean send(in DOMString data);
void close();
};
WebSocket implements EventTarget;
既然是一个协议就是要有客户端和服务端,服务端每种语言有自己的API
websocket
的作用已经完全可以代替ajax,客户端API和ajax十分相似下面我们来具体的介绍一下:
- onopen,表示和服务器建立链接,建立连接之后才能进入下面的步骤,这个对应于
open方法ajax
- send,想用户服务器发送数据,对应于
的send方法ajax
- onmessage,建立连接之后,客户端收到服务器发来的数据,这个而对应
的responseTextajax
- onerror,当连接出现错误的时候,接收错误信息,这个
里面没有ajax
- onclose,监听消息的关闭,当连接结束的时候出发,这个
里面没有ajax
-
close,主动关闭socket连接
再来介绍一下ready state表示的四种状态(ajax中是五种)
- CONNECTING (0):表示还没建立连接;
- OPEN (1): 已经建立连接,可以进行通讯;
- CLOSING (2):通过关闭握手,正在关闭连接;
- CLOSED (3):连接已经关闭或无法打开;
2.客户端的websocket
2.1 websocket实例
介绍完了API我们就要用这些API去做一些事情,下面我们通过一个实例来应用一下这些方法
var serverAddr = 'ws://localhost:3000';
// 创建一个Socket实例
var socket = new WebSocket(serverAddr);
// 打开Socket
socket.onopen = function(event) {
// 发送一个初始化消息
socket.send('I am the client and I\'m listening!');
// 监听消息
socket.onmessage = function(event) {
console.log('Client received a message',event);
};
// 监听Socket的关闭
socket.onclose = function(event) {
console.log('Client notified socket has closed',event);
};
// 关闭Socket....
//socket.close()
};
var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);
//第二个参数可接受可选子协议,它既可以是字符串,也可以是字符串数组。每个字符串都应代表一个子协议名称,而服务器只能接受数组中通过的一个子协议。访问 WebSocket 对象的 protocol 属性可确定接受的子协议。
通过上面的例子我们就在客户端建立了一个和服务器建立连接的程序,这种方法比ajax的好处我们也在前面说过了。下面我们来做一下对比顺便复习一下
序号 | 步骤 | websocket | ajax |
---|---|---|---|
1 | 创建对象 | new WebSocket(serverUrl) | $1600 |
2 | 和服务器建立连接 | onopen | open |
2 | 向服务器发送数据 | send | send |
3 | 监听服务器的信息 | onmessage | readyState,status |
产生异常的应对机制 | onerror | 无,在jquery框架中有 | |
监听连接关闭 | onclose | 没有,因为不是长连接 | |
能否主动关闭 | close | 没有必要主动发起 |
在来看一下ready state的对比图:
状态值 | websocket | ajax |
---|---|---|
还没建立连接 | 还没建立连接 | |
1 | 已经建立连接,可以进行通讯 | 请求已经建立,但是还没有发送 |
2 | 通过关闭握手,正在关闭连接 | 请求已发送,正在处理中 |
3 | 连接已经关闭或无法打开 | 请求在处理中;通常响应中已有部分数据可用了,但是服务器还没有完成响应的生成 |
4 | 无 | 响应已完成;您可以获取并使用服务器的响应了。 |
第一个对比中前面序号是ajax运行的四个步骤,但是少了第四步骤,第四个步骤是显示到相应的位置,在这里灭有过多去说,通过第二个对比我们可以看出在
状态值
中websocket和ajax有很大的区别,这还是因为
http
写一个
websocket
协议的不同。如果对这个几个步骤不清楚的可以看我的另一篇博文ajax全接触
2.1 socket.io
Socket.IO使用检测功能来判断是否建立WebSocket连接,或者是AJAX long-polling连接,或Flash等。可快速创建实时的应用程序。Socket.IO还提供了一个NodeJS API,
来看一个实例:
<script src="socket.io\node_modules\socket.io-client\socket.io.js"> </script>
<script>
var socket = io('http://localhost:3000');
socket.on('news', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
</script>
Socket.IO简化了WebSocket API,统一了返回运输的API。传输包括:
- WebSocket
- Flash Socket
- AJAX long-polling
- AJAX multipart streaming
- IFrame
-
JSONP polling
你还可以设置任意的Socket.IO构造器的第二个选项,选项包括:
port - 待连接的端口
transports - 一个数组,包含不同的传输类型
transportOptions - 传输的参数使用的对象,带附加属性
Socket.IO还提供了由本地WebSocket API提供的普通连接、断开连接、消息事件。Socket还提供了封装每个事件类型的方法。
后续有机会我们继续讨论。
服务器的websocket
握手协议已经由客户端代码解决了,我们这里主要说服务器的程序,服务器程序我们在这里主要是用的
nodejs
使用其他语言的可以参考一下逻辑,因为nodejs的语法 和JavaScript一样都能 看懂。除了nodejs目前还有很多例如:
- Kaazing WebSocket Gateway(一个 Java 实现的 WebSocket Server)
- mod_pywebsocket(一个 Python 实现的 WebSocket Server)
- Netty(一个 Java 实现的网络框架其中包括了对 WebSocket 的支持)
- WebSocket4Net(一个.net的服务器端实现)
下一个考虑的问题:怎么握手呢?
还几个上一篇文章中客户端和服务端的协议栈图么,
其中客户端有一个:
Sec-WebSocket-Key
字段,服务器有一个:
Sec-WebSocket-Accept
字段,
服务器的
Sec-WebSocket-Accept
是由客户端的
Sec-WebSocket-Key
计算出来的,上一篇文章说
Sec-WebSocket-Key
是客户端随机产生的其实是不准确的,上一篇文章只是让大家了解这个字段,现在我们来看一下
Sec-WebSocket-Key
及产生过程:
- 取出Sec-WebSocket-Key(随机产生),与一个magic string “258EAFA5-E914-47DA-95CA-C5AB0DC85B11” 连接成一个新的key串
- 将新的key串SHA1编码,生成一个由多组两位16进制数构成的加密串
- 把加密串进行base64编码生成最终的key,这个key就是Sec-WebSocket-Key
在考虑一个:怎么才算握手成功呢?
为了表示服务器同意和客户端进行Socket连接, 服务器端需要使用客户端发送的这个Key进行校验 ,然后返回一个校验过的字符串给客户端,客户端验证通过后才能正式建立Socket连接。
当握手成功之后会执行open方法。
这个过程可能以后会详细的解释。
我们再来看一下服务端的程序[1]:
var app = require('http').createServer(handler);
var io = require('socket.io')(app);
var fs = require('fs');
//监听端口
app.listen(3000);
//响应状态
function handler (req, res) {
//读取文件
fs.readFile(__dirname + '/index.html',
//读取异常,输出错误
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
//设置返回码
res.writeHead(200);
//结束时,输出返回信息
res.end(data);
});
}
//l建立连接
io.on('connection', function (socket) {
//返回数据
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
这个就不做过多的解释,注释就很清楚了
[1]来自socket.io官方文档
如果对我的文章感兴趣可以关注的公众号:云影原生。
