随机"远程主机强行关闭现有连接." TCP重置后

Tob*_*asW 11 .net c# sockets tcp wireshark

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

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

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

在System.Net.Sockets.Socket.Receive(Byte []缓冲区,Int32偏移量,Int32大小,SocketFlags socketFlags)

在System.Net.Sockets.NetworkStream.Read(Byte []缓冲区,Int32偏移量,Int32 siz)

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

在System.Net.Sockets.NetworkStream.Read(Byte []缓冲区,Int32偏移量,Int32 siz)

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();
Run Code Online (Sandbox Code Playgroud)

服务器代码:

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

            }
Run Code Online (Sandbox Code Playgroud)

我使用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);
Run Code Online (Sandbox Code Playgroud)

在客户端接收时也一样.因为我首先要发送所有数据然后让服务器响应这适用于我的应用程序.

usr*_*usr 8

我看到两个问题:

  1. 您假设读取size字节实际上将读取size字节.它不是.TCP是一种流媒体协议.读取至少读取一个字节.这是唯一的保证.
  2. 你的代码会随机死锁.客户端将数据写入服务器.服务器回复它.但是,在写完所有内容之前,客户端不会阅读.如果它写入超过网络缓冲区可以采取这将死锁.客户端必须同时读写.可能需要另一个线程/任务来读回数据.解决该问题的一个好模式是启动一个编写器任务,一个读取器任务和Task.WhenAll/WaitAll将它们连接起来.

我不确定TCP堆栈在什么情况下会发送RST.可能是由于死锁导致超时.

你没有吞下任何例外,对吧?

通常,关闭连接会在后台执行有序关闭.但我不确定当对方还在写这个时候会发生什么.也许答案是RST是由写入的接收者生成的.当然,TCP规范将回答这个问题.如果要回答这个答案,那么确实关闭(读取)/关闭后跟传入写入将RST连接.

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