2012-03-14 5 views
9

状況の簡単な概要:ストリームがクライアントによって閉じられているかどうかを検出できますか?

私は情報を受け取り、ソケット上で回答を送信するサービスを提供しています。接続は保護されていません。これらの接続にTLSを提供できる別のサービスをセットアップしたいと思います。この新しいサービスは単一のポートを提供し、提供されたクライアント証明書に基づいて接続を分散します。私はいくつかの理由でstunnelを使いたくはありません.1つは、受信ポートごとに1つの転送ポートが必要なことです。

私は現在、実装しようとしているソリューション:

を基本的には、私はカップルにNetworkStreamとSslStream(受信)(送信しようとしている - かもしれないソケットを、私はにそれを置きます着信と一致するようにNetworkStreamを作成し、2つのリンクを読み書きします。このリンクは、クライアント(SSL/TLSを介して)とサービス(セキュリティ保護されていない接続を介して)との間のフローを提供します。

public class StreamConnector 
{ 
    public StreamConnector(Stream s1, Stream s2) 
    { 
     StreamConnectorState state1 = new StreamConnectorState(s1, s2); 
     StreamConnectorState state2 = new StreamConnectorState(s2, s1); 
     s1.BeginRead(state1.Buffer, 0, state1.Buffer.Length, new AsyncCallback(ReadCallback), state1); 
     s2.BeginRead(state2.Buffer, 0, state2.Buffer.Length, new AsyncCallback(ReadCallback), state2); 
    } 

    private void ReadCallback(IAsyncResult result) 
    { 
     // Get state object. 
     StreamConnectorState state = (StreamConnectorState)result.AsyncState; 

     // Finish reading data. 
     int length = state.InStream.EndRead(result); 

     // Write data. 
     state.OutStream.Write(state.Buffer, 0, length); 

     // Wait for new data. 
     state.InStream.BeginRead(state.Buffer, 0, state.Buffer.Length, new AsyncCallback(ReadCallback), state); 
    } 
} 

public class StreamConnectorState 
{ 
    private const int BYTE_ARRAY_SIZE = 4096; 

    public byte[] Buffer { get; set; } 
    public Stream InStream { get; set; } 
    public Stream OutStream { get; set; } 

    public StreamConnectorState(Stream inStream, Stream outStream) 
    { 
     Buffer = new byte[BYTE_ARRAY_SIZE]; 
     InStream = inStream; 
     OutStream = outStream; 
    } 
} 

問題:ここで

は、私はこれらのストリームをリンクするために思い付いたクラスですクライアントがSslStreamの情報と処分を送信して行われた場合

、サーバーはしていませんこれが起こったかどうかの任意の並べ替えの表示をしています。このStreamConnectorクラスは、どんな種類のエラーも発生させることなく、楽しく永遠に実行し続けます。停止する必要のあるインジケータは見つかりません。 (もちろん、ReadCallbackで毎回0の長さになるという事実がありますが、長時間の接続を提供できる必要があるので、これは判断には良い方法ではありません)。

もう1つの可能性ReadCallbackは、利用可能なデータがなくても呼び出されるという問題があります。ストリームの代わりに直接ソケットを使用していた場合、それが違うかどうかはわかりませんが、コードを何度も何度も実行し続けることは非効率的です。

私の質問:

1)ストリームは、クライアント側から閉鎖されている場合に指示する方法はありますか?

2)私がやろうとしていることを行う良い方法はありますか?

2a)非同期読み書きループをより効率的に実行する方法はありますか?

EDIT:ありがとう、Robert。私がストリームを閉じていないので(ストリームを閉じなければならない時を知らせる方法が分からないため)、ループが呼び出され続けていることがわかります。 - それに基づいて、または何か - 切断を検出するために

/// <summary> 
/// Connects the read/write operations of two provided streams 
/// so long as both of the streams remain open. 
/// Disposes of both streams when either of them disconnect. 
/// </summary> 
public class StreamConnector 
{ 
    public StreamConnector(Stream s1, Stream s2) 
    { 
     StreamConnectorState state1 = new StreamConnectorState(s1, s2); 
     StreamConnectorState state2 = new StreamConnectorState(s2, s1); 
     s1.BeginRead(state1.Buffer, 0, state1.Buffer.Length, new AsyncCallback(ReadCallback), state1); 
     s2.BeginRead(state2.Buffer, 0, state2.Buffer.Length, new AsyncCallback(ReadCallback), state2); 
    } 

    private void ReadCallback(IAsyncResult result) 
    { 
     // Get state object. 
     StreamConnectorState state = (StreamConnectorState)result.AsyncState; 

     // Check to make sure Streams are still connected before processing. 
     if (state.InStream.IsConnected() && state.OutStream.IsConnected()) 
     { 
      // Finish reading data. 
      int length = state.InStream.EndRead(result); 

      // Write data. 
      state.OutStream.Write(state.Buffer, 0, length); 

      // Wait for new data. 
      state.InStream.BeginRead(state.Buffer, 0, state.Buffer.Length, new AsyncCallback(ReadCallback), state); 
     } 
     else 
     { 
      // Dispose of both streams if either of them is no longer connected. 
      state.InStream.Dispose(); 
      state.OutStream.Dispose(); 
     } 
    } 
} 

public class StreamConnectorState 
{ 
    private const int BYTE_ARRAY_SIZE = 4096; 

    public byte[] Buffer { get; set; } 
    public Stream InStream { get; set; } 
    public Stream OutStream { get; set; } 

    public StreamConnectorState(Stream inStream, Stream outStream) 
    { 
     Buffer = new byte[BYTE_ARRAY_SIZE]; 
     InStream = inStream; 
     OutStream = outStream; 
    } 
} 

public static class StreamExtensions 
{ 
    private static readonly byte[] POLLING_BYTE_ARRAY = new byte[0]; 

    public static bool IsConnected(this Stream stream) 
    { 
     try 
     { 
      // Twice because the first time will return without issue but 
      // cause the Stream to become closed (if the Stream is actually 
      // closed.) 
      stream.Write(POLLING_BYTE_ARRAY, 0, POLLING_BYTE_ARRAY.Length); 
      stream.Write(POLLING_BYTE_ARRAY, 0, POLLING_BYTE_ARRAY.Length); 
      return true; 
     } 
     catch (ObjectDisposedException) 
     { 
      // Since we're disposing of both Streams at the same time, one 
      // of the streams will be checked after it is disposed. 
      return false; 
     } 
     catch (IOException) 
     { 
      // This will be thrown on the second stream.Write when the Stream 
      // is closed on the client side. 
      return false; 
     } 
    } 
} 
+0

タイトルに「C#」という接頭辞を付けることは避けてください。それは何のためのタグですか:) – kprobst

+0

申し訳ありません。 :)私が研究している間、他のいくつかのタイトルで言葉の言葉を見た。それが助けになると思った。ここからタグに固執する。 – zimdanen

答えて

3

あなたは読み取りまたはソケットに書き込もうとする必要があります。他の誰かがこの問題に遭遇した場合には、私は完全なコードソリューションを含めています。

書き込みしようとすると、例外がスローされます(言語のパラダイムに応じてエラーが返されます)。おそらく0バイトしか書き込まれません。読み込みしようとすると、例外がスローされるか、エラーを返します(言語のパラダイムにもよります)。nullを返します。

選択ベースのサーバーモデルを使用している場合、接続されていないソケットが表示されます。つまり、切断されたソケットが表示されます。 null

+0

だから、私は各ストリームに書き込みを試みる必要があります(ストリームが閉じていないと、ストリームのデータが不正になる可能性があります)。 – zimdanen

+0

私が0バイトを書いているとすれば、それは問題ではないはずです。 – zimdanen

+2

ドロップしたものを検出する唯一の方法は、[** Nito **](http://nitoprograms.blogspot.co.il/2009/05/detection-of-half-open-dropped.html)によると、接続はストリームに書き込むことです。 –

0

何らかのメッセージが表示されたときにクライアントがサーバーに伝えるべきだとは思えません。電線が切断されているか電源が切れているかプラグが引っ張られている状態に備えておくのが最善ですが、一般的には何らかのエンドオブメッセージマーカーを使用して接続を終了したいと考えています。通常の会話ではなく、実際の問題に対する例外を保存します。

+0

クライアント(Socketを所有するNetworkStreamを所有する)にSslStreamを配置すると、サーバに何も送信されず、サーバが切断されたことを通知します。少なくとも、サーバー上のソケット/ストリームを閉じるものはありません。 – zimdanen

+0

@zimdanen:私はそれがSslStreamの(大きな)設計上の問題だと思っています。 SslStreamがあなたの権限を超えていれば、あなたがやったことのほかに多くはできません。私たちのソフトウェアは、必要とされるのではなく、世界と協働しなければなりません。 (しかし、私はそれについて恐怖に抵抗することはできません。) – RalphChapin

関連する問題