2015-09-10 17 views
9

私はクライアントとサーバーの2つの部分を持っています。そして、クライアントからサーバーにデータ(サイズ> 5840バイト)を送信しようとすると、サーバーからデータが返されます。私はこれを毎回何回も待っています。ランダム "既存の接続はリモートホストによって強制的に閉じられました。" TCPリセット後

Unhandled Exception: System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. --->

System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host

at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)

at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 s ize)

--- End of inner exception stack trace ---

at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 s ize)

at TCP_Server.Program.Main(String[] args)

クライアントコード(これはループ内で):

  try 
      { 
       Int32 port = 13777; 
       using (TcpClient client = new TcpClient(ip, port)) 
       using (NetworkStream stream = client.GetStream()) 
       { 
        client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); 
        var data = GenerateData(size); 

        sw.Start(); 
        // Send the message to the connected TcpServer. 
        stream.Write(data, 0, data.Length); 

        // Buffer to store the response bytes. 
        data = new Byte[size]; 

        // Read the first batch of the TcpServer response bytes. 
        Int32 bytes = stream.Read(data, 0, data.Length); 

        sw.Stop(); 
        Console.WriteLine(i + ": Done transporting " + size + " bytes to and from " + ip + " time: " + 
             sw.ElapsedMilliseconds + " ms"); 
        // Close everything. 
        stream.Close(); 
        client.Close(); 
       } 

      } 
      catch (ArgumentNullException e) 
      { 
       Console.WriteLine("ArgumentNullException: {0}", e); 
      } 
      catch (SocketException e) 
      { 
       Console.WriteLine("SocketException: {0}", e); 
      } 

      sw.Reset(); 

Serverコード:

  Byte[] bytes = new Byte[size]; 

      // Enter the listening loop. 
      for (int i = 0; i < numberOfPackages; i++) 
      { 
       using (TcpClient client = server.AcceptTcpClient()) 
       using (NetworkStream stream = client.GetStream()) 
       { 
        client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); 
        // Loop to receive all the data sent by the client. 
        while ((stream.Read(bytes, 0, bytes.Length)) != 0) 
        { 
         // Send back a response. 
         stream.Write(bytes, 0, size); 

        } 
        client.GetStream().Close(); 
        client.Close(); 
       } 


       Console.WriteLine("Receive data size " + size); 

      } 

私はいつかサーバアプリケーションのクラッシュ、クラッシュがエラー非常にランダムなようですwiresharkを使用して送信されたtcpパッケージを監視し、プログラムがクラッシュする前にクライアントからサーバーにTCP RSTが送信されたことを確認しました。だから私は問題は、RSTが正しく処理されていないと仮定します。 クライアントとホストの間にファイアウォールがないため、問題はありません。

クライアントとサーバの両方のためのwiresharkのファイルはここにある:https://www.dropbox.com/sh/ctl2chq3y2c20n7/AACgIJ8IRiclqnyOyw8sqd9La?dl=0

だから、どちらか私はTCP RSTを取り除く必要があるか、私はいくつかの方法でそれを処理し、クラッシュしないように自分のサーバーを必要としています。

私は長い待ち時間を使用しようとしましたが、それは役に立ちません。 データが5840バイト未満の場合は、私が知っているクラッシュはありません。

ご意見やご提案はありますか?

編集:私はそれは次のように変更して動作するようになったの答えに ありがとう:

サーバー:

// Loop to receive all the data sent by the client. 
int k = 0; 
while (k < size) 
{ 
    int bytesRead = stream.Read(bytes, 0, bytes.Length); 
    k += bytesRead; 
} 
// Send back a response. 
stream.Write(bytes, 0, size); 

そして、クライアント側で受信したときと同じ。私は最初にすべてのデータを送信してから、サーバーが自分のアプリケーションに対してこの作業に応答するようにしたいからです。

答えて

7

2つの問題:

  1. あなたはsizeバイトの読み取りは、実際にsizeバイトを読み取ることを想定しています。それはしません。 TCPはストリーミングプロトコルです。 readは少なくとも1バイトを読み込みます。それが唯一の保証です。
  2. コードはランダムにデッドロックします。クライアントはサーバーにデータを書き込みます。サーバーはそれをエコーバックします。しかし、クライアントはすべてが書かれるまでは読まない。ネットワークバッファー以上に書き込みを行うと、デッドロックになります。クライアントは同時に読み書きが必要です。おそらく、データを読み取るための別のスレッド/タスクが必要になるでしょう。その問題を解決する良いパターンは、1つのライタータスク、1つのリーダータスク、およびTask.WhenAll/WaitAllにそれらを戻すことです。

TCPスタックがRSTを送信する正確な状況ではわかりません。デッドロックが原因でタイムアウトが発生した可能性があります。

あなたは例外を飲み込んでいませんよね?

通常、接続を閉じると、バックグラウンドで正常にシャットダウンが実行されます。しかし、私は、相手側がまだこの時点で書いているときに何が起こるのか不明です。答えは、RSTが書き込みの受信者によって生成されるということです。確かに、TCP仕様はこの質問に答えるでしょう。 this answerが信頼される場合、シャットダウン(読み取り)/閉じるの後に着信書き込みが続くと、接続はRSTになります。

両方の問題を修正し、結果を報告してください。

+0

ありがとうございました!!! 私はアプリケーションを送信し、受信し、同時に実行しないので、最初の問題を修正する必要がありました。 – TobiasW

+0

@TobiasWあなたはデッドロックの問題を理解していないと思います。このコードはランダムにデッドロックします。 100MBを送信しようとすると、デッドロックが発生します。 – usr

+0

最初にクライアントからすべてを送信しても、サーバーはすべてを受信して​​から送信します。この後、私は待って、すべてをやり直す。このコードは実際には使用されておらず、ベンチマークアプリケーションのほうが多いです。 – TobiasW

0

私は専門家でないが、あなたはクライアント上で()stream.Closeを呼び出すと、サーバーがまだ

// Send back a response. 
stream.Write(bytes, 0, size); 

に書き込もうとしている間にも、あなたがマークにいくつかのデータを配置することがストリームを閉じるとは思いませんそれが終わると、サーバーは読み取りを知り、読み取りを停止します。私が見

+0

変更する必要はありますか?私は今この問題に2日間悩まされているので、本当にうんざりしています。 – TobiasW

+0

データの長さを含むヘッダーを送信するか、データの終わりを示すためにデータに固有のシーケンスを追加してください。サーバプロセスでは、サーバがいつ読書を終了するのかを知ることができます。また、他の答えでは、usrによる提案を実装します。または、片方向転送で最初に作業するだけで問題を特定できます。 – user2527768

+0

そして、複雑なことを避けるために、それが可能ならば、異なるポートで読み書きすることができます。 – user2527768

0

iisルートエントリのキャッシュ制限を増やして同じ問題を解決しました。キャッシュを無効にすることもできます。それが役に立てば幸い。