ASP.NET Web API上实现 Web Socket

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. 浏览器,服务器支持情况

参考维基百科,目前浏览器的支持情况如下:

IEChromeFirefoxSafariOpera
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的全双工通信机制已经建立好了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值