2016-03-25 13 views
0

TcpClientを半クロージングする際に重大な問題があります。私は何をしようとしていることである:半クローズTcpClient

クライアント上:

  1. メッセージ
  2. シャットダウンを送る
  3. を送信するための基盤となるソケットは(
  4. シャットダウンに読み取るための基本となるソケットを応答を受け取りますまたは、この時点で閉じてください)

サーバー上:

  1. を読み取るための基盤となるソケットは、しかし(ただそれを閉じ、この時点で、または)
  2. シャットダウンに

を記述するための基盤となるソケットを応答を送信メッセージ

  • シャットダウンを受け取ります、クライアントまたはサーバー上のステップ2の後、私はTcpClientのストリームを使用できません。 は、ここで(非同期呼び出し、処理およびクリーンアップせずに、また、代わりにXmlSerializerのStreamReaderをとのStreamWriterを使用して)私のコードの非常に単純化したバージョンです:

     //initialize the connection between the server and the client 
         var listener = new TcpListener(IPAddress.Any, 13546); 
         listener.Start(); 
         var client = new TcpClient("127.0.0.1", 13546); 
         var server = listener.AcceptTcpClient(); 
         listener.Stop(); 
    
         //CLIENT: send the message 
         var cwriter = new StreamWriter(client.GetStream()); 
    
         cwriter.Write("client's message"); 
         cwriter.Flush(); 
    
         client.Client.Shutdown(SocketShutdown.Send); 
    
         //SERVER: receive the message 
         string msg; 
         var sreader = new StreamReader(server.GetStream()); 
    
         msg = sreader.ReadToEnd(); 
    
         server.Client.Shutdown(SocketShutdown.Receive); 
    
         //SERVER: send a response 
         //Here the code fails on server.GetStream() - 
         //InvalidOperationException, apparently the whole connection is closed now 
    
         var swriter = new StreamWriter(server.GetStream()); 
    
         swriter.Write(msg + " with server's response"); 
         swriter.Flush(); 
    
         server.Client.Shutdown(SocketShutdown.Send); 
    
         //CLIENT: receive the message 
         var creader = new StreamReader(client.GetStream()); 
    
         var response = creader.ReadToEnd(); 
    
         client.Client.Shutdown(SocketShutdown.Receive); 
    

    はrawソケットを使用せずにこれを実行する方法はあります?私が間違っていることはありますか?

  • +0

    フェイルとは何ですか?生のソケットへの飛び越しは時期尚早で恣意的なようです。むしろ、バグを修正して、あなたは良いです。 – usr

    +1

    'StreamWriter'は、渡されたストリームの所有権を取得します。ガーベジコレクションされると、下にあるストリームも同様に廃棄されます。 'StreamWriter'を廃棄した場合、これは明らかに分かります(あなたは' IDisposable'で何をしているのでしょうか?)。ストリームを開いたままにするコンストラクタのオーバーロードを使用できます。あるいは、これを気にしないでください。 'TcpClient'の基底のソケットにアクセスする必要がある場合、通常は間違っています。正しいアボートロジックで手動でソケットをシャットダウンする必要はありません。 –

    +0

    @ JeroenMostert良いキャッチですが、問題は簡単には現れません。 SRにはファイナライザもありますか?それはしないでください。また、シャットダウンすることでストリームの完了をリモート側に知らせることができます。私は受信をシャットダウンすることはあまりありませんね、はい。 – usr

    答えて

    0

    問題は、ReadToEndがストリームの最後までデータを読み取ることです。 Client.Shutdownを発行すると、ソケットを実際に閉じて再利用できなくなります(少なくともTCPClientの場合)。ここにあなたが見ることができるように、エラーが原因閉じられたソケットにoccures GetStream()

    public NetworkStream GetStream() { 
        if(Logging.On)Logging.Enter(Logging.Sockets, this, "GetStream", ""); 
        if (m_CleanedUp){ 
         throw new ObjectDisposedException(this.GetType().FullName);   
        } 
        if (!Client.Connected) { 
         throw new InvalidOperationException(SR.GetString(SR.net_notconnected)); 
        } 
        if (m_DataStream==null) { 
         m_DataStream = new NetworkStream(Client, true); 
        } 
        if(Logging.On)Logging.Exit(Logging.Sockets, this, "GetStream", m_DataStream); 
        return m_DataStream; 
    } 
    

    のコードです。

    EDIT:それはばかげて奇妙ですが、私はそれが正しく動作しない理由を発見したと思います。その理由は、Shutdownは常にソケット全体のフラグを切断しているためです。たとえそれが実際に閉鎖されていないとしても!メソッドの初めにストリームを保存すると、問題はソケットの状態をチェックするGetStreamメソッドにあるので、この問題には直面しません。しかし、他のコードがソケットの状態をチェックすると、バグに晒されている可能性があります。

    +0

    したがって、TcpClientで半分を閉じることはできません。残念です。 – Noctiphobia

    +0

    接続を半分に閉じることができます。開いている質問は、コードが半分閉じて表示されている場合、ソケットが完全に閉じられている理由です。また、冒頭でGetStreamを一度呼び出すだけで、より洗練されたコードを手に入れることができます。 @Noctiphobiaはそれを行い、コードを更新してください。 – usr

    +0

    @Noctiphobia okコードを書き直しました。問題なくデータをやりとりできるようです。とにかく、双方向通信用に再利用したい場合は、ソケットを明示的に扱う方が安全です。IMO – greenshade