重用异步套接字:后续连接尝试失败
我试图在异步 HTTP 客户端中重用套接字,但我无法第二次连接到主机。我基本上将异步 HTTP 客户端视为具有以下状态的状态机:
- 可用:套接字可供使用
- 连接:套接字正在连接到端点
- 发送:套接字正在向端点发送数据
- 接收:套接字正在接收数据从端点
- 失败:套接字失败
- 清理:清理套接字状态
在连接状态中,我调用 BeginConnect
:
private void BeginConnect()
{
lock (_sync) // re-entrant lock
{
IPAddress[] addersses = Dns.GetHostEntry(_asyncTask.Host).AddressList;
// Connect to any available address
IAsyncResult result = _reusableSocket.BeginConnect(addersses, _asyncTask.Port, new AsyncCallback(ConnectCallback), null);
}
}
回调方法将状态更改为 Sending
一次已成功建立连接:
private void ConnectCallback(IAsyncResult result)
{
lock (_sync) // re-entrant lock
{
try
{
_reusableSocket.EndConnect(result);
ChangeState(EClientState.Sending);
}
catch (SocketException e)
{
Console.WriteLine("Can't connect to: " + _asyncTask.Host);
Console.WriteLine("SocketException: {0} Error Code: {1}", e.Message, e.NativeErrorCode);
ThreadPool.QueueUserWorkItem(o =>
{
// An attempt was made to get the page so perform a callback
ChangeState(EClientState.Failed);
});
}
}
}
在清理过程中,我关闭
套接字并使用重用标志断开
:
private void CleanUp()
{
lock (_sync) // re-entrant lock
{
// Perform cleanup
if (_reusableSocket.Connected)
{
_reusableSocket.Shutdown(SocketShutdown.Both);
_reusableSocket.Disconnect(true);
}
ChangeState(EClientState.Available);
}
}
后续调用BeginConnect
会导致超时,并且一个例外:
SocketException:连接尝试 失败是因为关联方这样做了 一段时间后未能正确回应 时间,或建立的连接失败 因为连接的主机未能 回复 XX.XXX.XX.XX:80
错误代码:10060
这是状态跟踪:
Initializing...
Change State: Connecting
Change State: Sending
Change State: Receiving
Change State: CleanUp
Callback: Received data from client 0 // <--- Received the first data
Change State: Available
Change State: Connecting // <--- Timeout when I try to reuse the socket to connect to a different endpoint
我需要做什么才能重用套接字来连接到不同的主机?
注意:我not尝试重新连接到同一主机,但我假设发生了同样的事情(即连接失败)。
更新
我在 BeginConnect 文档中找到了以下注释:
如果此套接字先前已断开连接,则必须在操作完成之前不会退出的线程上调用 BeginConnect。这是底层提供商的限制。此外,使用的 EndPoint 也必须不同。
我开始怀疑我的问题是否与此有关...我正在连接到不同的 EndPoint,但是我们调用 BeginConnect 的线程在操作完成之前不得退出是什么意思?
更新2.0:
我问了一个相关问题,我尝试使用“异步系列”电话而不是“开始家庭”电话,但我遇到了同样的问题!
I'm trying to reuse a socket in an asynchronous HTTP client, but I'm not able to connect to the host the second time around. I basically treat my asynchronous HTTP client as a state machine with the following states:
- Available: the socket is available for use
- Connecting: the socket is connecting to the endpoint
- Sending: the socket is sending data to the endpoint
- Receiving: the socket is receiving data from the endpoint
- Failed: there was a socket failure
- Clean Up: cleaning up the socket state
In the connecting state I call BeginConnect
:
private void BeginConnect()
{
lock (_sync) // re-entrant lock
{
IPAddress[] addersses = Dns.GetHostEntry(_asyncTask.Host).AddressList;
// Connect to any available address
IAsyncResult result = _reusableSocket.BeginConnect(addersses, _asyncTask.Port, new AsyncCallback(ConnectCallback), null);
}
}
The callback method changes the state to Sending
once a successful connection has been established:
private void ConnectCallback(IAsyncResult result)
{
lock (_sync) // re-entrant lock
{
try
{
_reusableSocket.EndConnect(result);
ChangeState(EClientState.Sending);
}
catch (SocketException e)
{
Console.WriteLine("Can't connect to: " + _asyncTask.Host);
Console.WriteLine("SocketException: {0} Error Code: {1}", e.Message, e.NativeErrorCode);
ThreadPool.QueueUserWorkItem(o =>
{
// An attempt was made to get the page so perform a callback
ChangeState(EClientState.Failed);
});
}
}
}
In the cleanup I Shutdown
the socket and Disconnect
with a reuse flag:
private void CleanUp()
{
lock (_sync) // re-entrant lock
{
// Perform cleanup
if (_reusableSocket.Connected)
{
_reusableSocket.Shutdown(SocketShutdown.Both);
_reusableSocket.Disconnect(true);
}
ChangeState(EClientState.Available);
}
}
Subsequent calls to BeginConnect
result in a timeout and an exception:
SocketException: A connection attempt
failed because the connected party did
not properly respond after a period of
time, or established connection failed
because connected host has failed to
respond XX.XXX.XX.XX:80Error Code: 10060
Here is the state trace:
Initializing...
Change State: Connecting
Change State: Sending
Change State: Receiving
Change State: CleanUp
Callback: Received data from client 0 // <--- Received the first data
Change State: Available
Change State: Connecting // <--- Timeout when I try to reuse the socket to connect to a different endpoint
What do I have to do to be able to reuse the socket to connect to a different host?
Note: I have not tried to re-connect to the same host, but I assume the same thing happens (i.e. fails to connect).
Update
I found the following note in the documentation of BeginConnect:
If this socket has previously been disconnected, then BeginConnect must be called on a thread that will not exit until the operation is complete. This is a limitation of the underlying provider. Also the EndPoint that is used must be different.
I'm starting to wonder if my issue has something to do with that... I am connecting to a different EndPoint, but what do they mean that the thread from which we call BeginConnect must not exit until the operation is complete?
Update 2.0:
I asked a related question and I tried using the "Async family" calls instead of the "Begin family" calls, but I get the same problem!!!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我评论了这个问题:套接字重用有什么好处在 C# 中,关于使用
Disconnect(true)/DisconnectEx()
进行套接字重用,这可能会对您有所帮助。就我个人而言,我认为客户端代码的优化太过分了。
重新更新 1 你的问题;不,如果是这种情况,您会收到 AbortedOperation 异常(请参见此处:部署时出现 VB.NET 3.5 SocketException,但开发机器上未出现)和如果您在 Vista 或更高版本上运行,则文档是错误的,因为它不强制执行以前的操作系统强制执行的“线程必须存在,直到重叠 I/O 完成后”规则。
正如我在回答链接问题时已经说过的;使用此功能来建立出站连接没有什么意义。它最初可能被添加到 Winsock API 中,以支持入站连接上
AcceptEx()
的套接字重用,其中,在使用TransmitFile()
的非常繁忙的 Web 服务器上> 将文件发送到客户端(这似乎是断开重用的起源)。文档指出它不能很好地与 TIME_WAIT 配合使用,因此将其用于启动主动关闭的连接(从而将套接字置于 TIME_WAIT 中,请参阅此处)实际上没有意义。您能解释一下为什么您认为这种微观优化在您的情况下实际上是必要的吗?
I commented on this question: what is benefit from socket reuse in C# about socket reuse using
Disconnect(true)/DisconnectEx()
and this may help you.Personally I think it's an optimisation too far in client code.
Re update 1 to your question; no, you'd get an AbortedOperation exception if that were the case (see here: VB.NET 3.5 SocketException on deployment but not on development machine) and the docs are wrong if you're running on Vista or later as it doesn't enforce the "thread must exist until after overlapped I/O completes" rule that previous operating systems enforce.
As I've already said in the reply to the linked question; there's very little point in using this functionality for outbound connection establishment. It's likely that it was originally added to the Winsock API to support socket reuse for
AcceptEx()
on inbound connections, where, on a very busy web server that was usingTransmitFile()
to send files to clients (which is where disconnect for reused seems to have originated). The docs state that it doesn't play well withTIME_WAIT
and so using it for connections where you initiate the active close (and thus put the socket intoTIME_WAIT
, see here) doesn't really make sense.Can you explain why you think this micro optimisation is actually necessary in your case?
您检查过 MaxConnections 设置吗?
http://msdn.microsoft.com/de-de /library/system.servicemodel.nettcpbinding.maxconnections.aspx
have you checked the MaxConnections Setting?
http://msdn.microsoft.com/de-de/library/system.servicemodel.nettcpbinding.maxconnections.aspx