none
TcpClientの切断で異常になる RRS feed

  • 質問

  • お世話になっております。

    表題の通り、TcpClientで接続後、切断処理を行うとおかしくなります。
    症状を以下に示します。

    クライアント側接続:

    _connect.Reset();
    client = new TcpClient();
    client.BeginConnect(ip, port, new AsyncCallback(ConnectCallback), client);
    

    サーバ側受信処理:

        private void ProcessMessage(StreamReader reader)
            {
                lock (this)
                {
                    string message = reader.ReadLine();
    
                    switch (message)
                    {
                    }
                }
            }

    クライアントから、接続後、conectedプロパティはtrueとなり、正常にメッセージの
    送受信が完了します。

    ところが、

    client.Close();
    後、何もメッセージを送信していないのに、サーバ受信側でnullが受信され”続けます”。

    且つ、その後のTcpClientオブジェクトは以下のようになります。

    何が起こっているのでしょうか?

    切断方法がおかしいのでしょうか?

    識者の方、ご教示ください。

    2015年8月19日 1:42

回答

  • 末尾というか、【切断を完了した後もずっとnullを受信し続ける】動作が正常ということでしょうか?
    StreamReaderにとって入力ストリームが何であるかは関係ありません。ですからネットワーク切断に限らず、FileStreamでも入力ストリームの末尾に到達した場合は同じことになります。そこには「受信し続ける」という意味はなくStreamReaderは仕様に従ってnullを返しただけです。
    今回、ご質問させて頂きましたのは、ほかに問題がありまして
    質問文にしか答えることができません。質問したい内容を書きましょう。

    その後、同じロジックを用いて、再接続するのですが
    接続完了後(conectedプロパティがtrue後)に、メッセージを送信してもnullを受信し続け
    本来の送りたいメッセージが受け取れていないという問題です。

    その原因は、nullを受信し続けることにあって、その為に正常に受信できていないと
    思っていますが、また別の問題があるのでしょうか。

    挙げられたサーバ側受信処理のコードにはネットワークに関する処理が一切含まれていないため、この範囲では何もわかりません。質問文を読む限り「再接続」をされているようですが、その場合、TcpListener(このクラスを使っているのかもわかりませんが)からAcceptメソッドで新しいTcpClientインスタンスを得ているはずですが、その新しいTcpClientインスタンスをどう処理したのでしょうか?
    • 回答としてマーク コーベル 2015年8月19日 5:30
    2015年8月19日 2:50
  • 挙げられたコードで説明すると、TcpClient型のserver変数はクライアント側の接続1回分でしかありません。クライアント側で再接続した場合は TcpClient server = listener.AcceptTcpClient(); 行を再度実行する必要があります。
    • 回答としてマーク コーベル 2015年8月19日 5:29
    2015年8月19日 4:00

すべての返信

  • client.Close();
    後、何もメッセージを送信していないのに、サーバ受信側でnullが受信され”続けます”。

    StreamReader.ReadLine メソッドには

    入力ストリームの末尾に到達した場合は null。

    と書かれているので、ドキュメント通りの動作ではありませんか?

    2015年8月19日 2:08
  • 佐祐理さん、ご回答ありがとうございます。

    末尾というか、【切断を完了した後もずっとnullを受信し続ける】動作が正常ということでしょうか?

    今回、ご質問させて頂きましたのは、ほかに問題がありまして

    その後、同じロジックを用いて、再接続するのですが
    接続完了後(conectedプロパティがtrue後)に、メッセージを送信してもnullを受信し続け
    本来の送りたいメッセージが受け取れていないという問題です。

    その原因は、nullを受信し続けることにあって、その為に正常に受信できていないと
    思っていますが、また別の問題があるのでしょうか。

    というか、closeしたインスタンス以外の、どのインスタンスが送り続けているのでしょうか。

    すみません、説明足らずですよね。

    2015年8月19日 2:21
  • 末尾というか、【切断を完了した後もずっとnullを受信し続ける】動作が正常ということでしょうか?
    StreamReaderにとって入力ストリームが何であるかは関係ありません。ですからネットワーク切断に限らず、FileStreamでも入力ストリームの末尾に到達した場合は同じことになります。そこには「受信し続ける」という意味はなくStreamReaderは仕様に従ってnullを返しただけです。
    今回、ご質問させて頂きましたのは、ほかに問題がありまして
    質問文にしか答えることができません。質問したい内容を書きましょう。

    その後、同じロジックを用いて、再接続するのですが
    接続完了後(conectedプロパティがtrue後)に、メッセージを送信してもnullを受信し続け
    本来の送りたいメッセージが受け取れていないという問題です。

    その原因は、nullを受信し続けることにあって、その為に正常に受信できていないと
    思っていますが、また別の問題があるのでしょうか。

    挙げられたサーバ側受信処理のコードにはネットワークに関する処理が一切含まれていないため、この範囲では何もわかりません。質問文を読む限り「再接続」をされているようですが、その場合、TcpListener(このクラスを使っているのかもわかりませんが)からAcceptメソッドで新しいTcpClientインスタンスを得ているはずですが、その新しいTcpClientインスタンスをどう処理したのでしょうか?
    • 回答としてマーク コーベル 2015年8月19日 5:30
    2015年8月19日 2:50
  • 佐祐理さん、ご回答ありがとうございます。

    入力ストリームの末尾に到達した場合は同じことになります。そこには「受信し続ける」という意味はなくStreamReaderは仕様に従ってnullを返しただけです。

    なるほど、仕様として理解するのみですね。
    Close()しなければ起こらないので、どういう処理が一番適しているのか知りたかった
    次第です。

    その新しいTcpClientインスタンスをどう処理したのでしょうか?

    リスナーは、initialで設定する以外の処理はしておりません。

        private void ServerStart()
            {
                try
                {
                    listener = new TcpListener(IPAddress.Any, port);
                    listener.Start();
                    
                    threadServer = new Thread(new ThreadStart(ServerListen));
                    threadServer.Start();
                }
                catch (Exception exception)
                {
                }
            }
    
    *********************************
        private void ServerListen()
            {
                try
                {
                    TcpClient server = listener.AcceptTcpClient();
                    
                    NetworkStream ns = server.GetStream();
    
                    serverReader = new StreamReader(ns, Encoding.UTF8);
                    serverWriter = new StreamWriter(ns, Encoding.UTF8);
    
                    while (true)
                    {
                        ProcessMessage(serverReader);
                    }
                }
                catch (Exception exception)
                {
                }
            }
    *********************************
    private void ProcessMessage(StreamReader reader)
            {
                lock (this)
                {
                    string message = reader.ReadLine();
    
                    switch (message)
                    {
                    }
                }
            }

    ServerStart()は最初にコールされ、その後はCloseイベントで破棄するだけとなっています。

    その場合、TcpListener(このクラスを使っているのかもわかりませんが)からAcceptメソッドで新しいTcpClientインスタンスを得ているはず

    これは、再接続されるたび(切断後?)に毎回あたらしいインスタンスを使用するということですか?

    無知ですみません。

    2015年8月19日 3:04
  • また、接続側は、以下の処理を繰り返し実行しているのみとなります。

    TcpClient client = new TcpClient("192.168.1.150", port);
    
                        NetworkStream ns = client.GetStream();
    
                        clientReader = new StreamReader(ns, Encoding.UTF8);
                        clientWriter = new StreamWriter(ns, Encoding.UTF8);
    
                        threadClient = new Thread(new ThreadStart(this.ClientListen));
                        threadClient.Start();
    
                        clientWriter.WriteLine("GetData");
                        
                        clientWriter.Flush();

    1度目は成功するのですが、2度目のメッセージが届きません。

    2015年8月19日 3:08
  • 挙げられたコードで説明すると、TcpClient型のserver変数はクライアント側の接続1回分でしかありません。クライアント側で再接続した場合は TcpClient server = listener.AcceptTcpClient(); 行を再度実行する必要があります。
    • 回答としてマーク コーベル 2015年8月19日 5:29
    2015年8月19日 4:00
  • 佐祐理さん、ご回答ありがとうございます。

    そういうことだったのですね、

    nullを受信する件、再接続できなかった件、ともに理解できました。

    コードも修正し正常にデータを受信できるようになりました。

    本当にご丁寧にありがとうございました^^

    2015年8月19日 5:29