65.9K
CodeProject 正在变化。 阅读更多。
Home

在 .NET 4.5 中使用 WebSocket (第 2 部分)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (18投票s)

2013 年 7 月 9 日

CPOL

2分钟阅读

viewsIcon

230775

downloadIcon

15343

在传统的 ASP.NET 和 MVC 4 中使用 WebSocket

引言

第 1 部分 概述了 WebSocket 协议和 .NET WebSocket 支持。

在本文中,我将演示如何使用 HttpContext.AcceptWebSocketRequest 在传统的 ASP.NETMVC 4 Web 应用程序中托管 WebSocket 服务器。我将展示客户端 Web 页面中的 JavaScript 代码。我还将展示一个客户端应用程序,该应用程序使用 System.Net.WebSockets 命名空间中的 ClientWebSocket 类与同一服务器进行通信。

准备工作

要在服务器端启用 WebSocket,请参考 第 1 部分

传统的 ASP.NET

为了托管 WebSocket 服务器,我创建了一个自定义 HTTP 处理程序来接受客户端 WebSocket 连接请求并与客户端通信。

public class WSHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        if (context.IsWebSocketRequest)
        {
            context.AcceptWebSocketRequest(ProcessWSChat);
        }
    }

    public bool IsReusable { get { return false; } }

    private async Task ProcessWSChat(AspNetWebSocketContext context)
    {
        WebSocket socket = context.WebSocket;
        while (true)
        {
            ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[1024]);
            WebSocketReceiveResult result = await socket.ReceiveAsync(
                buffer, CancellationToken.None);
            if (socket.State == WebSocketState.Open)
            {
                string userMessage = Encoding.UTF8.GetString(
                    buffer.Array, 0, result.Count);
                userMessage = "You sent: " + userMessage + " at " + 
                    DateTime.Now.ToLongTimeString();
                buffer = new ArraySegment<byte>(
                    Encoding.UTF8.GetBytes(userMessage));
                await socket.SendAsync(
                    buffer, WebSocketMessageType.Text, true, CancellationToken.None);
            }
            else
            {
                break;
            }
        }
    }
}

ProcessRequest 方法中,我检查 HttpContext.IsWebSocketRequest 是否为 trueTrue 表示该请求是一个 AspNetWebSocket (System.Web.WebSockets) 请求。然后,我调用 HttpContext.AcceptWebSocketRequest 来接受请求并建立连接。我传入一个方法作为参数。该方法包含通过 AspNetWebSocketContext.WebSocket 进行双工通信的代码。在这里,我只是回显客户端发送到服务器的内容。

不要忘记在 web.config 中注册 HTTP 处理程序

    <system.webServer> 
        <handlers> 
          <add path="/WSChat/WSHandler.ashx" verb="*" name="WSHandler"
               type="WSChat.WSHandler"/>
        </handlers> 
    </system.webServer> 

在网页中,我编写 JavaScript 代码与服务器通信

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>WebSocket Chat</title>
    <script type="text/javascript" src="Scripts/jquery-2.0.2.js"></script>
    <script type="text/javascript">
        var ws;
        $().ready(function () {
            $("#btnConnect").click(function () {
                $("#spanStatus").text("connecting");
                ws = new WebSocket("ws://" + window.location.hostname +
                    "/WSChat/WSHandler.ashx");
                ws.onopen = function () {
                    $("#spanStatus").text("connected");
                };
                ws.onmessage = function (evt) {
                    $("#spanStatus").text(evt.data);
                };
                ws.onerror = function (evt) {
                    $("#spanStatus").text(evt.message);
                };
                ws.onclose = function () {
                    $("#spanStatus").text("disconnected");
                };
            });
            $("#btnSend").click(function () {
                if (ws.readyState == WebSocket.OPEN) {
                    ws.send($("#textInput").val());
                }
                else {
                    $("#spanStatus").text("Connection is closed");
                }
            });
            $("#btnDisconnect").click(function () {
                ws.close();
            });
        });
    </script>
</head>
<body>
    <input type="button" value="Connect" id="btnConnect" />
    <input type="button" value="Disconnect" id="btnDisconnect" /><br />
    <input type="text" id="textInput" />
    <input type="button" value="Send" id="btnSend" /><br />
    <span id="spanStatus">(display)</span>
</body>
</html>

独立客户端应用程序的代码如下所示

class Program
{
    private static async Task ChatWithServer()
    {
        using (ClientWebSocket ws = new ClientWebSocket())
        {
            Uri serverUri = new Uri("ws:///WSChat/WSHandler.ashx");
            await ws.ConnectAsync(serverUri, CancellationToken.None);
            while (true)
            {
                Console.Write("Input message ('exit' to exit): ");
                string msg = Console.ReadLine();
                if (msg == "exit")
                {
                    break;
                }
                ArraySegment<byte> bytesToSend = new ArraySegment<byte>(
                    Encoding.UTF8.GetBytes(msg));
                await ws.SendAsync(
                    bytesToSend, WebSocketMessageType.Text, 
                    true, CancellationToken.None);
                ArraySegment<byte> bytesReceived = new ArraySegment<byte>(new byte[1024]);
                WebSocketReceiveResult result = await ws.ReceiveAsync(
                    bytesReceived, CancellationToken.None);
                Console.WriteLine(Encoding.UTF8.GetString(
                    bytesReceived.Array, 0, result.Count));
                if (ws.State != WebSocketState.Open)
                {
                    break;
                }
            }
        }
    }

    static void Main(string[] args)
    {
        Task t = ChatWithServer();
        t.Wait();
    }
}  

ClientWebSocket (System.Net.WebSockets) 扮演与 IIS 中托管的 WebSocket 服务器通信的角色。ClientWebSocketAspNetWebSocket 都继承自 System.Net.WebSockets.WebSocket。因此,用法是一致的。请注意,客户端应用程序只能在 Windows 8Windows Server 2012 及以上版本上运行。

MVC 4

我在一个 MVC 4 Web 应用程序中实现了相同的回显功能。我创建了一个 ApiController 来处理 WebSocket 请求。在 Get 方法中,我仍然使用 HttpContext.AcceptWebSocketRequest 来接受和建立连接

public class WSChatController : ApiController
{
    public HttpResponseMessage Get()
    {
        if (HttpContext.Current.IsWebSocketRequest)
        {
            HttpContext.Current.AcceptWebSocketRequest(ProcessWSChat);
        }
        return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols);
    }
    private async Task ProcessWSChat(AspNetWebSocketContext context)
    {
        WebSocket socket = context.WebSocket;
        while (true)
        {
            ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[1024]);
            WebSocketReceiveResult result = await socket.ReceiveAsync(
                buffer, CancellationToken.None);
            if (socket.State == WebSocketState.Open)
            {
                string userMessage = Encoding.UTF8.GetString(
                    buffer.Array, 0, result.Count);
                userMessage = "You sent: " + userMessage + " at " + 
                    DateTime.Now.ToLongTimeString();
                buffer = new ArraySegment<byte>(
                    Encoding.UTF8.GetBytes(userMessage));
                await socket.SendAsync(
                    buffer, WebSocketMessageType.Text, true, CancellationToken.None);
            }
            else
            {
                break;
            }
        }
    }
} 

我的 index.cshtml 中的 JavaScript 代码与我之前展示的示例几乎相同。唯一的区别是用于发送 WebSocket 连接请求的 URI。它遵循 MVC URI 风格

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
    <script type="text/javascript" src="Scripts/jquery-2.0.2.js"></script>
    <script type="text/javascript">
        var ws;
        $().ready(function () {
            $("#btnConnect").click(function () {
                $("#spanStatus").text("connecting");
                ws = new WebSocket("ws://" + window.location.hostname + 
                    "/Mvc4WSChat/api/WSChat");
                ws.onopen = function () {
                    $("#spanStatus").text("connected");
                };
                ws.onmessage = function (evt) {
                    $("#spanStatus").text(evt.data);
                };
                ws.onerror = function (evt) {
                    $("#spanStatus").text(evt.message);
                };
                ws.onclose = function () {
                    $("#spanStatus").text("disconnected");
                };
            });
            $("#btnSend").click(function () {
                if (ws.readyState == WebSocket.OPEN) {
                    ws.send($("#textInput").val());
                }
                else {
                    $("#spanStatus").text("Connection is closed");
                }
            });
            $("#btnDisconnect").click(function () {
                ws.close();
            });
        });
    </script>
</head>
<body>
    <input type="button" value="Connect" id="btnConnect" />
    <input type="button" value="Disconnect" id="btnDisconnect" /><br />
    <input type="text" id="textInput" />
    <input type="button" value="Send" id="btnSend" /><br />
    <span id="spanStatus">(display)</span>
</body>
</html>

摘要

接下来在 第 3 部分 中,我将演示如何使用 WCF 托管 WebSocket 服务器,以及如何使用 WCF 客户端应用程序或 Web 页面中的 JavaScript 与 WebSocket WCF 服务进行通信。

相关链接

在 .NET 4.5 中使用 WebSocket

© . All rights reserved.