Thursday, January 14, 2016

WebSocket

Implementing WebSocket in ASP.Net is quite easy. You need 2 components:

1. The ASHX that handle the web socket communcation.
2. The client side Javascript which sends message to the server and waiting for server message.

The WSHandler.ashx page resides in "WSChat" folder

<%@ WebHandler Language="C#" Class="WSHandler" %>
using System;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
using System.Web.WebSockets;
using System.Net.WebSockets;
using System.Text;
using System.Collections.Generic;
using System.Linq;

//22.Dec.15,lhw-
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;

        //<<=======
        MyConnection cn = new MyConnection(socket);
        _conn_list.Add(cn);
        //<<=======

        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);


                if (userMessage.StartsWith("helo from"))
                {
                    // user has signed in with his ID.
                    cn.uid = userMessage.Substring("helo from".Length, userMessage.Length - "helo from".Length).TrimStart();
                }
                else if (userMessage.ToLower().StartsWith("b:"))
                {
                    // broadcast the message.
                    userMessage = "You sent: " + userMessage + " at " + DateTime.Now.ToLongTimeString();
                    await Broadcast(userMessage);
                }
                else
                {
                    // echo the message
                    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 if (socket.State == WebSocketState.CloseReceived)
            {
                // remove current connection from the memory
                var v = _conn_list.Where(n => n.sess_id == cn.sess_id).FirstOrDefault();
                if (v != null)
                {
                    _conn_list.Remove(v);
                }

                // inform everyone that the current user has left the chat.
                await Broadcast(cn.uid + " has signed out");
            }
            else
            {
                break;
            }
        }
    }

    //------------------------------------------------------------------------------
    async Task Broadcast(string msg)
    {
        ArraySegment<byte> buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(msg));

        foreach (var item in _conn_list)
        {
            await item.socket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
        }
    }

    //------------------------------------------------------------------------------
    public class MyConnection
    {

        public string sess_id { get; private set; }
        public DateTime connected_on { get; private set; }
        public WebSocket socket { get; set; }

        // value from the browser. The user must send 'helo from xxx' where 'xxx' is the user id.
        public string uid { get; set; }

        public List<string> chat_room_list { get; set; }

        public MyConnection(WebSocket sk)
        {
            this.sess_id = Guid.NewGuid().ToString();
            this.connected_on = DateTime.Now;
            this.chat_room_list = new List<string>();
            this.socket = sk;
        }
    }

    static List<MyConnection> _conn_list = new List<MyConnection>();

}

The client side JavaScript

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>WebSocket Chat</title>
    <script type="text/javascript" src="js/jquery-1.7.js"></script>
    <script type="text/javascript">
        var ws;
        $().ready(function () {
            $("#btnConnect").click(function () {
                $("#spanStatus").text("connecting");
                ws = new WebSocket("ws://" + window.location.hostname +
                    ":64495/WSChat/WSHandler.ashx");
                ws.onopen = function () {
                    $("#spanStatus").text("connected");
                    post_msg();
                };
                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();
            });

            var _timer = null;
            function post_msg() {
                _timer = window.setInterval(function () {
                    send_msg('helo from lau - ' + (new Date()).getSeconds().toString());
                }, 300);
            }
            function send_msg(s) {
                if (_timer != null) {
                    clearInterval(_timer);
                }

                if (ws.readyState == WebSocket.OPEN) {
                    ws.send(s);
                }
                else {
                    $("#spanStatus").text("Connection is closed");
                }
            }
        });
    </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>