2009-08-27 9 views
4

私は、TCP接続し、データのブロックを送信し、ソケットを閉じる送信者を持っています。 私は、上記を行う最も簡単で信頼できるプログラムを作成したいと思います。様々なMSDNや他の材料の読書に基づかいくつかの質問に今TCPソケットをブロックしてすぐに書き込みを閉じる - それは問題ですか?

// assuming LingerOption(false), NoDelay set to whatever 
var client = new TcpClient().Connect(server, port); 
var stream = client.GetStream(); 
stream.Write(data, 0, data.Length); 
stream.Close(); 
client.Close(); 

  1. 最初は(一般的にはソケットに関連する質問が、.NETで例えば)の心に入ってきますstream.Close()はSocket.Close()を呼び出します。 Socket.Close()は、 immediatelly、廃棄ネットワーク 送信していないバッファデータを閉じると言われています。これは が悪いです。しかし、Socket.Write のドキュメントでは、 ソケットがブロックされている場合(デフォルトでは、 )、すべてのデータが送信されるまで、Socket.Writeはブロック をブロックします。だから 問題はありません。
  2. 一般的に、 のコードでは、 が送信されたすべてが受信者に届かないという状況が発生する可能性がありますか? (と仮定すると、100%の信頼性 ネットワーク)受信機がReadに達していない場合

答えて

4

を構築する必要がありabsolutelly OKですSocket.Writeの直後にCloseを呼び出す。

「接続を閉じると、すべてがサービスされるまで卓越したは、フロー制御が許す限り、送信(再送)される送信する意味で優雅な操作であることを意図しているはこのように、それは:ここではTCP RFC 793 saysは何でしょう。いくつかのSEND呼び出しを行い、その後にCLOSEを実行し、すべてのデータが宛先に送信されることを期待してください。

混乱は、部分的には、MSDN documentationから来ている:

」あなたが送る、コネクション指向のプロトコルを使用している場合は、バッファ内のバイトの全てがを送信されるまでブロックします。 "

実際には、書き込みをブロックすると、送信バッファにデータがコピーされ、返されます.Socket.Closの後にデータの配信を完了するのは、RFC 793のネットワークプロバイダの責任です。 eが呼び出されます(接続が死んでいない限り、もちろん)。

1

は、あなたがデータの一部を失う可能性があり、これは下位層に使用するバッファのサイズによって異なります。

しかし、受信者が読み取りでブロックされている場合、読み取り時に十分なバッファを提供している間は問題はありません。

信頼できるネットワークを想定し、受信側で「書き込み」と十分なバッファの前に「読み取り」を実行すると、このコードは絶対に信頼できます。

しかし、私が言ったように

は、受信側のバッファのサイズは本当に重要!:ソリューション。バッファに十分なスペースがない場合は、このコードで失われます!

だから、3つの選択肢があります:単純に十分なサイズでバッファを確保

  1. を。
  2. データを送信する前に、 が必要なバッファのサイズを送信し、実際のデータを送信することを、あなたのプロトコルを実装し
  3. (例えば、我々はファイルを送信するためのWin32 APIでTransmitFile機能を持っている)データを転送するためのライブラリまたはそれ以上のプロトコルを使用してください。

あなたはデータの最大サイズを知っている場合は、十分な時間やプロトコルに興味を持っていないを持っていない場合は、その後、私はその後、私はあなたに3

を提供し、あなたにそれ以外の2 を提供し、あなたにそれ以外の1 を提供します幸運;)

+0

したがって、受信者がAcceptの後すぐにReadを開始し、ソケットが閉じられるまで読み込んだとすると、私のコードは何があっても絶対に信頼できますか?ありがとう。 – Borka

+0

@Borka:信頼できるネットワークを想定し、 "Write"の前に "Read"を実行し、受信側に十分なバッファを実行すると、このコードは絶対に信頼できます。 – Isaac

+0

@Isaac - 実際には、これらの仮定が正しいと期待することはできません。 –

1

受信者は送信者にメッセージを送信するか、再送信する可能性があります。これは、データブロック全体が受信される前に受信者のrecvバッファがいっぱいになった場合、またはネットワーク上にパケットが失われた場合に発生する可能性があります。したがって、すべてのデータが交換される前にクライアントを終了すると、データが失われます。

一般的な解決方法については、この本を参照してください。UNIXネットワークプログラミング第1巻W. Richard Stevens。

はい、UNIXですが、最終的にはTCP/IP通信を処理しており、これは上記のルートにあります。申し訳ありませんが、本当にシンプルではありませんが、それほど難しくありません。あなたの提案よりも多くの州のステートマシンです。

+0

あなたが話している非常に良い本です。私はいくつかの深遠なreaqdingを行い、上記の更新を掲載します。ありがとう。 – Borka

1

TcpClient.Closeは、Closeメソッドが配置されたとして、インスタンスをマークし、関連するソケットは、TCP接続を閉じることを要求します」と言う。Close方法は時にデータが残っ呼び出された後LingerStateプロパティに基づいて、TCP接続はしばらくの間開いたままにします基本的な接続が完了したときに通知は提供されません。

つまり、LingerStateを操作していない限り、Closeを呼び出しても、データのOSバッファは破棄されません.TCPスタックは、書き込み済みのデータを配信するために最善を尽くします。

例外が発生した場合は、Writeまたは2 Closeの呼び出しで例外がスローされる可能性があります。その場合はソケットとストリームを廃棄してください。

また、データが送信されるまでブロックされないかもしれないことに注意してください。データがTCPスタックにコピーされるまでブロックされません(あまりにも多くのものを捨てると、TCPは部分的にネットワーク上でWriteが返されます)

0

これは元の質問を回避していますが、送信しているデータの種類は言いませんでした。最初にTCP接続を使用する理由は何ですか?どのくらいの頻度でこれらのデータブロックを書いていますか?非常に頻繁な場合は、次の2つのいずれかをお勧めします。

1)TCP.Streamを開いたままにしておきます。それ以外の場合、4分未満で〜64000ブロックを送信すると、利用可能な送信元ポート(1025-65535)のTCPスタックを使い果たす可能性があります。ソースマシン上で他のアプリケーションが開いていると、これは悪化します。この問題が発生すると、アプリケーションは4分以上経過した最初のポートが使用可能になるまでハングまたはエラーを起こします。

2)TCPではなくUDPを使用します。配信の保証はありませんが、これがリアルタイムデータの場合は、リアルタイムデータを大幅に遅延させる可能性があるため、TCPを使用しないことをお勧めします。

TCPを使用している場合、Client.Close()はTCPスタックを介してFINを送信する必要があります。他のホストがクラッシュしたり、それらの間のネットワークがダウンしたりしない限り、配信は保証されません。

は、あなたがその目的地に到着するデータのすべてのブロックを懸念している場合は、TCPやUDPを使用するかどうか、あなたはすべてのブロックが順番や破損していないに到着したいくつかのアプリケーションレベルのチェック...それはだ

関連する問題