NOTE: WebSockets on IIS only works on Windows 8. Windows 7 does not have the necessary websocket DLL that is needed by IIS. :( That wrinkle aside, WebSockets will work with IIS 8 Regular or Express editions. This demo used IIS Express.
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<script type="text/javascript">
var connectButton,
disconnectButton,
messageInput,
sendButton,
responseDiv,
uriSpan,
uri,
webSocket;
var connect = function () {
connectButton.disabled = true;
disconnectButton.disabled = false;
sendButton.disabled = false;
webSocket = new WebSocket(uri);
webSocket.onmessage = function (e) {
responseDiv.innerHTML +=
'<div>' + e.data + '</div>';
};
webSocket.onopen = function (e) {
responseDiv.innerHTML +=
'<div>Connecting...</div>';
};
webSocket.onclose = function (e) {
responseDiv.innerHTML +=
'<div>Disconnected.</div>';
};
webSocket.onerror = function (e) {
responseDiv.innerHTML += '<div>Error</div>'
};
};
var disconnect = function () {
connectButton.disabled = false;
disconnectButton.disabled = true;
sendButton.disabled = true;
webSocket.close();
};
var sendMessage = function () {
var message = messageInput.value;
webSocket.send(message);
messageInput.value = '';
};
var setup = function () {
connectButton = document.getElementById('connect');
disconnectButton =
document.getElementById('disconnect');
messageInput = document.getElementById('message');
responseDiv = document.getElementById('responseLog');
sendButton = document.getElementById('sendMessage');
uriSpan = document.getElementById('uri');
uri = 'ws://localhost:52618/api/websocket';
uriSpan.innerHTML = uri;
};
</script>
</head>
<body onload="setup()" style="font-family: sans-serif;">
<div>
<div>
<span id="uri"></span>
<button id="connect" onclick="connect()">
Connect
</button>
<button id="disconnect"
disabled="disabled"
onclick="disconnect()">Disconnect</button>
</div>
<label for="message">Message</label>
<input id="message"/>
<button id="sendMessage"
onclick="sendMessage()"
disabled="disabled">Send</button>
<hr />
<label for="responseLog">Response</label>
<div id="responseLog"
style="border: 1px solid grey;
width: 600px; height: 400px;
overflow: auto;
font-family: monospace;">
</div>
</div>
</body>
</html>
First, you must create a new Web API project. In this example, the project is named (with great flare and creativity) "Project1". Then, you need to add the Microsoft.WebSockets package from NuGet. (Right-click the project, click Manage NuGet Packages..., search field in upper right: microsoft.websockets, select the Microsoft.WebSockets package and click Install)
First create your WebSocket Service class, and put it somewhere in the project (mine is in /Controllers):
using Microsoft.Web.WebSockets;
using System;
namespace Project1.Controllers
{
public class ChatClient : WebSocketHandler
{
public readonly Guid ConnectionId = Guid.NewGuid();
private static WebSocketCollection chatClients =
new WebSocketCollection();
public override void OnOpen()
{
chatClients.Add(this);
chatClients.Broadcast(
"Client joined: " + ConnectionId.ToString()
);
}
public override void OnClose()
{
chatClients.Broadcast(
"Client left: " + ConnectionId.ToString()
);
chatClients.Remove(this);
}
public override void OnMessage(string message)
{
chatClients.Broadcast(
ConnectionId.ToString() + " said: " + message
);
}
}
}
Note I'm using Guid as a connection id, and the messages end up looking pretty ugly: 0195093f-70a5-4bfe-b707-8ac96ba94c31 said: test. But you can change that for your own needs.
The next step is to setup an ApiController. This is necessary to upgrade the HTTP request to a WebSocket request.
using Microsoft.Web.WebSockets;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
namespace Project1.Controllers
{
public class WebSocketController : ApiController
{
public HttpResponseMessage Get()
{
HttpContext.Current.AcceptWebSocketRequest(
new ChatClient()
);
return new HttpResponseMessage(
HttpStatusCode.SwitchingProtocols
);
}
}
}
As noted in another example I found, the first using statement is VERY IMPORTANT. It adds the AcceptWebSocketRequest overload that is needed for this code. The other overloads are lower level than I wanted.
That's it! But wait you say, how can I test it? Ok, here ya go. This is a simple html page I created to test the application. It doesn't use any external files (not even jQuery). You can replace the contents of Views/Home/Index.cshtml in the project with this:
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<script type="text/javascript">
var connectButton,
disconnectButton,
messageInput,
sendButton,
responseDiv,
uriSpan,
uri,
webSocket;
var connect = function () {
connectButton.disabled = true;
disconnectButton.disabled = false;
sendButton.disabled = false;
webSocket = new WebSocket(uri);
webSocket.onmessage = function (e) {
responseDiv.innerHTML +=
'<div>' + e.data + '</div>';
};
webSocket.onopen = function (e) {
responseDiv.innerHTML +=
'<div>Connecting...</div>';
};
webSocket.onclose = function (e) {
responseDiv.innerHTML +=
'<div>Disconnected.</div>';
};
webSocket.onerror = function (e) {
responseDiv.innerHTML += '<div>Error</div>'
};
};
var disconnect = function () {
connectButton.disabled = false;
disconnectButton.disabled = true;
sendButton.disabled = true;
webSocket.close();
};
var sendMessage = function () {
var message = messageInput.value;
webSocket.send(message);
messageInput.value = '';
};
var setup = function () {
connectButton = document.getElementById('connect');
disconnectButton =
document.getElementById('disconnect');
messageInput = document.getElementById('message');
responseDiv = document.getElementById('responseLog');
sendButton = document.getElementById('sendMessage');
uriSpan = document.getElementById('uri');
uri = 'ws://localhost:52618/api/websocket';
uriSpan.innerHTML = uri;
};
</script>
</head>
<body onload="setup()" style="font-family: sans-serif;">
<div>
<div>
<span id="uri"></span>
<button id="connect" onclick="connect()">
Connect
</button>
<button id="disconnect"
disabled="disabled"
onclick="disconnect()">Disconnect</button>
</div>
<label for="message">Message</label>
<input id="message"/>
<button id="sendMessage"
onclick="sendMessage()"
disabled="disabled">Send</button>
<hr />
<label for="responseLog">Response</label>
<div id="responseLog"
style="border: 1px solid grey;
width: 600px; height: 400px;
overflow: auto;
font-family: monospace;">
</div>
</div>
</body>
</html>
NOTE: Change the uri value to match the port your project uses. Otherwise it should work as is.
And here was my test run in Chrome 21 and IE 10.
1 comment:
Thank you. This post was a huge help with my problem. The connections kept closing immediately upon connecting. It turns out that if we leave out onMessage method from c# code, connections get closed without error. Even if I have an empty onMessage method, it works properly.
Post a Comment