C# .Net套接字没有';无法响应远程断开?
我正在编写一个小型(C#)客户端应用程序,它使用TCP/IP连接将数据发送到远程服务器。我正在使用标准的.Net TcpClient对象,并希望在定期向服务器提交数据包时保持连接从客户端打开。但是,服务器可能会关闭连接,在这种情况下,在发送下一个数据包之前,我需要知道如何重新连接 使用Wireshark,当服务器终止连接时,我可以看到(仅)以下对话框:C# .Net套接字没有';无法响应远程断开?,c#,.net,sockets,tcpclient,C#,.net,Sockets,Tcpclient,我正在编写一个小型(C#)客户端应用程序,它使用TCP/IP连接将数据发送到远程服务器。我正在使用标准的.Net TcpClient对象,并希望在定期向服务器提交数据包时保持连接从客户端打开。但是,服务器可能会关闭连接,在这种情况下,在发送下一个数据包之前,我需要知道如何重新连接 使用Wireshark,当服务器终止连接时,我可以看到(仅)以下对话框: server>>FIN,ACKACK通常,您应该能够在TcpCient实例上使用Connected属性: 请参见此处: 然而: 因为连接的属性
server>>FIN,ACK
ACK通常,您应该能够在TcpCient
实例上使用Connected
属性:
请参见此处:
然而:
因为连接的属性只反映了
从最近的操作开始,您应该尝试发送
或接收消息以确定当前状态。留言后
发送失败,此属性不再返回true。请注意
行为是故意的。您无法可靠地测试服务器的状态
连接,因为在测试和发送/接收之间,
连接可能已断开。您的代码应该假定
套接字已连接,并能正常处理失败的传输
请尝试以下操作,以确保已连接
标志保持最新状态:
var tcpClient = new TcpClient ();
tcpClient.Connect();
var stream = tcpClient.GetStream();
// buffer size need to be > 0
int[] buffer = new int[1];
stream.Read(buffer, 0, 0);
if(!tcpClient.Connected)
// do something
基于反编译,应该可以从流中读取0
字节,至少.NET FrameworkTcpClient
中没有阻止这种情况的检查。但是,在从框架调用以实际从网络流读取的外部代码中,它可能不会发出声音
确保TcpClient
和流的Dispose
完成后,处置TcpClient
不会处置流
,因此需要手动执行此操作,然后释放所有资源(在GC之后).我找到了问题的部分答案,解决了眼前的问题
虽然我仍然不知道是否可以让我的TcpClient完成断开连接,但我可以使用以下代码可靠地发现套接字是否已断开连接:
if (m_client.Client.Poll(1000, SelectMode.SelectRead)
&& (m_client.Client.Available == 0))
{
// Connection has gone - reconnect!
m_client = new TcpClient(AddressFamily.InterNetwork);
m_client.Connect(ipAddress, ipPort);
}
else
{
// Connection is good, nothing to do
}
来自MSDN属性:
类型:System.Boolean
如果客户端套接字在最近一次操作时已连接到远程资源,则为true;否则,错误
这意味着,您必须向服务器发送一些数据以检测断开的连接。从缓冲区读取时,读取不起作用
请参见我对相关问题的回答(),
链接其他人的答案,其中描述了检测连接状态的合适方法:
对您很重要:
当服务器自动关闭连接时,客户端不会自动关闭连接。连接在客户端处于CLOSE_WAIT状态,然后在服务器端处于FIN_WAIT2状态。请参阅维基百科文章中的相关部分。使用上面链接答案中的代码,您可以检测到连接即将关闭。此外,您还可以完成关闭过程,然后在需要时重新打开它。我用于检测连接状态的方法是此方法
static class SocketExtensions
{
/// <summary>
/// Extension method to tell if the Socket REALLY is closed
/// </summary>
/// <param name="socket"></param>
/// <returns></returns>
public static bool IsConnected(this Socket socket)
{
try
{
return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
}
catch (SocketException) { return false; }
}
}
静态类SocketExtensions
{
///
///用于判断套接字是否确实已关闭的扩展方法
///
///
///
公共静态端口已断开连接(此插槽)
{
尝试
{
return!(socket.Poll(1,SelectMode.SelectRead)和&socket.Available==0);
}
catch(SocketException){return false;}
}
}
当我想关闭连接时,我调用以下命令。关闭底层流,然后关闭顶部的客户端对象。
我将其包含在trys和catch中,以确保在每个trys和catch上都尝试关闭它们。
注意:本例中的PeerStream是NetworkStream(来自Client.GetStream())
//
///方法将强制断开此对等方的连接
///
公共空间断开连接()
{
尝试
{
PeerStream.Close();
}
捕获(异常ee)
{
}
尝试
{
_client.client.Disconnect(false);
}
捕获(异常ee)
{
}
}
感谢您的评论,但文档中指出,只有在上次读/写时,Connected才是正确的。我担心的是,TcpClient正在使连接处于半废弃状态。我已更新了我的答案,以便为您提供解决方法。但一般来说,框架处理Tcp连接的方式是,假设它是开放的,并且在不开放的情况下可以正常处理。您还负责清理TcpClient
和流
,如我上一段所述。非常感谢您的更新-我猜流.Read
调用与我发现的套接字调用基本相同,但是在更高的抽象级别上。更高级别的代码-NetworkStream实际上存在一个问题。至少在新打开的连接和流上,即使读取零字节,读取也会阻塞,而较低级别的套接字调用在相同的情况下不会阻塞。thx,很高兴知道!不过,您可以设置读取超时,一个非常小的超时,例如,通过这种方式,您可以在短时间内阻止流阻塞。很好,只需注意:与其专注于检测连接是否打开,最好的做法是假设连接已打开,而不是准备代码以优雅地处理异常,由于无法判断它是否已在您的支票和您发送/接收数据之间关闭,因此您仍然需要处理这些异常。是的,我的代码设置为处理异常
static class SocketExtensions
{
/// <summary>
/// Extension method to tell if the Socket REALLY is closed
/// </summary>
/// <param name="socket"></param>
/// <returns></returns>
public static bool IsConnected(this Socket socket)
{
try
{
return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
}
catch (SocketException) { return false; }
}
}
/// <summary>
/// Method will disconnect this peer forcefully
/// </summary>
public void Disconnect()
{
try
{
PeerStream.Close();
}
catch (Exception ee)
{
}
try
{
_client.Client.Disconnect(false);
}
catch (Exception ee)
{
}
}