TCP 重置后随机 "An existing connection was forcibly closed by the remote host."
Random "An existing connection was forcibly closed by the remote host." after a TCP reset
我有两部分,客户端和服务器。我尝试从客户端向服务器发送数据(大小 > 5840 字节),然后服务器将数据发回。我循环了很多次,每次之间等待一秒钟。有时服务器应用程序崩溃,崩溃似乎很随机错误:
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();
服务器代码:
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);
在客户端接收时也是如此。因为我首先要发送所有数据,然后让服务器响应,所以这适用于我的应用程序。
我不是专家,但你不认为在客户端上调用 stream.Close() 会在服务器仍在尝试写入
时关闭流
// Send back a response.
stream.Write(bytes, 0, size);
您可能还想放一些数据来标记结束,以便服务器知道并停止读取。
我看到的两个问题:
- 您假设读取
size
个字节实际上将读取 size
个字节。它不是。 TCP 是一种流式传输协议。一次读取至少读取一个字节。这是唯一的保证。
- 您的代码会随机死锁。客户端向服务器写入数据。服务器回应它。但是客户端在写完所有内容之前不会阅读。如果它写入的内容超过了网络缓冲区的容量,这将导致死锁。客户端必须同时读取和写入。可能,您需要另一个 thread/task 来读回数据。解决该问题的一个好模式是启动一个编写器任务、一个 reader 任务和任务。WhenAll/WaitAll 将它们加入回来。
我不确定 TCP 堆栈在什么情况下会发送 RST。可能是由于死锁导致超时。
你没有吞下任何异常,对吧?
通常情况下,关闭连接会在后台执行有序关闭。但我不确定此时对方仍在写作时会发生什么。也许答案是 RST 是由写入的接收者生成的。当然,TCP 规范会回答这个问题。如果 this answer 是可信的,那么关闭(读取)/关闭后跟传入的写入确实会 RST 连接。
解决这两个问题并报告您的发现。
我通过增加 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
或
---> System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send.
TCP 堆栈中的显式拥塞通知 (ECN) 存在问题。这也称为 ECN 能力。显式拥塞通知是 Internet 协议和传输控制协议的扩展,在 RFC 3168 中定义。ECN 允许在不丢弃数据包的情况下进行端到端的网络拥塞通知。
如果启用了 ECN 功能,您可以将其禁用:
netsh interface tcp set global ecncapability=disabled
不需要重启。
我有两部分,客户端和服务器。我尝试从客户端向服务器发送数据(大小 > 5840 字节),然后服务器将数据发回。我循环了很多次,每次之间等待一秒钟。有时服务器应用程序崩溃,崩溃似乎很随机错误:
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();
服务器代码:
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);
在客户端接收时也是如此。因为我首先要发送所有数据,然后让服务器响应,所以这适用于我的应用程序。
我不是专家,但你不认为在客户端上调用 stream.Close() 会在服务器仍在尝试写入
时关闭流// Send back a response.
stream.Write(bytes, 0, size);
您可能还想放一些数据来标记结束,以便服务器知道并停止读取。
我看到的两个问题:
- 您假设读取
size
个字节实际上将读取size
个字节。它不是。 TCP 是一种流式传输协议。一次读取至少读取一个字节。这是唯一的保证。 - 您的代码会随机死锁。客户端向服务器写入数据。服务器回应它。但是客户端在写完所有内容之前不会阅读。如果它写入的内容超过了网络缓冲区的容量,这将导致死锁。客户端必须同时读取和写入。可能,您需要另一个 thread/task 来读回数据。解决该问题的一个好模式是启动一个编写器任务、一个 reader 任务和任务。WhenAll/WaitAll 将它们加入回来。
我不确定 TCP 堆栈在什么情况下会发送 RST。可能是由于死锁导致超时。
你没有吞下任何异常,对吧?
通常情况下,关闭连接会在后台执行有序关闭。但我不确定此时对方仍在写作时会发生什么。也许答案是 RST 是由写入的接收者生成的。当然,TCP 规范会回答这个问题。如果 this answer 是可信的,那么关闭(读取)/关闭后跟传入的写入确实会 RST 连接。
解决这两个问题并报告您的发现。
我通过增加 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
或
---> System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send.
TCP 堆栈中的显式拥塞通知 (ECN) 存在问题。这也称为 ECN 能力。显式拥塞通知是 Internet 协议和传输控制协议的扩展,在 RFC 3168 中定义。ECN 允许在不丢弃数据包的情况下进行端到端的网络拥塞通知。 如果启用了 ECN 功能,您可以将其禁用:
netsh interface tcp set global ecncapability=disabled
不需要重启。