如何检查 TcpClient 连接是不是关闭?
Posted
技术标签:
【中文标题】如何检查 TcpClient 连接是不是关闭?【英文标题】:How to check if TcpClient Connection is closed?如何检查 TcpClient 连接是否关闭? 【发布时间】:2010-11-26 03:00:49 【问题描述】:我正在玩弄 TcpClient,并试图弄清楚如何在连接断开时使 Connected 属性为 false。
我试过了
NetworkStream ns = client.GetStream();
ns.Write(new byte[1], 0, 0);
但如果 TcpClient 断开连接,它仍然不会显示。你会如何使用 TcpClient 来解决这个问题?
【问题讨论】:
【参考方案1】:我不建议您尝试仅为测试套接字而编写。也不要在 .NET 的 Connected 属性上进行中继。
如果想知道远程端点是否还处于活动状态,可以使用TcpConnectionInformation:
TcpClient client = new TcpClient(host, port);
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections().Where(x => x.LocalEndPoint.Equals(client.Client.LocalEndPoint) && x.RemoteEndPoint.Equals(client.Client.RemoteEndPoint)).ToArray();
if (tcpConnections != null && tcpConnections.Length > 0)
TcpState stateOfConnection = tcpConnections.First().State;
if (stateOfConnection == TcpState.Established)
// Connection is OK
else
// No active tcp Connection to hostName:port
client.Close();
另请参阅:MSDN 上的TcpConnectionInformation MSDN 上的IPGlobalProperties TcpState 状态的描述 Wikipedia 上的网络统计
这里是作为 TcpClient 的扩展方法。
public static TcpState GetState(this TcpClient tcpClient)
var foo = IPGlobalProperties.GetIPGlobalProperties()
.GetActiveTcpConnections()
.SingleOrDefault(x => x.LocalEndPoint.Equals(tcpClient.Client.LocalEndPoint));
return foo != null ? foo.State : TcpState.Unknown;
【讨论】:
这是一个很棒的答案。您可以改进它的唯一方法是将测试呈现为 Socket 的扩展方法,该方法返回套接字状态。 不错的一个。我真的想知道是否有更快的方法来做到这一点 谢谢!你的回答是第一个对我有用的。我尝试了各种使用 client.Client.Poll 的答案,但它们都不起作用。 使用FirstOrDefault
而不是SingleOrDefault
换行,.SingleOrDefault(x => x.LocalEndPoint.Equals(tcpClient.Client.LocalEndPoint) && x.RemoteEndPoint.Equals(tcpClient.Client.RemoteEndPoint));【参考方案2】:
据我所知/记得,除了读取或写入之外,没有其他方法可以测试套接字是否已连接。
我根本没有使用过 TcpClient,但如果远程端已正常关闭,Socket 类将从对 Read 的调用中返回 0。 如果远程端没有正常关闭[我认为]你得到一个超时异常,不记得抱歉类型。
使用像 'if(socket.Connected) socket.Write(...)
这样的代码会产生竞争条件。最好只调用 socket.Write 并处理异常和/或断开连接。
【讨论】:
是的。套接字层应使用异常进行管理。抛出的 IOException 将内部异常设置为 SocketException,其中包含远程检测超时或关闭套接字所需的所有信息。【参考方案3】:Peter Wone 和 uriel 的解决方案非常好。但是您还需要检查远程端点,因为您可以与本地端点建立多个打开的连接。
public static TcpState GetState(this TcpClient tcpClient)
var foo = IPGlobalProperties.GetIPGlobalProperties()
.GetActiveTcpConnections()
.SingleOrDefault(x => x.LocalEndPoint.Equals(tcpClient.Client.LocalEndPoint)
&& x.RemoteEndPoint.Equals(tcpClient.Client.RemoteEndPoint)
);
return foo != null ? foo.State : TcpState.Unknown;
【讨论】:
【参考方案4】:我已经创建了这个函数并为我检查客户端是否仍然与服务器连接。
/// <summary>
/// THIS FUNCTION WILL CHECK IF CLIENT IS STILL CONNECTED WITH SERVER.
/// </summary>
/// <returns>FALSE IF NOT CONNECTED ELSE TRUE</returns>
public bool isClientConnected()
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections();
foreach (TcpConnectionInformation c in tcpConnections)
TcpState stateOfConnection = c.State;
if (c.LocalEndPoint.Equals(ClientSocket.Client.LocalEndPoint) && c.RemoteEndPoint.Equals(ClientSocket.Client.RemoteEndPoint))
if (stateOfConnection == TcpState.Established)
return true;
else
return false;
return false;
【讨论】:
【参考方案5】:@uriel 的回答对我很有用,但我需要在 C++/CLI 中对其进行编码,这并不完全是微不足道的。这是(大致等效的)C++/CLI 代码,添加了一些稳健性检查以进行良好的衡量。
using namespace System::Net::Sockets;
using namespace System::Net::NetworkInformation;
TcpState GetTcpConnectionState(TcpClient ^ tcpClient)
TcpState tcpState = TcpState::Unknown;
if (tcpClient != nullptr)
// Get all active TCP connections
IPGlobalProperties ^ ipProperties = IPGlobalProperties::GetIPGlobalProperties();
array<TcpConnectionInformation^> ^ tcpConnections = ipProperties->GetActiveTcpConnections();
if ((tcpConnections != nullptr) && (tcpConnections->Length > 0))
// Get the end points of the TCP connection in question
EndPoint ^ localEndPoint = tcpClient->Client->LocalEndPoint;
EndPoint ^ remoteEndPoint = tcpClient->Client->RemoteEndPoint;
// Run through all active TCP connections to locate TCP connection in question
for (int i = 0; i < tcpConnections->Length; i++)
if ((tcpConnections[i]->LocalEndPoint->Equals(localEndPoint)) && (tcpConnections[i]->RemoteEndPoint->Equals(remoteEndPoint)))
// Found active TCP connection in question
tcpState = tcpConnections[i]->State;
break;
return tcpState;
bool TcpConnected(TcpClient ^ tcpClient)
bool bTcpConnected = false;
if (tcpClient != nullptr)
if (GetTcpConnectionState(tcpClient) == TcpState::Established)
bTcpConnected = true;
return bTcpConnected;
希望这会对某人有所帮助。
【讨论】:
【参考方案6】:截至 2019 年,在跨平台和异步环境中,我使用以下代码持续检查 TCP 通道是否打开。此检查触发例如如果我的 Windows 机器上的以太网电缆被拔出,或者我的 android 设备上的 Wifi 被禁用。
private async Task TestConnectionLoop()
byte[] buffer = new byte[1];
ArraySegment<byte> arraySegment = new ArraySegment<byte>(buffer, 0, 0);
SocketFlags flags = SocketFlags.None;
while (!_cancellationSource.Token.IsCancellationRequested)
try
await _soc.SendAsync(arraySegment, flags);
await Task.Delay(500);
catch (Exception e)
_cancellationSource.Cancel();
// Others can listen to the Cancellation Token or you
// can do other actions here
【讨论】:
【参考方案7】:请注意,我发现 System.Net.Sockets.TcpClient
的 GSF.Communication
包装器很有帮助,因为它有一个 CurrentState
属性来指示套接字是打开/连接还是关闭/断开连接。您可以在此处找到有关 NuGet 包的详细信息:
https://github.com/GridProtectionAlliance/gsf
下面是如何设置一个简单的 TCP 套接字并测试它是否已连接:
GSF.Communication.TcpClient tcpClient;
void TestTcpConnectivity()
tcpClient = new GSF.Communication.TcpClient();
string myTCPServer = "localhost";
string myTCPport = "8080";
tcpClient.MaxConnectionAttempts = 5;
tcpClient.ConnectionAttempt += s_client_ConnectionAttempt;
tcpClient.ReceiveDataComplete += s_client_ReceiveDataComplete;
tcpClient.ConnectionException += s_client_ConnectionException;
tcpClient.ConnectionEstablished += s_client_ConnectionEstablished;
tcpClient.ConnectionTerminated += s_client_ConnectionTerminated;
tcpClient.ConnectionString = "Server=" + myTCPServer + ":" + myTCPport;
tcpClient.Initialize();
tcpClient.Connect();
Thread.Sleep(250);
if (tcpClient.CurrentState == ClientState.Connected)
Debug.WriteLine("Socket is connected");
// Do more stuff
else if (tcpClient.CurrentState == ClientState.Disconnected)
Debug.WriteLine(@"Socket didn't connect");
// Do other stuff or try again to connect
void s_client_ConnectionAttempt(object sender, EventArgs e)
Debug.WriteLine("Client is connecting to server.");
void s_client_ConnectionException(object sender, EventArgs e)
Debug.WriteLine("Client exception - 0.", e.Argument.Message);
void s_client_ConnectionEstablished(object sender, EventArgs e)
Debug.WriteLine("Client connected to server.");
void s_client_ConnectionTerminated(object sender, EventArgs e)
Debug.WriteLine("Client disconnected from server.");
void s_client_ReceiveDataComplete(object sender, GSF.EventArgs<byte[], int> e)
Debug.WriteLine(string.Format("Received data - 0.", tcpClient.TextEncoding.GetString(e.Argument1, 0, e.Argument2)));
【讨论】:
【参考方案8】:试试这个,它对我有用
private void timer1_Tick(object sender, EventArgs e)
if (client.Client.Poll(0, SelectMode.SelectRead))
if (!client.Connected) sConnected = false;
else
byte[] b = new byte[1];
try
if (client.Client.Receive(b, SocketFlags.Peek) == 0)
// Client disconnected
sConnected = false;
catch sConnected = false;
if (!sConnected)
//--Basically what you want to do afterwards
timer1.Stop();
client.Close();
ReConnect();
我使用 Timer 是因为我想定期检查连接状态 而不是在监听代码的循环中[我觉得它减慢了发送-接收过程]
【讨论】:
偶然发现了这个答案,只是好奇为什么它被否决了?【参考方案9】:就我而言,我正在向服务器发送一些命令(在同一台计算机上的虚拟机中运行)并等待响应。但是,如果服务器在等待时意外停止,我没有收到任何通知。我尝试了其他海报提出的可能性,但都没有奏效(它总是说服务器仍然连接)。对我来说,唯一有效的是将 0 字节写入流:
var client = new TcpClient();
//... open the client
var stream = client.GetStream();
//... send something to the client
byte[] empty = 0 ;
//wait for response from server
while (client.Available == 0)
//throws a SocketException if the connection is closed by the server
stream.Write(empty, 0, 0);
Thread.Sleep(10);
【讨论】:
以上是关于如何检查 TcpClient 连接是不是关闭?的主要内容,如果未能解决你的问题,请参考以下文章