Using WebSocket in .NET 4.5 (Part 2)





5.00/5 (18 votes)
Using WebSocket in traditional ASP.NET and MVC 4
Introduction
Part 1 gives an overview to WebSocket protocol and .NET WebSocket support.
In
this article, I will demonstrate how to host a WebSocket server in a
traditional ASP.NET or MVC 4 web application using
HttpContext.AcceptWebSocketRequest
. I will show JavaScript code in client-side
web page. I will also show a client-side application to communicate with the
same server using the ClientWebSocket
class in the System.Net.WebSockets
namespace.
- WSChat.zip contains the code sample for traditional ASP.NET.
- Mvc4WSChat.zip contains the code sample for MVC 4.
Preparation
To enable WebSocket on the server side, please refer to Part 1.
Traditional ASP.NET
To host a WebSocket server, I create a custom HTTP handler to accept client WebSocket connection requests and communicate with the client.
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;
}
}
}
}
In the ProcessRequest
method, I check whether
HttpContext.IsWebSocketRequest
is true
. True
means the request is an AspNetWebSocket
(System.Web.WebSockets
) request. Then, I call HttpContext.AcceptWebSocketRequest
to accept the request and establish the connection. I pass in a method as the
argument. This method contains code to do the duplex communication through AspNetWebSocketContext.WebSocket
.
Here, I just echo what the client has sent to the server.
Don’t forget to register the HTTP handler in web.config:
<system.webServer>
<handlers>
<add path="/WSChat/WSHandler.ashx" verb="*" name="WSHandler"
type="WSChat.WSHandler"/>
</handlers>
</system.webServer>
In the web page, I write JavaScript code to communicate with the server:
<!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>
The code of the standalone client application looks like:
class Program
{
private static async Task ChatWithServer()
{
using (ClientWebSocket ws = new ClientWebSocket())
{
Uri serverUri = new Uri("ws://localhost/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
) plays
the role to communicate with the WebSocket server hosted in IIS. Both
ClientWebSocket
and AspNetWebSocket
are inherited from System.Net.WebSockets.WebSocket
.
So the usage is consistent. Note that the client application can only work on Windows 8, Windows Server 2012 and above.
MVC 4
I
implement the same echo function in a MVC 4 web application. I create an
ApiController
to handle WebSocket requests. In the Get
method, I still use HttpContext.AcceptWebSocketRequest
to accept and establish connections:
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;
}
}
}
}
The JavaScript code in my index.cshtml is almost the same as the example I have shown before. The only difference is the URI used to send WebSocket connection request. It follows the MVC URI style:
@{
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>
Summary
Next in Part 3, I will demonstrate how to use WCF to host WebSocket server and how to communicate WebSocket WCF service using WCF client application or JavaScript in a web page.
Related Links
Using WebSocket in .NET 4.5