WebSocketC#Demo
WebSocket 规范
WebSocket 协议本质上是⼀个基于 TCP 的协议。为了建⽴⼀个 WebSocket 连接,客户端浏览器⾸先要向服务器发起⼀个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了⼀些附加头信息,其中附加头信息”Upgrade: WebSocket”表明这是⼀个申请协议升级的HTTP 请求,服务器端解析这些附加的头信息然后产⽣应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建⽴起来了,双⽅就可以通过这个连接通道⾃由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某⼀⽅主动的关闭连接。
下⾯我们来详细介绍⼀下 WebSocket 规范,由于这个规范⽬前还是处于草案阶段,版本的变化⽐较快,我们选择 draft-hixie-thewebsocketprotocol-76版本来描述 WebSocket 协议。因为这个版本⽬前在⼀些主流的浏览器上⽐如 Chrome,、FireFox、Opera 上都得到⽐较好的⽀持,您如果参照的是新⼀些的版本话,内容可能会略有差别。
⼀个典型的 WebSocket 发起请求和得到响应的例⼦看起来如下:
清单 1. WebSocket 握⼿协议
客户端到服务端:
GET /demo HTTP/1.1
Host: example
Connection: Upgrade
Sec-WebSocket-Key2: 12998 5 Y3 1 .P00
Upgrade: WebSocket
Sec-WebSocket-Key1: 4@1 46546xW%0l 1 5
Origin: example
[8-byte security key]
服务端到客户端:
HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin: example
WebSocket-Location: ws://example/demo
[16-byte hash response]
这些请求和通常的 HTTP 请求很相似,但是其中有些内容是和 WebSocket 协议密切相关的。我们需要简单介绍⼀下这些请求和应答信息,”Upgrade:WebSocket”表⽰这是⼀个特殊的 HTTP 请求,请求的⽬的就是要将客户端和服务器端的通讯协议从 HTTP 协议升级到WebSocket 协议。从客户端到服务器端请求的信息⾥包含有”Sec-WebSocket-Key1”、“Sec-WebSocket-Key2”和”[8-byte securitykey]”这样的头信息。这是客户端浏览器需要向服务器端提供的握⼿信息,服务器端解析这些头信息,并在握⼿的过程中依据这些信息⽣成⼀个 16 位的安全密钥并返回给客户端,以表明服务器端获取了客户端的请求,同意创建 WebSocket 连接。⼀旦连接建⽴,客户端和服务器端就可以通过这个通道双向传输数据了。
在实际的开发过程中,为了使⽤ WebSocket 接⼝构建 Web 应⽤,我们⾸先需要构建⼀个实现了 WebSocket 规范的服务器,服务器端的实现不受平台和开发语⾔的限制,只需要遵从 WebSocket 规范
即可,⽬前已经出现了⼀些⽐较成熟的 WebSocket 服务器端实现,⽐如:
Kaazing WebSocket Gateway — ⼀个 Java 实现的 WebSocket Server
mod_pywebsocket — ⼀个 Python 实现的 WebSocket Server
Netty —⼀个 Java 实现的⽹络框架其中包括了对 WebSocket 的⽀持
node.js —⼀个 Server 端的 JavaScript 框架提供了对 WebSocket 的⽀持
如果以上的 WebSocket 服务端实现还不能满⾜您的业务需求的话,开发⼈员完全可以根据 WebSocket 规范⾃⼰实现⼀个服务器。
在“WebSocket 实战”这⼀节,我们将使⽤ Microsoft .NET 平台上的 C# 语⾔来打造⼀个简单的 WebSocket 服务器,继⽽构建⼀个简单的实时聊天系统。
WebSocket JavaScript 接⼝
上⼀节介绍了 WebSocket 规范,其中主要介绍了 WebSocket 的握⼿协议。握⼿协议通常是我们在构建 WebSocket 服务器端的实现和提供浏览器的 WebSocket ⽀持时需要考虑的问题,⽽针对 Web 开发⼈员的 WebSocket JavaScript 客户端接⼝是⾮常简单的,以下是WebSocket JavaScript 接⼝的定义:
清单 2. WebSocket JavaScript 定义
-收缩JavaScript代码
[Constructor(in DOMString url, in optional 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;
其中 URL 属性代表 WebSocket 服务器的⽹络地址,协议通常是”ws”,send ⽅法就是发送数据到服务器端,close ⽅法就是关闭连接。除了这些⽅法,还有⼀些很重要的事件:onopen,onmessage,onerror 以及 onclose。我们借⽤ Nettuts ⽹站上的⼀张图来形象的展⽰⼀下 WebSocket 接⼝:
图 2. WebSocket JavaScript 接⼝
下⾯是⼀段简单的 JavaScript 代码展⽰了怎样建⽴ WebSocket 连接和获取数据:
清单 3. 建⽴ WebSocket 连接的实例 JavaScript 代码
-收缩JavaScript代码
var wsServer = 'ws://localhost:8888/Demo';
var websocket = new WebSocket(wsServer);
function onOpen(evt) {
console.log("Connected to WebSocket server.");
}
function onClose(evt) {
console.log("Disconnected");
}
function onMessage(evt) {
console.log('Retrieved data from server: ' + evt.data);
}
function onError(evt) {
console.log('Error occured: ' + evt.data);
}
浏览器⽀持
下⾯是主流浏览器对 HTML5 WebSocket 的⽀持情况:
浏览器⽀持情况
Chrome Supported in version 4+
Firefox Supported in version 4+
Internet Explorer Supported in version 10+
Opera Supported in version 10+
Safari Supported in version 5+
WebSocket 实战
这⼀节⾥我们⽤⼀个案例来演⽰怎么使⽤ WebSocket 构建⼀个实时的 Web 应⽤。这是⼀个简单的实时多⼈聊天系统,包括客户端和服务端的实现。客户端通过浏览器向聊天服务器发起请求,服务器端解析客户端发出的握⼿请求并产⽣应答信息返回给客户端,从⽽在客户端和服务器之间建⽴连接通道。服务器⽀持⼴播功能,每个聊天⽤户发送的信息会实时的发送给所有的⽤户,当⽤户退出聊天室时,服务
器端需要清理相应⽤户的连接信息,避免资源的泄漏。以下我们分别从服务器端和客户端来演⽰这个 Web 聊天系统的实现,在实现⽅式上我们采⽤了 C# 语⾔来实现 WebSocket 服务器,⽽客户端是⼀个运⾏在浏览器⾥的 HTML ⽂件。
WebSocket 服务器端实现
这个聊天服务器的实现和基于套接字的⽹络应⽤程序⾮常类似,⾸先是服务器端要启动⼀个套接字监听来⾃客户端的连接请求,关键的区别在于 WebSocket 服务器需要解析客户端的 WebSocket 握⼿信息,并根据 WebSocket 规范的要求产⽣相应的应答信息。⼀旦WebSocket 连接通道建⽴以后,客户端和服务器端的交互就和普通的套接字⽹络应⽤程序是⼀样的了。所以在下⾯的关于 WebSocket 服务
器端实现的描述中,我们主要阐述 WebSocket 服务器怎样处理 WebSocket 握⼿信息,⾄于 WebSocket 监听端⼝的建⽴,套接字信息流的读取和写⼊,都是⼀些常⽤的套接字编程的⽅式,我们就不多做解释了,您可以⾃⾏参阅本⽂的附件源代码⽂件。
在描述 WebSocket 规范时提到,⼀个典型的 WebSocket Upgrade 信息如下所⽰:
GET /demo HTTP/1.1
Host: example
Connection: Upgrade
Sec-WebSocket-Key2: 12998 5 Y3 1 .P00
Upgrade: WebSocket
Sec-WebSocket-Key1: 4@1 46546xW%0l 1 5
Origin: example
[8-byte security key]
其中 Sec-WebSocket-Key1,Sec-WebSocket-Key2 和 [8-byte security key] 这⼏个头信息是 WebSocket 服务器⽤来⽣成应答信息的来源,依据 draft-hixie-thewebsocketprotocol-76 草案的定义,WebSocket 服务器基于以下的算法来产⽣正确的应答信息:
1. 逐个字符读取 Sec-WebSocket-Key1 头信息中的值,将数值型字符连接到⼀起放到⼀个临时字符串⾥,同时统计所有空格的数量;
2. 将在第 1 步⾥⽣成的数字字符串转换成⼀个整型数字,然后除以第 1 步⾥统计出来的空格数量,将得到的浮点数转换成整数型;
3. 将第 2 步⾥⽣成的整型值转换为符合⽹络传输的⽹络字节数组;
4. 对 Sec-WebSocket-Key2 头信息同样进⾏第 1 到第 3 步的操作,得到另外⼀个⽹络字节数组;
5. 将 [8-byte security key] 和在第 3,第 4 步⾥⽣成的⽹络字节数组合并成⼀个 16 字节的数组;
6. 对第 5 步⽣成的字节数组使⽤ MD5 算法⽣成⼀个哈希值,这个哈希值就作为安全密钥返回给客户端,以表明服务器端获取了客户端的
请求,同意创建 WebSocket 连接
⾄此,客户端和服务器的 WebSocket 握⼿就完成了,WebSocket 通道也建⽴起来了。下⾯⾸先介绍⼀下服务器端实现是如何根据⽤户传递的握⼿信息来⽣成⽹络字节数组的。.NET 平台提供了很⽅便的对字符串,数值以及数组操作的函数,所以⽣成字节数组的⽅法还是⾮常简单明了的,代码如下:
清单 4. ⽣成⽹络字节数组的代码
得到⽹络字节数组以后,服务器端⽣成 16 位安全密钥的⽅法如下所⽰:
-收缩C#代码
private byte[] BuildServerPartialKey(string clientKey)
{
string partialServerKey = "";
byte[] currentKey;
int spacesNum = 0;
char[] keyChars = clientKey.ToCharArray();
foreach (char currentChar in keyChars)
{
if (char.IsDigit(currentChar)) partialServerKey += currentChar;
if (char.IsWhiteSpace(currentChar)) spacesNum++;
}
try
{
currentKey = BitConverter.GetBytes((int)(Int64.Parse(partialServerKey)
/ spacesNum));
if (BitConverter.IsLittleEndian) Array.Reverse(currentKey);
}
catch
{
if (currentKey!= null) Array.Clear(currentKey, 0, currentKey.Length);
}
字符串数组怎么转成bytereturn currentKey;
}
清单 5. ⽣成 16 位安全密钥的代码
-收缩C#代码
private byte[] BuildCompleteServerKey(byte[] serverKey1, byte[] serverKey2,
byte[] last8Bytes)
{
byte[] concatenatedKeys = new byte[16];
Array.Copy(serverKey1, 0, concatenatedKeys, 0, 4);
Array.Copy(serverKey2, 0, concatenatedKeys, 4, 4);
Array.Copy(last8Bytes, 0, concatenatedKeys, 8, 8);
System.Security.Cryptography.MD5 MD5Service =
System.Security.Cryptography.MD5.Create();
return MD5Service.ComputeHash(concatenatedKeys);
}
整个实现是⾮常简单明了的,就是将⽣成的⽹络字节数组和客户端提交的头信息⾥的 [8-byte security key] 合并成⼀个 16 位字节数组并⽤ MD5 算法加密,然后将⽣成的安全密钥作为应答信息返回给客户端,双⽅的 WebSocekt 连接通道就建⽴起来了。实现了 WebSocket 握⼿信息的处理逻辑,⼀个具有基本功能的 WebSocket 服务器就完成了。整个 WebSocket 服务器由两个核⼼类构成,⼀个是WebSocketServer,另外⼀个是 SocketConnection,出于篇幅的考虑,我们不介绍每个类的属性和⽅法了,⽂章的附件会给出详细的源代码,有兴趣的读者可以参考。
服务器刚启动时的画⾯如下:
图 3. WebSocket 服务器刚启动的画⾯
客户端可以依据这个信息填写聊天服务器的连接地址,当有客户端连接到聊天服务器上时,服务器会打印出客户端和服务器的握⼿信息,每个客户的聊天信息也会显⽰在服务器的界⾯上,运⾏中的聊天服务器的界⾯如下:
图 4. 有客户端连接到 WebSocket 服务器的
以上我们简单描述了实现⼀个 WebSocket 服务器的最基本的要素,下⼀节我们会描述客户端的实现。
客户端实现
客户端的实现相对于服务器端的实现来说要简单得多了,我们只需要发挥想象去设计 HTML ⽤户界⾯,然后呼叫 WebSocket JavaScript 接⼝来和 WebSocket 服务器端来交互就可以了。当然别忘了使⽤⼀个⽀持 HTML5 和 WebSocket 的浏览器,在笔者写这篇⽂章的时候使⽤的浏览器是 Firefox。客户端的页⾯结构是⾮常简洁的,初始运⾏界⾯如下:
图 5. 聊天室客户端初始页⾯
当页⾯初次加载的时候,⾸先会检测当前的浏览器是否⽀持 WebSocket 并给出相应的提⽰信息。⽤户按下连接按钮时,页⾯会初始化⼀个到聊天服务器的 WebSocekt 连接,初始化成功以后,页⾯会加载对应的 WebSocket 事件处理函数,客户端 JavaScript 代码如下所⽰:
清单 6. 初始化客户端 WebSocket 对象的代码
-收缩JavaScript代码
function ToggleConnectionClicked() {
if (SocketCreated && (ws.readyState == 0 || ws.readyState == 1)) {
ws.close();
} else {
Log("准备连接到聊天服务器 ...");
try {
ws =
new WebSocket("ws://" + ElementById("Connection").value);
SocketCreated = true;
} catch (ex) {
Log(ex, "ERROR");
return;
}
}
};
function WSonOpen() {
Log("连接已经建⽴。", "OK");
$("#SendDataContainer").show("slow");
};
function WSonMessage(event) {
Log(event.data);
};
function WSonClose() {
Log("连接关闭。", "ERROR");
$("#SendDataContainer").hide("slow");
};
function WSonError() {
Log("WebSocket错误。", "ERROR");
};
当⽤户按下发送按钮,客户端会调⽤WebSocket对象向服务器发送信息,并且这个消息会⼴播给所有的⽤户,实现代码如下所⽰:
-收缩JavaScript代码
function SendDataClicked()
{
if (ElementById("DataToSend").value != "") {
ws.ElementById("txtName").value + "说 :\"" +
}
};
如果有多个⽤户登录到聊天服务器,客户端页⾯的运⾏效果如下所⽰:
图 6. 聊天客户端运⾏页⾯
⾄此我们已经完成了⼀个完整的 WebSocket 客户端实现,⽤户可以体验⼀下这个聊天室的简单和快捷,完全不⽤考虑页⾯的刷新和繁琐的 Ajax 调⽤,享受桌⾯程序的⽤户体验。WebSocket 的强⼤和易⽤可见⼀斑,您完全可以在这个基础上加⼊更多的功能,设计更加漂亮的⽤户界⾯,切⾝体验 WebSocket 的震撼⼒。完整的客户端代码请参阅附件提供的源代码。
WebSocket 的局限性
WebSocket 的优点已经列举得很多了,但是作为⼀个正在演变中的 Web 规范,我们也要看到⽬前⽤ Websocket 构建应⽤程序的⼀些风险。⾸先,WebSocket 规范⽬前还处于草案阶段,也就是它的规范和 API 还是有变动的可能,另外的⼀个风险就是微软的 IE 作为占市场份额最⼤的浏览器,和其他的主流浏览器相⽐,对 HTML5 的⽀持是⽐较差的,这是我们在构建企业级的 Web 应⽤的时候必须要考虑的⼀个问题。
总结
本⽂介绍了 HTML5 WebSocket 的横空出世以及它尝试解决的的问题,然后介绍了 WebSocket 规范和 WebSocket 接⼝,以及和传统的实时技术相⽐在性能上的优势,并且演⽰了怎样使⽤ WebSocket 构建⼀个实时的 Web 应⽤,最后我们介绍了当前的主流浏览器对HTML5 的⽀持情况和 WebSocket 的局限性。不过,我们应该看到,尽管 HTML5 WebSocket ⽬前还有⼀些局限性,但是已经是⼤势所趋,微软也明确表达了未来对 HTML5 的⽀持,⽽且这些⽀持我们可以在 Windows 8 和 IE10 ⾥看到,我们也在各种移动设备,平板电脑上看到了 HTML5 和 WebSocket 的⾝影。WebSocket 将会成为未来开发实时 Web 应⽤的⽣⼒军应该是毫⽆悬念的了,作为 Web 开发⼈员,关注 HTML5,关注 WebSocket 也应该提上⽇程了,否则我们在新⼀轮的软件⾰新的浪潮中只能做壁上观了。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论