1. 什么是Web Socket
Web Socket是Html5中引入的通信机制,它为浏览器与后台服务器之间提供了基于TCP的全双工的通信通道。用以替代以往的LongPooling等comet style的实时解决方案。基于它们之间的比较以及Web Socket的优势参考https://www.websocket.org/quantum.html.
2. Web Socket如何工作
Connect
Web Socket在建立之前需要先与后台服务器进行握手。具体来说通过如下Http请求:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
后台服务器如果支持切换到WebSocket,会返回如下Response:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
浏览器收到该Response会切换到基于当前TCP连接的WebSocket通道。
Data Transfer
连接建立后,浏览器端可以和服务器发送Text类型的消息进行全双工的通信,类似于基于TCP的Socket通信。
Disconnect
当浏览器或者后台服务器想终止通信,需向对方发送类型为Close的消息,并等待对方收到消息并确认后连接断开。
3. 浏览器,服务器支持情况
参考维基百科,目前浏览器的支持情况如下:
IE | Chrome | Firefox | Safari | Opera |
---|---|---|---|---|
10+ | 16+ | 11+ | 6+ | 12.10+ |
IIS从8.0开始支持Web Socket
Coding:
服务端代码:
1. 为了接受浏览器端的握手请求,我们需要定义一个Web Api接口接受握手请求
[Route("login/socket")]
[HttpGet]
public HttpResponseMessage longsocket()
{
if (HttpContext.Current.IsWebSocketRequest)
{
//如果是,我们附加异步处理程序
HttpContext.Current.AcceptWebSocketRequest(ProcessWSMsg);
}
return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols);
}
2. 为了对Web Socket进行消息收发,需要定义Web Socket的消息收发函数(以前的.net 版本是接受一个实现特定接口的对象,新版改成了接受一个函数),代码如下:
private static Hashtable webSocketPool = new Hashtable();
private async Task ProcessWSMsg(AspNetWebSocketContext arg)
{
//获取当前的WebSocket对象
WebSocket webSocket = arg.WebSocket;
var hostaddress = arg.UserHostAddress;
//添加到websocket连接池
webSocketPool.Add(hostaddress, webSocket);
/*
* 我们定义一个常数,它将表示接收到的数据的大小。 它是由我们建立的,我们可以设定任何值。 我们知道在这种情况下,发送的数据的大小非常小。
*/
const int maxMessageSize = 1024;
//received bits的缓冲区
var receivedDataBuffer = new ArraySegment<Byte>(new Byte[maxMessageSize]);
var cancellationToken = new CancellationToken();
//检查WebSocket状态
while (webSocket.State == WebSocketState.Open)
{
//读取数据
WebSocketReceiveResult webSocketReceiveResult = await webSocket.ReceiveAsync(receivedDataBuffer, cancellationToken);
//如果输入帧为取消帧,发送close命令
if (webSocketReceiveResult.MessageType == WebSocketMessageType.Close)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, String.Empty, cancellationToken);
}
else
{
byte[] payloadData = receivedDataBuffer.Array.Where(b => b != 0).ToArray();
//因为我们知道这是一个字符串,我们转换它
string receiveString = System.Text.Encoding.UTF8.GetString(payloadData, 0, payloadData.Length);
//将字符串转换为字节数组.
var newString = String.Format("Hello, " + receiveString + " ! Time {0}", DateTime.Now.ToString());
Byte[] bytes = System.Text.Encoding.UTF8.GetBytes(newString);
//发回数据
await webSocket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, cancellationToken);
}
}
}
客户端JS代码:
<!DOCTYPE html>
<html>
<head>
<title>websocket client</title>
<script type="text/javascript">
//**************zzt****************
var webSocket;
//HTTP处理程序的地址
var handlerUrl = "ws://localhost:48734/login/socket";
function doConnection() {
InitWebSocket();
}
function SendData() {
var input = document.getElementById('sendText');
//初始化WebSocket
InitWebSocket();
//如果WebSocket打开,发送数据
if (webSocket.OPEN && webSocket.readyState == 1) {
if (input.value != "")
webSocket.send(input.value);
}
//如果WebSocket关闭,显示消息
if (webSocket.readyState == 2 || webSocket.readyState == 3) {
alert("WebSocket关闭了,无法发送数据");
}
}
function CloseWebSocket() {
webSocket.close();
webSocket = undefined;
}
function InitWebSocket() {
//如果WebSocket对象未初始化,我们将初始化它
if (webSocket == undefined) {
webSocket = new WebSocket(handlerUrl);
//打开连接处理程序
webSocket.onopen = function () {
alert("WebSocket已连接");
};
//消息数据处理程序
webSocket.onmessage = function (e) {
alert(e.data);
};
//关闭事件处理程序
webSocket.onclose = function () {
alert("WebSocket closed.");
};
//错误事件处理程序
webSocket.onerror = function (e) {
alert(e.message);
};
}
else {
//webSocket.open();没有open方法
}
}
function doDeleteConnection(devid) {
//初始化WebSocket
InitWebSocket();
//如果WebSocket打开,发送数据
if (webSocket.OPEN && webSocket.readyState == 1) {
webSocket.send("DelConnection^" + devid);
}
//如果WebSocket关闭,显示消息
if (webSocket.readyState == 2 || webSocket.readyState == 3) {
//document.getElementById("di_span").innerText = "WebSocket关闭了,无法发送数据";
alert("WebSocket关闭了,无法发送数据");
}
}
//**************zztend*************
</script>
</head>
<body>
<form id="sendForm">
<input id="sendText" placeholder="Text to send" />
<button type="button" οnclick="doConnection();">连接</button>
<button type="button" οnclick="SendData();">发送</button>
<button type="button" οnclick="CloseWebSocket();">关闭</button>
</form>
</body>
</html>
到这里,基于web socket的全双工通信机制已经建立好了