none
別スレッドで使用中のSocketを閉じたい RRS feed

  • 質問

  • サーバーのプログラムでSocketを複数使って複数のクライアントと送受信しています
    ThreadPool.UnsafeQueueUserWorkItem((Object o) => {
    	var Socket変数=o as Socket;
    	Socket変数.Receive(.......//ここで受信待ちしている
    },AccpetされたSocket変数);
    外部のメインスレッドから内部のスレッドを強制終了したいです。
    正式には内部のSocketをShutodown,Closeするのがよいと思います。
    Acceptした全てのSocketの配列を用意し、メインスレッドからShutdown,Closeしてもスレッドセーフである保証はなく困っています。
    できればSocketを別スレッドから適切に閉じ、ブロッキングを解除または例外を発生させる。又は他によい方法があればご教授願いたいです。
    2009年12月30日 5:06

回答

  • ありがとうございました。

    別スレッドのオブジェクトは明示的にGC出来ない?
    とは関係が有ります。
    挙げていただいた例の非同期ソケットは今までやったことがありませんでした。
    参考にしてテストプログラムを作り動作としては上手くいきました。
    しかし既に実装してあるブロッキング方式よりもコードが複雑化し何とかならないものかと試行錯誤しました。
    例にある
                    //WSAEWOULDBLOCKにならずにlen=0なら切断されてる
                    connected = sock.Connected && len > 0;
    
    のlen>0でふと思いついたのはブロッキング方式でもクライアント側で切断した場合0が返るという仕様というのをサーバーサイドで接続したSocketを切断して同じ結果が出ることを期待していましたがこれは、先に試した結果上手くいかなかったので諦めていました。
    ところがlockステートメントについてすっかり抜けていました。
    確保したSocketをコレクションにlockして追加削除することでブロッキング方でもブロッキングを中止して0が返るという動きになりすんなりと上手くいきました。
    ただsocketの仕様として正しいのかどうか調べておく必要があり、一応両方の実装は残しておこうと思います。

    • 回答としてマーク 和和和 2009年12月31日 2:53
    2009年12月31日 2:52

すべての返信

  • 別スレッドのオブジェクトは明示的にGC出来ない? の続きでしょうか。

    メインスレッドから閉じるのが嫌でしたら、送受信スレッド内で閉じればいいのでは?

    public class TestClass : IDisposable
    {
        private TcpListener listener;
        private List<Socket> OpenSockets = new List<Socket>();
        private volatile bool socketCloseFlag = false;
    
        /// <summary>TCPの受信待ちを開始</summary>
        public void StartListen(IPEndPoint iep)
        {
            socketCloseFlag = false;
            listener = new TcpListener(iep);
            listener.Start();
            listener.BeginAcceptSocket(AcceptCallBack , listener);
        }
    
        /// <summary>接続中のSocketの数</summary>
        public int OpenSocketCount
        {
            get { lock (OpenSockets) { return OpenSockets.Count; } }
        }
        private void AppendOpenSockets(Socket sock)
        {
            lock (OpenSockets)
            {
                OpenSockets.Add(sock);
            }
        }
        private void RemoveOpenSockets(Socket sock)
        {
            lock (OpenSockets)
            {
                OpenSockets.Remove(sock);
            }
        }
    
        private void AcceptCallBack(IAsyncResult iar)
        {
            TcpListener listener = (TcpListener)iar.AsyncState;
            Socket sock = listener.EndAcceptSocket(iar);
            AppendOpenSockets(sock);
    
            //既に別スレッドになってるからわざわざThreapoolにする必要もないだろうけど
            System.Threading.ThreadPool.UnsafeQueueUserWorkItem(SocketRead , sock);
        }
    
        private const int WSAEWOULDBLOCK = 10035;
        private const int WSAETIMEDOUT = 10060;
    
        private void SocketRead(object oSocket)
        {
            System.IO.MemoryStream ms = new MemoryStream();
            byte[] buff = new byte[65536];
            int len;
            Socket sock = oSocket as Socket;
    
            sock.Blocking = false;
            bool connected = true;
            try
            {
                while (!socketCloseFlag && connected)
                {
                    len = 0;
                    try
                    {
                        len = sock.Receive(buff , buff.Length , SocketFlags.None);
                        ms.Write(buff , 0 , len);
                    }
                    catch (System.Net.Sockets.SocketException ex)
                    {
                        if (ex.ErrorCode == WSAEWOULDBLOCK)
                        { //非ブロッキングだからデータ受信なしの場合
                            System.Threading.Thread.Sleep(100);
                            connected = true;
                            continue;
                        }
                        throw ex;
                    }
    
                    //WSAEWOULDBLOCKにならずにlen=0なら切断されてる
                    connected = sock.Connected && len > 0;
                }
            }
            finally
            {
                sock.Shutdown(SocketShutdown.Both);
                sock.Close();
                RemoveOpenSockets(sock);
            }
        }
    
        public void CloseAllSocket()
        {
            socketCloseFlag = true;
            listener.Stop();
    
            //開いているSocketがなくなるまで待つ
            while (OpenSocketCount > 0)
            {
                System.Threading.Thread.Sleep(100);
            }
        }
    
        public void Dispose()
        {
            CloseAllSocket();
        }
    }

    2009年12月30日 7:57
  • ありがとうございました。

    別スレッドのオブジェクトは明示的にGC出来ない?
    とは関係が有ります。
    挙げていただいた例の非同期ソケットは今までやったことがありませんでした。
    参考にしてテストプログラムを作り動作としては上手くいきました。
    しかし既に実装してあるブロッキング方式よりもコードが複雑化し何とかならないものかと試行錯誤しました。
    例にある
                    //WSAEWOULDBLOCKにならずにlen=0なら切断されてる
                    connected = sock.Connected && len > 0;
    
    のlen>0でふと思いついたのはブロッキング方式でもクライアント側で切断した場合0が返るという仕様というのをサーバーサイドで接続したSocketを切断して同じ結果が出ることを期待していましたがこれは、先に試した結果上手くいかなかったので諦めていました。
    ところがlockステートメントについてすっかり抜けていました。
    確保したSocketをコレクションにlockして追加削除することでブロッキング方でもブロッキングを中止して0が返るという動きになりすんなりと上手くいきました。
    ただsocketの仕様として正しいのかどうか調べておく必要があり、一応両方の実装は残しておこうと思います。

    • 回答としてマーク 和和和 2009年12月31日 2:53
    2009年12月31日 2:52