C# .Net套接字没有';无法响应远程断开?

C# .Net套接字没有';无法响应远程断开?,c#,.net,sockets,tcpclient,C#,.net,Sockets,Tcpclient,我正在编写一个小型(C#)客户端应用程序,它使用TCP/IP连接将数据发送到远程服务器。我正在使用标准的.Net TcpClient对象,并希望在定期向服务器提交数据包时保持连接从客户端打开。但是,服务器可能会关闭连接,在这种情况下,在发送下一个数据包之前,我需要知道如何重新连接 使用Wireshark,当服务器终止连接时,我可以看到(仅)以下对话框: server>>FIN,ACKACK通常,您应该能够在TcpCient实例上使用Connected属性: 请参见此处: 然而: 因为连接的属性

我正在编写一个小型(C#)客户端应用程序,它使用TCP/IP连接将数据发送到远程服务器。我正在使用标准的.Net TcpClient对象,并希望在定期向服务器提交数据包时保持连接从客户端打开。但是,服务器可能会关闭连接,在这种情况下,在发送下一个数据包之前,我需要知道如何重新连接

使用Wireshark,当服务器终止连接时,我可以看到(仅)以下对话框:


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 Framework
TcpClient
中没有阻止这种情况的检查。但是,在从框架调用以实际从网络流读取的外部代码中,它可能不会发出声音


确保
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)
            {

            }
        }