SocketAsyncEventArgs を使ったサーバーを作る
まぁ、MS の公式サイトに載ってるのをほぼぱくっただけですが。
Begin/End パターンに見慣れているので、StartAccept を BeginAccept にしたってだけ。受け入れたクライアントを閉じる処理はしていない。OnAccept は virtual にしておいた。
- Server.Start() -> AcceptClient イベント
- Server を継承したクラスで OnAccept を override
という使い方ができるかな。
using System; using System.Net; using System.Net.Sockets; using System.Diagnostics; namespace Samples { delegate void AcceptClient(object sender, Socket client); class ServerBase { Socket listener; IPEndPoint endpoint; EventHandler<SocketAsyncEventArgs> acceptCallback; public bool IsListening { get; private set; } public event AcceptClient AcceptClient; public ServerBase(IPEndPoint endpoint) { if (endpoint == null) throw new ArgumentNullException("endpoint"); this.endpoint = endpoint; this.acceptCallback = new EventHandler<SocketAsyncEventArgs>(AcceptCompleted); this.IsListening = false; } public void Start() { if (IsListening) // リスニング中 return; // 新しく作る listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); listener.Bind(endpoint); listener.Listen(10); IsListening = true; // true にしたあと BeginAccept BeginAccept(null); // 受け入れ開始 } public void Stop() { if (!IsListening) // 既に止まってる return; IsListening = false; listener.Close(); listener = null; } protected virtual void OnAccept(Socket client) { if (AcceptClient != null) AcceptClient(this, client); } private void BeginAccept(SocketAsyncEventArgs args) { if (IsListening) { if (args == null) // 1回だけ作る。後は再利用する。 { args = new SocketAsyncEventArgs(); args.Completed += acceptCallback; } else { args.AcceptSocket = null; } if (!listener.AcceptAsync(args)) EndAccept(args); // 同期的に完了した場合 } else { if (args != null) args.Dispose(); } } private void AcceptCompleted(object sender, SocketAsyncEventArgs args) { EndAccept(args); } private void EndAccept(SocketAsyncEventArgs args) { Socket client = args.AcceptSocket; // 取り出してすぐ次の受け入れへ BeginAccept(args); if (args.SocketError == SocketError.Success) { OnAccept(client); // event を発生させる } else { if (client != null && client.Connected) client.Close(); } } } }