C# “随机”;远程主机强制关闭了现有连接。”;在TCP重置之后

C# “随机”;远程主机强制关闭了现有连接。”;在TCP重置之后,c#,.net,sockets,tcp,wireshark,C#,.net,Sockets,Tcp,Wireshark,我有两个部分,一个客户端和一个服务器。我尝试将数据(大小>5840字节)从客户端发送到服务器,然后服务器将数据发送回服务器。我循环了很多次,每次循环之间等待一秒钟。有时服务器应用程序崩溃,崩溃似乎非常随机,错误如下: 未处理的异常:System.IO.IOException:无法从传输连接读取数据:远程主机强制关闭了现有连接。--> System.Net.Sockets.SocketException:远程主机强制关闭了现有连接 位于System.Net.Sockets.Socket.Recei

我有两个部分,一个客户端和一个服务器。我尝试将数据(大小>5840字节)从客户端发送到服务器,然后服务器将数据发送回服务器。我循环了很多次,每次循环之间等待一秒钟。有时服务器应用程序崩溃,崩溃似乎非常随机,错误如下:

未处理的异常:System.IO.IOException:无法从传输连接读取数据:远程主机强制关闭了现有连接。-->

System.Net.Sockets.SocketException:远程主机强制关闭了现有连接

位于System.Net.Sockets.Socket.Receive(字节[]缓冲区、Int32偏移量、Int32大小、SocketFlags和SocketFlags)

位于System.Net.Sockets.NetworkStream.Read(字节[]缓冲区,Int32偏移量,Int32 s (放大)

---内部异常堆栈跟踪的结束---

位于System.Net.Sockets.NetworkStream.Read(字节[]缓冲区,Int32偏移量,Int32 s (放大)

在TCP_Server.Program.Main上(字符串[]args)

客户端代码(在循环中):

服务器代码:

            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);

            }
Byte[]bytes=新字节[大小];
//进入监听循环。
对于(int i=0;i
我使用wireshark监视发送的tcp包,发现在程序崩溃之前,tcp RST从客户端发送到服务器。所以我假设问题在于RST没有正确处理。 客户端和主机之间没有防火墙,所以这不是问题所在

客户端和服务器的wireshark文件如下:

所以,要么我需要摆脱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);
//循环以接收客户端发送的所有数据。
int k=0;
while(k

在客户端接收时也是如此。因为我首先要发送所有数据,然后让服务器响应,这对我的应用程序很有效。

我不是专家,但您不认为在客户端调用stream.Close()会在服务器仍在尝试写入时关闭流吗

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

另外,您可能需要放置一些数据以标记其结束,以便服务器知道并停止读取。

我看到两个问题:

  • 您假设对
    大小
    字节的读取实际上将读取
    大小
    字节。事实并非如此。TCP是一种流协议。读操作至少读取一个字节。这是唯一的保证
  • 您的代码将随机死锁。客户端将数据写入服务器。服务器将其回显。但是客户端在写下所有内容之前不会阅读。如果写入的数据超过了网络缓冲区的容量,则会导致死锁。客户端必须同时读写。可能需要另一个线程/任务来读取数据。解决这个问题的一个好模式是启动一个writer任务、一个reader任务和一个task.WhenAll/WaitAll任务,然后将它们重新加入
  • 我不确定TCP堆栈在什么具体情况下会发送RST。可能是由于死锁导致超时

    你没有接受任何例外,对吗

    通常,关闭连接会在后台执行有序关机。但我不确定当另一方此时仍在写作时会发生什么。也许答案是RST是由写入的接收器生成的。当然,TCP规范会回答这个问题。如果是可信的,那么关闭(读取)/关闭后再输入写入将重新启动连接


    修复这两个问题并报告您的发现。

    我通过增加iis根条目的缓存限制解决了相同的问题。您还可以禁用缓存。希望有帮助。

    我们在使用HttpClient随机错误时遇到了这个问题,例如

    System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner 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
    

    TCP堆栈中的显式拥塞通知(ECN)存在问题。这也称为ECN能力。显式拥塞通知是Internet协议和传输控制协议的扩展,在RFC 3168中定义。ECN允许在不丢弃数据包的情况下发送网络拥塞的端到端通知。 如果启用了ECN功能,您可以禁用它:

    netsh interface tcp set global ecncapability=disabled
    

    不需要重新启动。

    可能需要,您知道要更改什么吗?我已经被这个问题困扰了2天了,所以我真的厌倦了。要么发送一些可能包含数据长度的标题,要么在数据中附加一些唯一的序列来标记它的结束。在服务器进程中,这需要知道服务器应该何时结束读取。在其他回答中也实施usr的建议。或者只需先进行单向传输,这样您就可以锁定问题。为了避免复杂性,您可以在不同的端口上读写,如果这对您来说是可行的
     ---> System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send.
    
    netsh interface tcp set global ecncapability=disabled