引き続きWebSocket。
クライアント側のサーバーから切断されてしまった時の再接続機能をさくっと作ってみた。
基本的にはクローズイベントかエラー発生のイベントで再接続をすれば良い。ただ、APIによって仕様が違ったりする。将来的には実装されるAPIまで標準化されることでしょう。
ここでは画面上にサーバーが送信してくる時刻を常に表示し続けるクライアントを作成します。
画面
C# – WCF WebSockets版
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.ServiceModel.WebSockets; namespace WsForm_WCF { public partial class Form1 : Form { /// <summary> /// WebSocketsインスタンス /// </summary> private WebSocket _ws = null; /// <summary> /// リトライカウンタ /// </summary> private int _retryCount = 0; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { /// サーバー接続開始 Connect(); } /// <summary> /// サーバーへ接続する /// </summary> private void Connect() { /// listBoxへ文字列を挿入する。 Action<string> AddText = (s) => { listBox1.Items.Add(s); listBox1.TopIndex = listBox1.Items.Count - 1; }; if (_ws == null) { AddText("サーバー接続を開始します。"); } _ws = new WebSocket("ws://192.168.1.2:12345"); /// 文字列受信 _ws.OnData += (s, e) => { AddText(e.TextData); }; /// サーバー接続完了 _ws.OnOpen += (s, e) => { _retryCount = 0; AddText("サーバーに接続しました。"); }; /// 接続断の発生 _ws.OnClose += (s, e) => { AddText("サーバー接続中..リトライ" + (++_retryCount).ToString() + "回目"); /// 再接続を試行する Connect(); }; /// サーバー接続開始 _ws.Open(); } } }
C# – WebSocket4Net版
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using WebSocket4Net; namespace WsForm_WebSocket4Net { public partial class Form1 : Form { /// <summary> /// WebSocketsインスタンス /// </summary> private WebSocket _ws = null; /// <summary> /// リトライカウンタ /// </summary> private int _retryCount = 0; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { /// サーバー接続開始 Connect(); } /// <summary> /// サーバーへ接続する /// </summary> private void Connect() { /// listBoxへ文字列を挿入する。 Action<string> AddText = (s) => { this.Invoke(new Action(() => { listBox1.Items.Add(s); listBox1.TopIndex = listBox1.Items.Count - 1; })); }; if (_ws == null) { AddText("サーバー接続を開始します。"); } _ws = new WebSocket("ws://192.168.1.2:12345"); /// 文字列受信 _ws.MessageReceived += (s, e) => { AddText(e.Message); }; /// サーバー接続完了 _ws.Opened += (s, e) => { _retryCount = 0; AddText("サーバーに接続しました。"); }; /// 接続断の発生 _ws.Error += (s,e)=> { AddText("サーバー接続中..リトライ" + (++_retryCount).ToString() + "回目"); /// 再接続を試行する Connect(); }; /// 接続断の発生 _ws.Closed += (s, e) => { AddText("サーバー接続中..リトライ" + (++_retryCount).ToString() + "回目"); /// 再接続を試行する Connect(); }; /// サーバー接続開始 _ws.Open(); } } }
WCF WebSocketsとWebSocket4Netの違いはほぼないものの、エラーハンドリングがあるだけWebSocket4Netはちょっと分かりやすいかもしれません。
WCF WebSocketsのエラー内容はOnClose()のsenderから得ることができます。
ws.OnClose += (s, e) => { Console.WriteLine("Last Error:{0}.", ((WebSocket)s).LastError.ToString()); };
実行画面
気になったのはエラー発生後、サーバーへの接続を行うsleepは指定していませんが、どれも1秒おきくらいにやっているように見えることです。ちなみに、ブラウザ(Javascript版)では実行環境の違いによって試行までの時間(他のPC環境では10秒おきくらい)が違ったりしました。
おまけ
JavaScript版
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>WebSocket Sample</title> </head> <body> <div class="page"> <header> <div id="title"> <h1>WebSocket アプリケーション</h1> </div> </header> <div id="log"></div> <script type="text/javascript"> var ws; var retryCount = 0; var logCount = 0; var connect = function(){ if ("WebSocket" in window) { ws = new WebSocket("ws://192.168.1.2:12345"); } else if ("MozWebSocket" in window) { ws = new MozWebSocket("ws://192.168.1.2:12345"); } ws.onmessage = function(event){ output(event.data); } if( retryCount > 0 ){ output("サーバー接続中..リトライ" + retryCount.toString() + "回目"); } ws.onopen = function(event){ output("サーバーに接続しました。"); retryCount = 0; } ws.onclose = function(event){ retryCount++; connect(); } function output(str){ document.getElementById("log").innerHTML += str + "<hr />"; logCount++; scroll(0,logCount*100); } function disconnect(){ ws.close(); ws = null; } } connect(); </script> </section> <footer> </footer> </div> </body> </html>