WebSocketを利用したアプリケーションを作成します。
事前準備
サーバーにWebSocketの機能をインストールします。今回サーバーはIIS (Internet Information Services) を利用します。インストールの手順は
こちらの記事を参照してください。
プログラム例:シンプルなリピーター
サーバーからリピートでメッセージが送信されるプログラムを作成します。
コード (サーバー側)
ジェネリックハンドラを作成し、下記のコードを実装します。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.WebSockets;
using System.Net.WebSockets;
namespace SimpleWebSocket
{
/// <summary>
/// RepeatHandler の概要の説明です
/// </summary>
public class RepeatHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.IsWebSocketRequest) {
context.AcceptWebSocketRequest(RepeatWebSocket);
}
}
public async Task RepeatWebSocket(AspNetWebSocketContext context)
{
WebSocket socket = context.WebSocket;
while (true) {
if (socket.State == WebSocketState.Open) {
string returnMessage = string.Format("{0}:やあ、こんにちは", DateTime.Now);
ArraySegment<byte> returnBuffer
= new ArraySegment<byte>(System.Text.Encoding.UTF8.GetBytes(returnMessage));
System.Threading.Thread.Sleep(3000);
await socket.SendAsync(returnBuffer, WebSocketMessageType.Text,
true, System.Threading.CancellationToken.None);
}
else {
break;
}
}
}
public bool IsReusable {
get {
return false;
}
}
}
}
解説
ProcessRequest
メソッドでは、下記のコードにより、AcceptWebSocketRequestメソッドを呼び出し、WebSocketの処理を実行します。第一引数にはWebSocketの処理を実行するTaskを与えます。
if (context.IsWebSocketRequest) {
context.AcceptWebSocketRequest(RepeatWebSocket);
}
AcceptWebSocketRequestからの呼び出しで処理されるメソッドが下記コードです。
asyncが記述されており、非同期のメソッドとして実行されます。メソッド内では whileループにより接続を維持したまま処理を実行します。RepeatWebSocket()メソッドを抜けるとWebSocketとの接続が閉じられ処理が完了します。
Whileループ内では
await socket.SendAsync()
メソッドを呼び出しクライアントにデータを送信します。SendAsyncメソッドの第一引数には返信するデータのバッファを与えます。第二引数にはデータのタイプを指定します。第三引数はバッファ内のデータがメッセージの最後になるかを指定します。今回は1回の送信で1つのメッセージを送るため、
true
とします。第4引数が操作を取り消す必要があることを示す通知トークンを指定します。今回はキャンセル動作はないため、
None
を与えます。
送信バッファは
ArraySegment<byte>
オブジェクトで作成します。
public async Task RepeatWebSocket(AspNetWebSocketContext context)
{
WebSocket socket = context.WebSocket;
while (true) {
if (socket.State == WebSocketState.Open) {
string returnMessage = string.Format("{0}:やあ、こんにちは", DateTime.Now);
ArraySegment<byte> returnBuffer
= new ArraySegment<byte>(System.Text.Encoding.UTF8.GetBytes(returnMessage));
System.Threading.Thread.Sleep(3000);
await socket.SendAsync(returnBuffer, WebSocketMessageType.Text,
true, System.Threading.CancellationToken.None);
}
else {
break;
}
}
}
WebSocketMessageType の値
名称 | 値の意味 |
Binary | バイナリーデータ |
Close | 接続が閉じられた |
Text | テキストデータ |
今回は、SendAsyncのため、与える値はTextかBinaryのどちらかになります。
コード (クライアント側)
クライアント側のHTMLファイルを作成します。下記のコードを記述します。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
var socket;
function appendInfo(msg) {
var textBox = document.getElementById("TextBoxReceive");
textBox.value = textBox.value + msg + "\n";
}
function init() {
var host = "ws://localhost:60524/RepeatHandler.ashx"
try {
socket = new WebSocket(host);
socket.onopen = function () {
appendInfo("socket onOpen イベントを受信");
};
socket.onclose = function () {
appendInfo("socket onclose イベントを受信");
};
socket.onmessage = function (msg) {
appendInfo("socket onmessage イベントを受信");
appendInfo(msg.data);
};
} catch (ex) {
alert(ex);
}
}
</script>
</head>
<body onload="init();">
<textarea id="TextBoxReceive" rows="20" cols="80"></textarea>
</body>
</html>
解説
ページの表示時に
onload
イベントが実行され
init
関数が呼び出されます。
init関数は下記のコードとなっています。
WebSocketオブジェクトを作成します。コンストラクタに接続するURLを与えます。WebSocketのURLのため、プロトコルが
ws://
になります。
WebSocketのイベントにイベントハンドラを設定して、受信時や接続時の処理を実装します。
今回の例では、ソケットが開かれたタイミング、ソケットが閉じられたタイミング、メッセージを受信したタイミングで画面にメッセージを表示します。また、メッセージ受信時は受信したメッセージも表示します。
function init() {
var host = "ws://localhost:60524/RepeatHandler.ashx"
try {
socket = new WebSocket(host);
socket.onopen = function () {
appendInfo("socket onOpen イベントを受信");
};
socket.onclose = function () {
appendInfo("socket onclose イベントを受信");
};
socket.onmessage = function (msg) {
appendInfo("socket onmessage イベントを受信");
appendInfo(msg.data);
};
} catch (ex) {
alert(ex);
}
}
実行結果
プロジェクトを実行します。Webブラウザが起動し下図の画面が表示されます。
"socket onOpen"のメッセージが表示され、サーバーとWebSocketの接続ができたメッセージが表示されます。その後、約3秒ごとに "socket onmessage イベントを受信" と 「やあ、こんにちは」のメッセージがテキストエリアに表示されます。
3秒に1回のペースでメッセージが送られ続けます。
WebSocketを利用したメッセージ受信プログラムができました。
プログラム例:双方向でやり取りをするリピーター
続いて、クライアント側からの送信により、サーバー側で切断処理をするリピーターを作成します。
コード (サーバー側)
ジェネリックハンドラを作成し、下記のコードを実装します。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using System.Web.WebSockets;
using System.Net.WebSockets;
namespace SimpleWebSocket
{
/// <summary>
/// RepeatCloseHandler の概要の説明です
/// </summary>
public class RepeatCloseHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.IsWebSocketRequest) {
context.AcceptWebSocketRequest(RepeatCloseWebSocket);
}
}
public bool IsReusable
{
get
{
return false;
}
}
public async Task RepeatCloseWebSocket(AspNetWebSocketContext context)
{
WebSocket socket = context.WebSocket;
while (true) {
if (socket.State == WebSocketState.Open) {
string returnMessage = string.Format("{0}:やあ、こんにちは", DateTime.Now);
ArraySegment<byte> returnBuffer = new ArraySegment<byte>(System.Text.Encoding.UTF8.GetBytes(returnMessage));
await socket.SendAsync(returnBuffer, WebSocketMessageType.Text, true, System.Threading.CancellationToken.None);
ArraySegment<byte> receiveBuffer = new ArraySegment<byte>(new byte[1024]);
WebSocketReceiveResult result = await socket.ReceiveAsync(receiveBuffer, System.Threading.CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Text) {
byte[] receive_byte = receiveBuffer.Take(result.Count).ToArray();
string receiveText = System.Text.Encoding.UTF8.GetString(receive_byte);
if (receiveText[0] == 'C') {
break;
}
}
}
else {
break;
}
}
}
}
}
解説
基本の動作は先のプログラムと同様ですが、
System.Threading.Thread.Sleep(3000);
せずに、
socket.ReceiveAsync()
メソッドを呼び出しクライアントとからのメッセージを待つ動作になります。
クライアントからのレスポンスを受け取るコードが下記です。
ReceiveAsync()
メソッドでクライアントからのメッセージを待ちます。ReceiveAsyncメソッドは非同期メソッドですが、awaitキーワードを利用して呼び出しているため、レスポンスが戻るまで以降の行は実行されず、処理が停止した状態になります。クライアントからのメッセージを受信すると以降の行が実行されます。
MessageTypeを判定し、テキストのメッセージで先頭の一文字目が'C'であれば、break文でwhileループを抜け接続を終了します。メッセージの一文字目が'C'でない場合は処理を続行し、再度クライアントにメッセージを送信します。
ArraySegment<byte> receiveBuffer = new ArraySegment<byte>(new byte[1024]);
WebSocketReceiveResult result = await socket.ReceiveAsync(receiveBuffer, System.Threading.CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Text) {
byte[] receive_byte = receiveBuffer.Take(result.Count).ToArray();
string receiveText = System.Text.Encoding.UTF8.GetString(receive_byte);
if (receiveText[0] == 'C') {
break;
}
}
コード (クライアント側)
クライアント側のHTMLファイルを作成します。下記のコードを記述します。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
var socket;
function appendInfo(msg) {
var textBox = document.getElementById("TextBoxReceive");
textBox.value = textBox.value + msg + "\n";
}
function init() {
var host = "ws://localhost:60524/RepeatCloseHandler.ashx"
try {
socket = new WebSocket(host);
socket.onopen = function () {
appendInfo("socket onOpen イベントを受信");
};
socket.onclose = function () {
appendInfo("socket onclose イベントを受信");
};
socket.onmessage = function (msg) {
appendInfo("socket onmessage イベントを受信");
appendInfo(msg.data);
};
} catch (ex) {
alert(ex);
}
}
function ButtonSend_Click() {
var message = document.getElementById("TextBoxSend").value;
socket.send(message);
}
</script>
</head>
<body onload="init();">
<textarea id="TextBoxReceive" rows="20" cols="80"></textarea><br />
<input id="TextBoxSend" type="text" /><input id="ButtonSend" type="button" value="送信" onclick="ButtonSend_Click();" />
</body>
</html>
解説
先のプログラムと同様、ページの表示時に
onload
イベントが実行され
init
関数が呼び出されます。init関数の処理は先のプログラムと同様で、ソケットが開いたとき、閉じたとき、メッセージを受信したときにメッセージをテキストエリアに表示する動作です。
ボタンをクリックしたときのイベントハンドラは下記になります。テキストボックスに入力された文字列を取得し、sendメソッドを利用して送信します。
function ButtonSend_Click() {
var message = document.getElementById("TextBoxSend").value;
socket.send(message);
}
実行結果
プロジェクトを実行します。Webブラウザが表示されます。クライアントのHTML(RepeatCloseClient.html)を開きます。
WebSocketの接続メッセージとサーバーからのメッセージが表示されます。今回のプログラムでは1回メッセージが表示されるとクライアントからの受信待ちとなります。
画面下部のテキストボックスに"ABC"の文字列を入力し、[送信]ボタンをクリックします。
1文字目が'C'でないメッセージのため、切断されず再度メッセージが受信されテキストエリアに表示されます。テキストボックスに"DEF"の文字列を入力し、[送信]ボタンをクリックします。
メッセージが表示され続けます。
テキストボックスに"C"の文字列を入力し、[送信]ボタンをクリックします。
サーバー側から接続が閉じられ、WebSocketが閉じられた旨のメッセージがテキストエリアに表示されます。
WebSocketを利用したシンプルなプログラムを作成できました。
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2024-01-06
作成日: 2018-03-01