QA@IT

C# 名前付きパイプにおいて、接続を繰り返したい。

14171 PV

C# で名前付きパイプを使用し、パイプクライアントからサーバへ文字列を送ろうとしています。

クライアント側は他のプログラムを間借りしている関係上、その場で完結しないといけないため、
・サーバはクライアントからの接続を待ち、接続があったら処理をする。その後再び接続を待つ。
・クライアントは接続後、文字列を送ったら終わり。
ということを繰り返したいと思っています。

ところが1回は正常に接続しデータを送信できるのですが、
サーバ側の接続待ち受け(WaitForConnection)の2回目に
ObjectDisposedException : 閉じているパイプにはアクセスできません。
という例外が出てしまいます。

クライアントが接続しデータ送信後に接続をクローズすると、
サーバ側のパイプは再び接続待ちにすることは出来ないのでしょうか?

以下、試しているソースの抜粋です。

クライアント側

    {
        StringBuilder sb = new StringBuilder();
        sb.AppendFormat("\t{0}\t{1}\t{2}", eventType, e.X, e.Y);

        // PIPEで送信する
        using (NamedPipeClientStream pipeClient = new NamedPipeClientStream("localhost", pipeNameMouse, PipeDirection.Out, PipeOptions.None, System.Security.Principal.TokenImpersonationLevel.Impersonation))
        using (StreamWriter sw = new StreamWriter(pipeClient))
        {
            pipeClient.Connect();
            sw.Write(sb);
            sw.Flush();
            pipeClient.WaitForPipeDrain();
        }
    }

サーバ側

    private NamedPipeServerStream pipeServer = null;

    // コンストラクタ内
    {
        pipeServer = new NamedPipeServerStream(pipeServerName, PipeDirection.In);
    }
    ...

    // スレッド実行中のあるメソッド内

    while (true)
    {
        string inputData = String.Empty;
        try
        {
            pipeServer.WaitForConnection();
        }
        catch (Exception ex)
        {
            // ここで2回目以降の受信時に例外を拾ってしまう
        }
        using (StreamReader sr = new StreamReader(pipeServer))
        {
            inputData = sr.ReadToEnd();
        }
        // inputData を何に使う

        // その他終了判定など
    }

    ...

    // 終了メソッド内
    {
        if (pipeServer != null) pipeServer.Close();
  }

宜しくお願い致します。

  • 確認したいのですが「終了メソッド」はサーバーを終了するようなときに呼び出されるもので今回は呼び出されないと思っていていいですか?
    クライアント側から閉じるだけですよね?その後またクライアントから接続するとエラーという。
    -
  • はい、その通りです。
    サーバ側は開きっぱなしです。
    サーバ側のプログラムが終了するまでは、whileループを回るのみです。
    -

回答

オブジェクトの初期化の位置がそこではうまく動かないと思います。
だいぶざっくりしたサンプルですが、以下のように変えれば動くのではないかとおもいますがどうでしょうか。

usingの部分は割愛してます。
サーバーのbutton1を押してから、クライアントのbutton1を何度か押してください。
出力ウィンドウに文字が複数回出ていると思います。

サーバーのbutton2は今回とはあまり関係ないですが、一応whileから抜けられるようにしてます。

Server (WinFormにボタン2つ貼り付けただけ)

namespace qaitNPServer
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        ThreadState s = ThreadState.Stopped;
        String pipeServerName = "pipeServerName";

        private void button1_Click(object sender, EventArgs e)
        {
            Thread t = new Thread(new ThreadStart(Method1));
            s = ThreadState.Running;
            t.Start();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            s = ThreadState.Stopped;
        }

        private void Method1()
        {
            while (true)
            {
                // 初期化をここにしています。
                using (var pipeServer 
                           = new NamedPipeServerStream(pipeServerName, PipeDirection.In))
                {

                    string inputData = String.Empty;
                    try
                    {
                        pipeServer.WaitForConnection();
                    }
                    catch (Exception ex)
                    {
                        // ここで2回目以降の受信時に例外を拾ってしまう
                    }

                    using (StreamReader sr = new StreamReader(pipeServer))
                    {
                        inputData = sr.ReadToEnd();
                    }
                    // inputData を何に使う
                    System.Diagnostics.Debug.Print(inputData);
                }
                // その他終了判定など
                if (s == ThreadState.Stopped) return;
            }
        }
    }
}

クライアント (こちらは文字列以外何も変わってません)

namespace qaitNPClient
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        // 名前かえました
        String pipeServerName = "pipeServerName";

        private void button1_Click(object sender, EventArgs e)
        {
            var sb = new StringBuilder();
            // ここ適当に文字に変えてます
            sb.AppendFormat("\t{0}\t{1}\t{2}", "etype", "e.X", "e.Y");

            // PIPEで送信する
            using (var pipeClient = new NamedPipeClientStream("localhost", pipeServerName , PipeDirection.Out, PipeOptions.None, System.Security.Principal.TokenImpersonationLevel.Impersonation))
            using (var sw = new StreamWriter(pipeClient))
            {
                pipeClient.Connect();
                sw.Write(sb);
                sw.Flush();
                pipeClient.WaitForPipeDrain();
            }
        }
    }
}
編集 履歴 (4)
  • 期待通りに動作しました。大変ありがとうございました。 -
ウォッチ

この質問への回答やコメントをメールでお知らせします。