10

ドキュメントにもかかわらず、NetworkStream.Writeはデータが送信されるまで待機していないようです。代わりに、データがバッファにコピーされるまで待機してから戻ります。そのバッファはバックグラウンドで送信されます。

これは私が現在持っているコードです。ns.Writeとns.BeginWriteのどちらを使用するかは関係ありません。どちらも、すぐに戻ります。EndWriteもすぐに戻ります(ネットワークへの書き込みではなく、送信バッファーへの書き込みであるため、これは理にかなっています)。

    bool done;
    void SendData(TcpClient tcp, byte[] data)
    {
        NetworkStream ns = tcp.GetStream();
        done = false;
        ns.BeginWrite(bytWriteBuffer, 0, data.Length, myWriteCallBack, ns);
        while (done == false) Thread.Sleep(10);
    }
   
    public void myWriteCallBack(IAsyncResult ar)
    {
        NetworkStream ns = (NetworkStream)ar.AsyncState;
        ns.EndWrite(ar);
        done = true;
    }

データが実際にクライアントに送信されたことをどのように確認できますか?

データを送信した後、サーバーからの応答を(たとえば)10秒間待ちたいのですが、そうでない場合は、何かが間違っていると思います。データの送信に15秒かかる場合、NetworkStream.Writeが戻ったとき(データが送信される前)からしかカウントを開始できないため、常にタイムアウトになります。データがネットワークカードを離れてから10秒のカウントを開始したいと思います。

データの量と送信時間はさまざまです。送信には1秒、送信には10秒、送信には1分かかる場合があります。サーバーはデータを受信したときに応答を送信します(SMTPサーバーです)が、データの形式が正しくなく、応答が返ってこない場合は、永遠に待ちたくないので、私がmデータが送信されるのを待っている場合、またはサーバーが応答するのを待っている場合。

ユーザーにステータスを表示したい場合があります-「サーバーにデータを送信しています」と「サーバーからの応答を待っています」を表示したいのですが、どうすればよいですか?

4

9 に答える 9

11

私は C# プログラマーではありませんが、この質問の仕方は少し誤解を招きます。データがいつ「受信」されたかを知る唯一の方法は、「受信」の有用な定義のために、データが完全に処理されたことを示す特定の確認メッセージをプロトコルに含めることです。

データはネットワーク カードから正確に「離れる」わけではありません。プログラムとネットワークの関係を考える最良の方法は次のとおりです。

あなたのプログラム -> 多くの紛らわしいもの -> ピアプログラム

「多くの紛らわしいもの」に含まれる可能性のあるもののリスト:

  • CLR
  • オペレーティング システムのカーネル
  • 仮想化されたネットワーク インターフェイス
  • スイッチ
  • ソフトウェア ファイアウォール
  • ハードウェア ファイアウォール
  • ネットワーク アドレス変換を実行するルーター
  • ネットワーク アドレス変換を実行するピア側のルーター

したがって、別のオペレーティング システムでホストされ、仮想マシンのネットワーク動作を制御するソフトウェア ファイアウォールを備えた仮想マシンを使用している場合、データが「実際に」ネットワーク カードから出たのはいつでしょうか? 最良のシナリオであっても、これらのコンポーネントの多くがパケットをドロップする可能性があり、ネットワーク カードが再送信する必要があります。最初の (失敗した) 試行が行われたときに、ネットワーク カードがネットワーク カードから「離れた」ことはありますか? ほとんどのネットワーク API は、反対側が TCP 確認応答を送信するまで "送信" されていません。

とはいえ、 NetworkStream.Write のドキュメントは、少なくとも「送信」操作を開始するまで返されないことを示しているようです:

Write メソッドは、要求されたバイト数が送信されるか、SocketException がスローされるまでブロックされます。

もちろん、「送信された」というのは、上で述べた理由からややあいまいです。また、データがプログラムによって「実際に」送信され、ピア プログラムによって受信される可能性もありますが、ピアがクラッシュするか、実際にはデータを処理しません。そのため、ピアが実際にメッセージを処理したときにのみ発行されるメッセージの のWrite後にを実行する必要があります。Read

于 2008-09-17T03:32:07.133 に答える
5

TCPは「信頼性の高い」プロトコルです。つまり、ソケットエラーがない場合、データはもう一方の端で受信されます。私は、より高いレベルのアプリケーション確認を使用してTCPを2番目に推測する多くの努力を見てきましたが、私見では、これは通常、時間と帯域幅の浪費です。

通常、説明する問題は、通常のクライアント/サーバー設計によって処理されます。これは、最も単純な形式では次のようになります...

クライアントはサーバーに要求を送信し、ある種の応答を待っているソケットでブロッキング読み取りを実行します。TCP接続に問題がある場合、その読み取りは中止されます。クライアントは、タイムアウトを使用して、サーバーに関するネットワークに関連しない問題を検出する必要もあります。リクエストが失敗またはタイムアウトした場合、クライアントは再試行したり、エラーを報告したりできます。

サーバーが要求を処理して応答を送信すると、サーバーは通常、トランザクション中にソケットがなくなったとしても、それ以上の対話を開始するのはクライアントの責任であるため、何が起こるかを気にしません。個人的には、サーバーであることが非常に快適だと思います。:-)

于 2008-09-15T23:30:29.280 に答える
1

NetworkStream.Write ができるだけ早くデータをサーバーに送信しないシナリオは考えられません。大規模なネットワークの輻輳や切断がなければ、妥当な時間内に相手側に到達するはずです. プロトコルに問題がある可能性はありますか? たとえば、HTTP では、要求ヘッダーは空白行で終了する必要があり、サーバーは応答が発生するまで応答を送信しません。使用中のプロトコルには、同様のメッセージ終了特性がありますか?

デリゲート、フィールド、および Thread.Sleep を削除して、元のバージョンよりもクリーンなコードを次に示します。機能的にはまったく同じ方法で実行されます。

void SendData(TcpClient tcp, byte[] data) {
    NetworkStream ns = tcp.GetStream();
    // BUG?: should bytWriteBuffer == data?
    IAsyncResult r = ns.BeginWrite(bytWriteBuffer, 0, data.Length, null, null);
    r.AsyncWaitHandle.WaitOne();
    ns.EndWrite(r);
}

上記を書いている間に質問が変更されたようです。.WaitOne() は、タイムアウトの問題に役立つ場合があります。タイムアウト パラメータを渡すことができます。これは遅延待機です。結果が終了するか、タイムアウトになるまで、スレッドは再度スケジュールされません。

于 2008-09-16T00:15:27.003 に答える
1

私は .NET NetworkStream 設計者の意図を理解しようと努めており、彼らはこのように設計する必要があります。書き込み後、送信するデータは .NET によって処理されなくなります。したがって、Write がすぐに返されるのは妥当です (データはすぐに NIC から送信されます)。

したがって、アプリケーションの設計では、自分のやり方で機能させる以外に、このパターンに従う必要があります。たとえば、NetworkStream からデータを受信するまでのタイムアウトを長くすると、コマンドが NIC を離れるまでの時間を補うことができます。

全体として、ソース ファイル内にタイムアウト値をハード コードするのは悪い習慣です。実行時にタイムアウト値を設定できる場合は、すべて正常に動作するはずです。

于 2008-09-16T05:55:17.657 に答える
1

一般的に、とにかくクライアントから確認を送信することをお勧めします。そうすれば、データが正しく受信されたことを 100% 確信できます。

于 2008-09-15T23:04:26.147 に答える
1

推測するに、NetworkStream は、バッファーを Windows Socket に渡すと、データが送信されたと見なします。したがって、TcpClient を介して目的を達成する方法があるかどうかはわかりません。

于 2008-09-15T23:07:18.260 に答える
0

Bellow .netは、TCPを使用するWindowsソケットです。TCPはACKパケットを使用して、データが正常に転送されたことを送信者に通知します。したがって、送信側のマシンはデータがいつ転送されたかを認識していますが、.netでその情報を取得する方法はありません(私は知っています)。

編集:単なるアイデアで、試したことはありません:書き込み()は、ソケットバッファがいっぱいの場合にのみブロックします。したがって、そのバッファーサイズ(SendBufferSize)を非常に低い値(8?1?0?)に下げると、必要なものが得られる可能性があります:)

于 2009-04-01T22:25:00.723 に答える
0

Flush()メソッドを使用するのはどうですか。

ns.Flush()

これにより、続行する前にデータが確実に書き込まれるようになります。

于 2008-09-15T22:57:12.217 に答える
-1

一応設定してみる tcp.NoDelay = true

于 2008-09-15T23:11:38.717 に答える