IIS 8 以降で WebSocketがサポートされます。 今回は WebSocket を使用するサーバーサイドとクライアントサイドの簡単なサンプルプログラムを作成したので、覚書として記事に記載したいと思います。

検証環境は次の通りです

  • IIS 8, WIndows Server 2012
  • Visual Studio 2012 を使用して サーバーサイドのハンドラーを実装
  • クライアントのブラウザーは IE 10

1. WebSocket の機能をインストールする

IIS8 のサーバーの役割を追加したときに  WebSocket プロトコルの機能を含めていなかった場合、 WebSocketプロトコル の機能をセットアップする必要があります。WebSocketプロトコルをインストールするには下図のように サーバーの役割の追加ウィザードで Webサーバー >> アプリケーション開発 >> WebSocket プロトコル にチェックをして役割をインストールしてください。

2.WebSocket を処理するハンドラーの作成

Webアプリケーションプロジェクトを作成します。クライアントの WebSocket からの通信を処理するハンドラーを作成します。今回は クライアントから渡された文字列をそのままクライアントに返すカスタムハンドラーを実装してみます。 ソリューションエクスプローラーからプロジェクトを選択して右クリック -> 追加 -> 新しい項目 を選択してカスタムハンドラー (ashx) クラスを追加します。本例では EchoHandler.ashx という名前でハンドラーを作成します。

カスタムハンドラーのコードビハンドファイルで実装したサンプルコードを以下に記載します。サンプルコードでは、 WebSocket からのリクエストの場合に AcceptWebSocketRequest メソッドを呼び出して EchoWebSocket ハンドラで WebSocket との通信の処理を行うようにしています。EchoWebSocket では クライアントの WebSocket からの通信を待機して メッセージを受信したらそのままそのメッセージをWebSocket.SendAsyncメソッドを使用して クライアント側に返却するようにしています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Threading.Tasks;
using System.Web;
using System.Web.WebSockets;

namespace WebApi
{
    /// <summary>
    /// EchoHandler の概要の説明です
    /// </summary>
    public class EchoHandler : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            if (context.IsWebSocketRequest)
            {
                // WebSocket の処理を EchoWebSocket で実施
                context.AcceptWebSocketRequest(EchoWebSocket);
            }
        }

        public async Task EchoWebSocket(AspNetWebSocketContext context)
        {
            WebSocket socket = context.WebSocket;
            while (true)
            {
                ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[1024]);
                WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, System.Threading.CancellationToken.None);

                if (socket.State == WebSocketState.Open)
                {
                    string message = System.Text.Encoding.UTF8.GetString(buffer.Array, 0, result.Count);
                    string returnMessage = string.Format("Echo Reply:{0}  Date:{1}", message, DateTime.Now);

                    ArraySegment<byte> returnBuffer = new ArraySegment<byte>(System.Text.Encoding.UTF8.GetBytes(message));

                    await socket.SendAsync(returnBuffer, WebSocketMessageType.Text, true, System.Threading.CancellationToken.None);

                }
                else
                {
                    break;
                }
            }
            
        }


        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

3.WebSocketクライアントの作成

サーバーとwebsocket で通信するためのクライアント (html) を作成してみます。Webアプリケーションプロジェクトに新しく html ファイルを追加します。本例では EchoClient.html という名前でhtmlファイルを1つ追加しました。htmlではWebSocketオブジェクトを作成します。引数に 作成したカスタムハンドラーの URL を設定します。あとはonopen, onclose, onmessage メソッドを実装して サーバーと通信するようにしています。onmessage はサーバーからメッセージが通知されたときに呼び出されるコールバックfunctionです。 send というjavascriptのfunctionを使用して サーバーにフォーム画面に入力した文字列を送信するようにしています。

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>TEST</title>
    <script type="text/javascript">
        var socket;
        var logid = "log";
        function appendLog(msg){
            var v =  document.getElementById(logid).value;
            document.getElementById(logid).value = v + msg + "\n";
        }
        function init() {
            var host = "ws://localhost:7269/EchoHandler.ashx"

            try{
                socket = new WebSocket(host);


                socket.onopen = function(){
                    appendLog("socket onopen");
                };
                socket.onclose = function(){
                    appendLog("socket onclose");
                };
                socket.onmessage = function(msg){
                    appendLog("socket onmessage");
                    appendLog(msg.data);
                };
            } catch (ex) {
                alert(ex);
            }
        }

        function send() {
            var message = document.getElementById("message").value;
            socket.send(message);
        }
        init();
    </script>
</head>
<body>
    <input type="text" id="message" /><input type="button" onclick="send()" value="送信" /><br />
    <textarea id="log" rows="10" cols="40"></textarea>
</body>
</html>

html自身は非常にシンプルな構造になっていると思います。

ファイルの準備ができたので、ビルドを行ってエラーが発生しないことを確認してください。

プロジェクトデバッグ実行して動作確認してみます。 テキストボックスにテキストを入力して 送信ボタンをクリックすると 下図のように サーバーに送信したメッセージがそのまま クライアントに通知されて テキストエリアに受信したメッセージが追加sれます。

4.まとめ

簡単ですが説明は以上です。間違いや指摘点などがあればご連絡ください。 また、websocket は使用しているブラウザーによってはサポートされていない場合があることに気を付ける必要があります。 ASP.NET の場合は SignalR を使用すれば Websocket の機能を活用しつつWeSocketがサポートしていないブラウザーでも同じような処理を透過的に実装することができるようになると思います。