C# 如何在连接TCPClient后测试其断开的连接?
我已经为一个问题奋斗了整整3天,我找不到任何解决方案,请帮助:)C# 如何在连接TCPClient后测试其断开的连接?,c#,visual-studio-2010,tcp,C#,Visual Studio 2010,Tcp,我已经为一个问题奋斗了整整3天,我找不到任何解决方案,请帮助:) 我使用Visual Studio 2010和C语言 我有一个像服务器一样工作的设备,它在非常不规则的时间段内发送一些数据(不可能定义任何读取超时)。 我编写了一个TCP客户端来连接到该服务器并读取数据。它工作正常,但当网络出现问题且服务器不可用时(例如,当我从计算机上拔出网络电缆时),应用程序“注意”到服务器没有连接并引发异常大约需要10秒钟。(我不知道为什么精确到10秒?定义在哪里?我可以更改它吗? 我希望反应更快—比如说在连接
我使用Visual Studio 2010和C语言 我有一个像服务器一样工作的设备,它在非常不规则的时间段内发送一些数据(不可能定义任何读取超时)。
我编写了一个TCP客户端来连接到该服务器并读取数据。它工作正常,但当网络出现问题且服务器不可用时(例如,当我从计算机上拔出网络电缆时),应用程序“注意”到服务器没有连接并引发异常大约需要10秒钟。(我不知道为什么精确到10秒?定义在哪里?我可以更改它吗?
我希望反应更快—比如说在连接断开一秒钟后。
然而,谷歌搜索答案并不能为我提供任何有效的解决方案 测试代码如下,我试着在两个线程上运行:一个线程正在读取数据,第二个线程正在查找连接状态,当连接断开时应该会向我发出警报。它既不适用于TCPClient,也不适用于Socket类。我尝试使用
tcpClient.SendTimeout=100读取/写入一些数据
和stream.WriteTimeout=100代码>但它似乎不起作用
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
namespace TCPClient
{
class Program
{
static volatile bool _continue = true;
static TcpClient tcpClient;
static NetworkStream stream;
static void Main(string[] args)
{
try
{
//client thread - listen from server
Thread tcpListenThread = new Thread(TcpListenThread);
tcpListenThread.Start();
//connection checking thread
Thread keepAliveThread = new Thread(KeepAliveThread);
keepAliveThread.Start();
while (_continue)
{
if (Console.ReadLine() == "q")
{
_continue = false;
}
}
keepAliveThread.Join(2000);
if (keepAliveThread.IsAlive)
{
Console.WriteLine("Thread keepAlive has been aborted...");
keepAliveThread.Abort();
}
tcpListenThread.Join(2000);
if (tcpListenThread.IsAlive)
{
Console.WriteLine("Listen thread has been aborted...");
tcpListenThread.Abort();
}
}
catch (Exception ex)
{
Console.WriteLine("\n" + ex.Message);
}
Console.WriteLine("\nHit any key to quit...");
Console.Read();
}
private static void TcpListenThread()
{
string server = "172.20.30.40";
int port = 3000;
try
{
using (tcpClient = new TcpClient())
{
tcpClient.Connect(server, port);
if (tcpClient.Connected)
Console.WriteLine("Successfully connected to server");
using (stream = tcpClient.GetStream())
{
while (_continue)
{
Byte[] data = new Byte[1024];
Int32 bytes = stream.Read(data, 0, data.Length);
string responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
Console.WriteLine("Received: {0}, responseData);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Listen thread exception! " + ex.Message);
}
}
private static void KeepAliveThread()
{
while (_continue)
{
if (tcpClient != null)
{
try
{
//...what to put here to check or throw exception when server is not available??...
}
catch (Exception ex)
{
Console.WriteLine("Disconnected...");
}
}
Thread.Sleep(1000); //test for connection every 1000ms
}
}
}
}
编辑:
@卡斯滕的回答:虽然看起来很有希望,但这个解决方案不起作用……
我为此制作了最简单的测试应用程序:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
namespace TCPClientTest
{
class Program
{
static void Main(string[] args)
{
try
{
string server = "172.20.30.40";
int port = 3000;
using (TcpClient tcpClient = new TcpClient())
{
tcpClient.Connect(server, port);
int i = 0;
while (true)
{
// This is how you can determine whether a socket is still connected.
bool blockingState = tcpClient.Client.Blocking;
try
{
byte[] tmp = new byte[1];
tcpClient.Client.Blocking = false;
tcpClient.Client.Send(tmp, 0, 0);
Console.WriteLine("Connected!");
}
catch (SocketException e)
{
// 10035 == WSAEWOULDBLOCK
if (e.NativeErrorCode.Equals(10035))
Console.WriteLine("Still Connected, but the Send would block");
else
{
Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode);
}
}
finally
{
tcpClient.Client.Blocking = blockingState;
}
Console.WriteLine("Connected: {0} ({1})", tcpClient.Client.Connected, i++);
Thread.Sleep(1000);
}
}
}
catch (Exception ex)
{
Console.WriteLine("Global exception: {0}", ex.Message);
}
}
}
}
结果如下所示,它显示:
已连接
已连接:正确
加上我每秒钟的订单号。当我断开网络电缆时,需要8秒钟才能开始打印:
断开连接:错误代码10054
已连接:错误
所以到了8秒钟,我还没有意识到连接断开了。看起来ping是这里最好的选择,但我将测试其他解决方案。简单答案。你不能。Tcp是以一种不允许这种情况的方式生成的。然而,实现这一点的正常方法是以比消息更短的间隔发送ping。比如说,每当你从服务器收到一条消息时,你启动一个倒计时1分钟的时钟,然后你发送一个“ping”命令(如果你在这期间没有收到新消息)。如果在30秒内没有收到对“ping”的响应,则表明连接已断开
从理论上讲,您应该能够在包级别执行此操作(tcp发送ACK,您应该能够检查),但我不知道在C#或任何编程语言中是否可以执行此操作,或者,如果您需要在固件/硬件中执行此操作。在oginal unix Implementation中有一个名为“SO_KEEPALIVE”的套接字选项,它让TCP协议偶尔发送探测,以确保管道的另一端仍在侦听。它可以与通过Socket.IOControl设置探头的频率一起设置。我认为这是一个经常出现的问题。这可能就是为什么MSDN文档确实给出了一个很好的答案-请参阅
引述:
Connected属性获取截至的套接字的连接状态
最后一次I/O操作。当它返回false时,套接字为
从未连接,或不再连接
关联财产的价值反映了资产的状态
从最近的操作开始的连接。如果你需要确定
连接的当前状态,生成一个非阻塞的零字节
打电话。如果调用成功返回或抛出WaeWoldBlock
错误代码(10035),则插座仍处于连接状态;否则
插座不再连接
使用此示例代码:
// .Connect throws an exception if unsuccessful
client.Connect(anEndPoint);
// This is how you can determine whether a socket is still connected.
bool blockingState = client.Blocking;
try
{
byte [] tmp = new byte[1];
client.Blocking = false;
client.Send(tmp, 0, 0);
Console.WriteLine("Connected!");
}
catch (SocketException e)
{
// 10035 == WSAEWOULDBLOCK
if (e.NativeErrorCode.Equals(10035))
Console.WriteLine("Still Connected, but the Send would block");
else
{
Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode);
}
}
finally
{
client.Blocking = blockingState;
}
Console.WriteLine("Connected: {0}", client.Connected);
以及扩展方法的直接主题化:
public static bool IsConnected(this Socket client)
{
// This is how you can determine whether a socket is still connected.
bool blockingState = client.Blocking;
try
{
byte [] tmp = new byte[1];
client.Blocking = false;
client.Send(tmp, 0, 0);
return true;
}
catch (SocketException e)
{
// 10035 == WSAEWOULDBLOCK
if (e.NativeErrorCode.Equals(10035))
return true;
else
{
return false;
}
}
finally
{
client.Blocking = blockingState;
}
}
如果您键入以下内容,它将是正确且有效的:
byte[] tmp = new byte[] {0};
...
client.Send(tmp, 1, 0);
...
这是一个非常古老的帖子,但这是我搜索这个问题时出现的第一个SO帖子,我在其他地方找到了一个更有用的解决方案,所以我想我应该发布一个链接,以帮助将来的其他人:
ElFenix发布了这个对我有用的答案:
// Detect if client disconnected
if (tcp.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (tcp.Client.Receive(buff, SocketFlags.Peek) == 0)
{
// Client disconnected
bClosed = true;
}
}
以防其他人需要简单有效的东西
这是我想出的密码
while (true)
{
string s = null;
DateTime readLag = DateTime.Now;
try
{
s = streamIn.ReadLine();
}
catch (Exception ex)
{
SocketException sockEx = ex.InnerException as SocketException;
if (sockEx != null)
{
OnDebug("(" + sockEx.NativeErrorCode + ") Exception = " + ex);
}
else
{
OnDebug("Not SockEx :" + ex);
}
}
if (enableLog) OnDebug(s);
if (s == null || s == "")
{
if (readLag.AddSeconds(.9) > DateTime.Now)
{
break;
}
}
else
{
CommandParser(s);
}
}
每次读取失败时,我得到的是本机错误10035,但会被阻止
当连接被破坏时,我立即得到了一个没有数据的返回
将readLag.AddSeconds()设置为刚好低于读取超时,这会让我非常清楚,时间从未流逝,而且我没有得到任何数据。这是不应该发生的
这个标准一出现,我就退出循环,线程就结束了
希望这能对其他人有所帮助。可能的重复:嗯,KEEPALIVEs充其量也只是一个片片。它们有时会造成比它们解决的问题更多的问题-我只是不记得为什么或者源代码是什么-但是要小心使用。如果我读对了,这是否意味着(基本上)通过执行Send(新字节[0])来
Send方法等待底层的ACK
作为对发送的包的响应到达?@Carsten König:谢谢您的回答。看起来很有希望,但恐怕不行。我已经编辑了我的帖子来解释这个解决方案的问题。谢谢你的回答。到目前为止,ping似乎是最好的选择,但是当我挖掘答案时,我发现一些页面指出它是一个糟糕的解决方案-不知道为什么。我认为你没有理解一些东西,或者我(这很可能是btw xD)。据我所知,你刚刚更新的帖子是ping,只是用了一种比自己更聪明的方式。另外,8秒钟直到你发现一个断开连接听起来一点也不糟糕。例如,IRC服务器在ping(!)之间最多使用5分钟。我更新了我的帖子,表明Carsten König提出的解决方案不起作用。它试图每秒执行tcpClient.Client.Send(…)
,所以我一直希望在断开电缆连接后,它会抛出异常或其他异常