WebSocketでブラウザにプッシュ通知するサーバをC#で実装

# はじめに
* C#を使用してWebSocketサーバを実装しクライアントであるブラウザにプッシュ通知を行うのを試しました
* IISは使用せずSystem.Net.WebSocketsを使って自前で実装を行っています
* [MSDNの記事](https://msdn.microsoft.com/ja-jp/magazine/jj863133.aspx?f=255&MSPPError=-2147217396)を参考に実装しました

# サンプルのイメージ
* サーバ側からWebSocketで値がとんできたらポップアップ表示する

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310068/39e04dc0-1904-441a-d0c5-76678efcafdf.png" width="600">

# サーバ側の実装

* 全体はこんな感じです
* 以下で詳細を説明します

```C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WebSocketServerSample02
{
    class Program
    {
        static void Main(string[] args)
        {
            Run();
            Console.ReadKey();
        }

        static async Task Run()
        {
            HttpListener s = new HttpListener();
            s.Prefixes.Add("http://IPアドレス:ポート番号/");
            s.Start();
            Console.WriteLine("サーバ起動");

            var hc = await s.GetContextAsync();
            if (!hc.Request.IsWebSocketRequest)
            {
                hc.Response.StatusCode = 400;
                hc.Response.Close();
                return;
            }
            var wsc = await hc.AcceptWebSocketAsync(null);
            var ws = wsc.WebSocket;
            for (int i = 0; i != 5; ++i)
            {
                await Task.Delay(2000);
                var response = "push_event " + DateTime.Now.ToLongTimeString();
                var buffer = Encoding.UTF8.GetBytes(response);
                var segment = new ArraySegment<byte>(buffer);
                await ws.SendAsync(segment, WebSocketMessageType.Text, true, CancellationToken.None);
            }
            await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Done", CancellationToken.None);
        }
    }
}
```

## WebSocketの通信を受付してるところ
```C#
            HttpListener s = new HttpListener();
            s.Prefixes.Add("http://IPアドレス:ポート番号/");
            s.Start();
            Console.WriteLine("サーバ起動");

            var hc = await s.GetContextAsync();
            if (!hc.Request.IsWebSocketRequest)
            {
                hc.Response.StatusCode = 400;
                hc.Response.Close();
                return;
            }
            var wsc = await hc.AcceptWebSocketAsync(null);
```

* HttpListenerを使用してHTTPサーバとしての処理を起動
* GetContextAsync()でHttpListenerでのWebSocket接続の受信要求を待ち状態にする
* Request.IsWebSocketRequestを参照してWebSocket接続でない場合はステータスコード400を返却
* AcceptWebSocketAsync(null)でWebSocket接続を受入可能状態にする


## 定期的にクライアントに通信しているところ 
```C#
            var ws = wsc.WebSocket;
            for (int i = 0; i != 5; ++i)
            {
                await Task.Delay(2000);
                var response = "push_event " + DateTime.Now.ToLongTimeString();
                var buffer = Encoding.UTF8.GetBytes(response);
                var segment = new ArraySegment<byte>(buffer);
                await ws.SendAsync(segment, WebSocketMessageType.Text, true, CancellationToken.None);
            }
            await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Done", CancellationToken.None);
```

* 「push_event 現在時刻」をクライアント側に送信する値をresponseに格納
* SendAsyncでクライアントに送信


# クライアント側の実装
* サーバ側からの通信が来た場合、webSocket.onmessageに記載された処理が動作

```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocketClient</title>
<script src="js/jquery-1.11.3.js"></script>
<script type="text/javascript">
	var webSocket;
	window.onload = function() {
		url = "http://IPアドレス:ポート番号/";
		webSocket = new WebSocket(url.replace("http://", "ws://").replace("https://", "wss://"));
		var messageArea = document.getElementById("messageArea");
		var appendMessage = function(value, color) {
			var messageElement = document.createElement("div");
			messageElement.style.color = color;
			messageElement.innerText = value;
			messageArea.insertBefore(messageElement, messageArea.firstChild);
		}
		webSocket.onopen = function() {
			appendMessage("Opened", "blue");
		}
		webSocket.onclose = function() {
			appendMessage("Closed", "red");
		}
		webSocket.onmessage = function(message) {
			var data = message.data;
			alert(data);
			appendMessage(data, "black");
		}
		webSocket.onerror = function(message) {
			appendMessage(message, "red");
		}
	}
</script>
</head>
<body>
    <div id="messageArea"></div>
</body>
</html
```