I've been hearing about HTML5 for quite some time now, and I especially liked the idea of web sockets. Giving us the ability to send data directly to the user, instead of doing something like polling with AJAX requests seems like a very neat idea.
So the other day, I decided that I would try to make a small server and test it out on my localhost. As far as I can tell, only Google Chrome supports web sockets at the moment, but I sure hope that it will pick up.
This screenshot is an example of how web sockets could be used (the code is in the attached zip file).
Naturally, I started the development with a Google search; this however didn't really help much. I found a Python and a PHP implementation, but not much about C#. This is my motivation for writing this article; I'll walk though some of the code for the server and a bit about how to interact with it from JavaScript.
Background
The server is heavily based on sockets. I don't think you would need to be very experienced in working with sockets, but a bit of knowledge shouldn't hurt. The code is not entirely simple, I'm using a small amount of delegates and lambda expressions, but again, it’s not very hard either. I guess you'll be fine.
Using the Code
The code is organized into a couple of classes in a class library, which could be included as a project in your solution or compiled into an assembly. It’s hard to create a ready-made demo application as you need a webserver, a browser and the web socket server to run simultaneously in order for it to work. But I'll try to explain how to get it all working. The project contains two main classes:
WebSocketServer
and WebSocketConnection
. The WebSocketServer
is responsible for handling all the client connections and the initial handshaking. The WebSocketConnection
handles the individual connections. The server listens for connections and creates the connection objects upon connection, and the connection objects are used to interact with the clients (sending/receiving data).
The following is an example of how you could start the server:
var wss = new WebSocketServer(8181,
"http://localhost:8080",
"ws://localhost:8181/service");
wss.Start();
The constructor takes three arguments:
public WebSocketServer(int port, string origin, string location)
- The port on which to listen for connections
- The origin of the connections (the location of the web page)
- The location of the server.
The last two arguments are used when "authenticating" the connections through the handshake between the server and the client. The inner workings of the protocol is described here: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75
To enable you to see what is happening in the server, a very rude logging mechanism is implemented: You can attach a
TextWriter
to the server, and specify the logging level with the ServerLogLevel
enum:wss.Logger = Console.Out;
wss.LogLevel =ServerLogLevel.Verbose;
There are three different log levels:
ServerLogLevel.Verbose //tells you just about everything
//(including all the data received)
ServerLogLevel.Subtle //tells you when clients connect/disconnect
//and when they are sending data to the server
ServerLogLevel.Nothing //well.. nothing.
An even better way keep an eye on what is going on, is to attach methods to the events that the server and the connections are invoking:
wss.ClientConnected += new ClientConnectedEventHandler(OnClientConnected);
///
static void OnClientConnected(WebSocketConnection sender, EventArgs e)
{
Console.WriteLine("client connected");
sender.Disconnected += new WebSocketDisconnectedEventHandler(OnDisconnected);
sender.DataReceived += new DataReceivedEventHandler(OnDataReceived);
}
static void OnDataReceived(WebSocketConnection sender, DataReceivedEventArgs e)
{
Console.WriteLine("data received");
}
static void OnDisconnected(WebSocketConnection sender, EventArgs e)
{
Console.WriteLine("client disconnected");
}
The "sender" in these methods is always the
WebSocketConnection
object associated with the connection. The only method that has data in the EventArgs
is the OnDataReceived
method, the EventArgs
(DataReceivedEventArgs
) in this method contains the size of the data and the data itself, these are accessible through properties on the object (Size
and Data
). The data transferred between the client and the server is UFT8 encoded strings (as specified in the protocol).
The above is pretty much the simplest usage example of the classes, but in order to get any clients to connect we need some JavaScript and a browser to run it. But before we continue to the JavaScript, I'd like to just attach a few extra words to the classes:
As I said, the
WebSocketSever
object listens for new clients and creates the WebSocketConnection
objects as new clients connect. The objects are kept in a List
, and this list is updated as the clients disconnect. The listener socket is accessible though the ListenerSocket
property, and the List
though the Connections
property. Apart from the Start
method, the server contains two public
methods; SendToAll
and SendToAllExceptOne
. These methods can be used to send data to all the connected clients, and to send data to all the connected clients except a specified client (this could be used in a scenario where a client wishes to send data to everybody else). These methods simply call the public Send
method on the WebSocketConnection
objects in the List
. This method sends a string to the client. The WebSocketConnection
has one more public function: Close
, this method just closes the connecting socket. The socket is available thought the ConnectionSocket
property.
The following is an outline of the
public
methods, properties and events of the two classes:WebSocketServer:
// an event that is triggered every time a client connects to the server
event ClientConnectedEventHandler ClientConnected;
// a property to access the list of WebSocketConnections of the server
List<WebSocketConnection> Connections;
// a property to access the socket that is listening for new connections
Socket ListenerSocket;
// a property that can be used to achieve simple logging
TextWriter Logger;
// a property that determines the level of verbose-ness of the logging
ServerLogLevel LogLevel;
// a property to access the port the sever is listening to
int Port;
// sends a string to all connected clients
void SendToAll(string str);
// sends a string to all connected clients except a specified client
void SendToAllExceptOne(string str, WebSocketConnection indifferent);
// starts the server
void Start();
WebSocketConnection:
// method that closes the connecting socket
void Close();
// property to access the connecting socket
Socket ConnectionSocket;
// event that is triggered whenever data is received
event DataReceivedEventHandler DataReceived;
// event that is triggered when the client disconnects
event WebSocketDisconnectedEventHandler Disconnected;
// a property to get a Guid for the client connection
Guid GUID;
// method that sends a string to the client
void Send();
So! After all that it is time to look at the JavaScript. A very simple client would look a bit like this:
I use JQuery and put the code below inside the
$(document).ready()
function, but I guess you could do it in a lot of ways.if('WebSocket' in window){
connect('ws://localhost:8181/service');
}
function connect(host) {
var ws = new WebSocket(host);
ws.onopen = function () {
alert('connected');
};
ws.onmessage = function (evt) {
alert('reveived data:'+evt.data);
};
ws.onclose = function () {
alert('socket closed');
};
};
0 comments:
Post a Comment