gpt4 book ai didi

c# - TcpClient 异常死锁

转载 作者:行者123 更新时间:2023-11-30 12:41:39 25 4
gpt4 key购买 nike

我在继承的一些代码中有一个奇怪的行为 - 下面的简化示例演示了如果内置到普通控制台应用程序中的问题。

WhoIs 在第 5 次调用时达到其使用限额 - 并返回一条消息 + 关闭套接字。使用 ReadLineAsync 这会产生一个 SocketException 和一些 IOExceptions - 有时它们会在 catch block 中被捕获并且一切都应该如此,大多数时候它们没有被捕获并且程序只是挂起 - Break all in the VS debugger 向我展示了其中一个在主线程上调用 Console.WriteLine。当直接在调试器外部运行 .exe 文件时,此行为仍然存在。

谁能看出这是什么/为什么会这样?

我实际上可以通过使用 Peek() 来解决我的问题,但我想知道除了未被捕获的异常之外发生了什么 - 以及“死锁”。大概是某种线程或上下文问题。如果这是我正在做的事情,我想知道是什么,这样我就可以在其他地方避免它!

using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace AsyncIssueConsoleApplication
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result);
Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result);
Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result);
Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result);
Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result);
Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result);
Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result);
Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result);
Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result);
Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result);
Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result);
Console.ReadLine();
}

private static async Task<string> LookupAsync(string domain)
{
StringBuilder builder = new StringBuilder();
TcpClient tcp = new TcpClient();
await tcp.ConnectAsync("whois.pir.org", 43).ConfigureAwait(false);
string strDomain = "" + domain + "\r\n";
byte[] bytDomain = Encoding.ASCII.GetBytes(strDomain.ToCharArray());
try
{
using (Stream s = tcp.GetStream())
{
await s.WriteAsync(bytDomain, 0, strDomain.Length).ConfigureAwait(false);
using (StreamReader sr = new StreamReader(s, Encoding.ASCII))
{
try
{
//This is fine
/*while (sr.Peek() >= 0)
{
builder.AppendLine(await sr.ReadLineAsync());
}*/

//This isn't - produces SocketException which usually isn't caught below
string strLine = await sr.ReadLineAsync().ConfigureAwait(false);
while (null != strLine)
{
builder.AppendLine(strLine);
strLine = await sr.ReadLineAsync().ConfigureAwait(false);
}
}
catch (Exception e)
{
//Sometimes the SocketException/IOException is caught, sometimes not
return builder.ToString();
}
}
}

}
catch (Exception e)
{
return builder.ToString();
}
return builder.ToString();
}
}
}

建议duplicate Q&A可能相关但没有回答我可以看到的这个查询,当然不完全:即我需要对 SynchronizationContext 做些什么 - 我已经在使用 ConfigureAwait(false)。

当代码如上所述死锁时,堆栈跟踪是:

mscorlib.dll!System.Threading.Monitor.Wait(object obj, int millisecondsTimeout, bool exitContext)   Unknown
mscorlib.dll!System.Threading.Monitor.Wait(object obj, int millisecondsTimeout) Unknown
mscorlib.dll!System.Threading.ManualResetEventSlim.Wait(int millisecondsTimeout = -1, System.Threading.CancellationToken cancellationToken) Unknown
mscorlib.dll!System.Threading.Tasks.Task.SpinThenBlockingWait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Unknown
mscorlib.dll!System.Threading.Tasks.Task.InternalWait(int millisecondsTimeout = -1, System.Threading.CancellationToken cancellationToken) Unknown
mscorlib.dll!System.Threading.Tasks.Task<string>.GetResultCore(bool waitCompletionNotification = true) Unknown
mscorlib.dll!System.Threading.Tasks.Task<System.__Canon>.Result.get() Unknown
AsyncIssueConsoleApplication.exe!AsyncIssueConsoleApplication.Program.Main(string[] args = {string[0]}) Line 18 C#

IOException 是:{“无法从传输连接读取数据:现有连接被远程主机强行关闭。”}

SocketException 是:{“已建立的连接被您主机中的软件中止”}

最佳答案

我现在可以重现它。为了查看它卡在哪里,我切换到同步 IO。异步 IO 的一个常见调试问题是您看不到当前有哪些 IO 处于挂起状态。这是您一开始可能不想使用异步 IO 的一个关键原因。

enter image description here

enter image description here

挂起是因为远程服务器没有关闭连接。 ReadLine 调用只会在远程端关闭连接时结束。

这可能是速率限制代码中的错误。它也可能是协议(protocol)的预期行为。也许你打算现在发送下一个请求?或者,也许您应该检测上一行的速率限制并自行关闭。

这不是线程问题。根本没有并发发生。所有 LookupAsync 实例都按顺序运行。

我还尝试正确关闭 TcpClient,以防远程服务器在面对多个连接时表现不同。没有效果。不过,无论如何您都应该处置您的资源。这是一次严重的泄漏。

关于c# - TcpClient 异常死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36919035/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com