SocketAsyncEventArgs を使った非同期サーバ

Socket.AcceptAsync() と SocketAsyncEventArgs を使います。

using System;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;

namespace Samples
{
    /// <summary>
    /// SocketAsyncEventArgs を使ったサーバー。
    /// </summary>
    class SocketAsyncListener
    {
        Socket listener;
        public SocketAsyncListener()
        {
            Debug.WriteLine("Enter SocketAsyncListener::.ctor");
            listener = null;
            Debug.WriteLine("Exit  SocketAsyncListener::.ctor");
        }

        public void Start()
        {
            if (listener != null)
                throw new InvalidOperationException();

            Debug.WriteLine("Enter SocketAsyncListener::Start");
            try
            {
                listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                listener.Bind(new IPEndPoint(IPAddress.Loopback, 8081)); // localhost:8081
                listener.Listen(10);
                Debug.WriteLine(string.Format("Info  SocketAsyncListener::Start listener#{0}", listener.GetHashCode()));
            }
            catch
            {
                if (listener != null)
                    listener.Close();
                listener = null;
            }
            finally
            {
                if (listener != null)
                    BeginAccept(listener, null);
                Debug.WriteLine("Exit  SocketAsyncListener::Start");
            }
        }

        public void Stop()
        {
            if (listener == null)
                return;

            Debug.WriteLine("Enter SocketAsyncListener::Stop");

            listener.Close();
            listener = null;
            Debug.WriteLine("Exit  SocketAsyncListener::Stop");
        }

        private void BeginAccept(Socket listener, SocketAsyncEventArgs args)
        {
            Debug.WriteLine("Enter SocketAsyncListener::BeginAccept");
            if (args == null) // null なら新しく作る。
            {
                args = new SocketAsyncEventArgs();
                args.Completed += new EventHandler<SocketAsyncEventArgs>(EndAccept);
                Debug.WriteLine(string.Format("Info  SocketAsyncEventArgs#{0}", args.GetHashCode()));
            }
            args.AcceptSocket = null; // リセットしておく

            try
            {
                if (!listener.AcceptAsync(args))
                    EndAccept(listener, args);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(string.Format("Exception SocketListener::BeginAccept {0}", ex.GetType().Name));
                //throw ex; 何か問題があれば投げる?
            }
            finally
            {
                Debug.WriteLine("Exit  SocketAsyncListener::BeginAccept");
            }
        }

        private void EndAccept(object sender, SocketAsyncEventArgs e)
        {
            Debug.WriteLine("Enter SocketAsyncListener::EndAccept");
            // SocketAsyncEventArgs の便利なところ。操作の結果がだいたいわかる
            if (e.SocketError == SocketError.OperationAborted) // listener が閉じられたので操作が中止された
            {
                Debug.WriteLine(string.Format("Info  SocketAsyncListener::EndAccept listener#{0} is closed", sender.GetHashCode()));
                e.Dispose();
                Debug.WriteLine(string.Format("Exit  SocketAsyncListener::EndAccept SocketAsyncEventArgs#{0} is disposed", e.GetHashCode()));
                return;
            }

            Socket listener = (Socket)sender;
            Socket client = e.AcceptSocket;

            BeginAccept(listener, e); // 次へ

            Debug.WriteLine("Exit  SocketAsyncListener::EndAccept");

            // ここでクライアントと通信する。
            // とりあえず今回は閉じるだけ。
            client.Close();
        }
    }
}