在 .NET 4.5 中使用 WebSocket (第 2 部分)
在传统的 ASP.NET 和 MVC 4 中使用 WebSocket
引言
第 1 部分 概述了 WebSocket 协议和 .NET WebSocket 支持。
在本文中,我将演示如何使用 HttpContext.AcceptWebSocketRequest
在传统的 ASP.NET 或 MVC 4 Web 应用程序中托管 WebSocket 服务器。我将展示客户端 Web 页面中的 JavaScript 代码。我还将展示一个客户端应用程序,该应用程序使用 System.Net.WebSockets
命名空间中的 ClientWebSocket
类与同一服务器进行通信。
- WSChat.zip 包含传统 ASP.NET 的代码示例。
- Mvc4WSChat.zip 包含 MVC 4 的代码示例。
准备工作
要在服务器端启用 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
是否为 true
。True
表示该请求是一个 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 服务器通信的角色。ClientWebSocket
和 AspNetWebSocket
都继承自 System.Net.WebSockets.WebSocket
。因此,用法是一致的。请注意,客户端应用程序只能在 Windows 8、Windows 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