TCPClient连接后如何测试是否断线?
我已经与一个问题斗争了整整三天,我找不到任何解决方案,请帮助:)
我使用 Visual Studio 2010 和 C# 语言。
我有一个像服务器一样工作的设备,它在非常不规则的时间段内发送一些数据(无法定义任何读取超时)。
我编写了一个 TCP 客户端来连接到该服务器并读取数据。它工作正常,但是当网络出现问题并且服务器变得不可用时(例如,当我从计算机上拔掉网络电缆时),应用程序需要大约 10 秒的时间“注意到”没有与服务器的连接并抛出异常一个例外。 (我不知道为什么正好是10秒?它在哪里定义的?我可以更改它吗?)
我想反应更快 - 比如说连接断开后一秒钟。
然而,谷歌搜索答案并没有为我提供任何可行的解决方案。
测试代码如下,我尝试在 2 个线程上进行:一个正在读取数据,第二个正在查找连接状态,并且应该在连接断开时向我发出警报。它不适用于 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
}
}
}
}
编辑:
@carsten 的回答:虽然看起来很有希望,但这个解决方案不起作用......
我为此制作了最简单的测试应用程序:
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);
}
}
}
}
结果如下,它显示:
已连接!
已连接:True
每秒加上我的订单号。当我断开网络电缆时,需要 8 秒才能开始打印:
断开连接:错误代码 10054!
Connected: False
所以到 8 秒时我不知道连接丢失了。看起来 ping 是最好的选择,但我将测试其他解决方案。
I've been fighting with one problem for a whole 3 days I can't find any solution, please help :)
I work with Visual Studio 2010 and C# language.
I have a device working like a server, that sends some data in a very irregular periods of time (not possible to define any read timeout).
I wrote a TCP client to connect to that server and read data. It works OK, however when something is wrong with the network and server becomes unavailable (e.g. when I plug out the network cable from my computer), it takes about 10 seconds for application to "notice" there is no connection to the server and throw an exception. (I don't know why exactly 10 seconds? Where it's defined? Can I change it?)
I want to react faster - let say after one second after connection broken.
Googling for answer however doesn't provide me any working solution.
The test code is below, I try to make it on 2 threads: one is reading data, the second one is looking for connection status and should alarm me when it's broken. It's not working neither for TCPClient nor Socket class. I've tried to read / write some data with tcpClient.SendTimeout = 100;
and stream.WriteTimeout = 100;
but it doesn't seem to work.
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
}
}
}
}
Edit:
@carsten's answer: although it looks promising, this solution do not work...
I made the simplest test application for that:
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);
}
}
}
}
The results are following, it displays:
Connected!
Connected: True
plus my order number every one second. When I disconnect network cable, it takes 8 seconds to start printing:
Disonnected: error code 10054!
Connected: False
so by 8 seconds I'm not aware that the connection is lost. It looks like pinging is the best option here, yet I'll test another solutions.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我想这是一个经常出现的问题。这可能就是为什么 MSDN 文档确实对此给出了很好的答案 - 请参阅 Socket.Connected
引用自那里:
使用此示例代码:
以及扩展方法的直接动机:
I think this is a question that often comes around. This might be why MSDN docs really give a good answer to this - see Socket.Connected
Quote from there:
with this sample code:
and the straight forward motification for an extension-method:
这是一个非常古老的线程,但这是我搜索这个问题时出现的第一篇 SO 帖子,并且我在其他地方找到了更有用的解决方案,所以我想我应该发布一个链接来帮助其他人将来:
<一href="https://social.msdn.microsoft.com/Forums/en-US/c857cad5-2eb6-4b6c-b0b5-7f4ce320c5cd/c-how-to-确定-if-a-tcpclient-has-been-disconnected ?forum=netfxnetcom&prof=必填" rel="noreferrer">https://social.msdn.microsoft.com/Forums/en-US/c857cad5-2eb6-4b6c-b0b5-7f4ce320c5cd/c-how-to-确定-if-a-tcpclient-has- was-disconnected?forum=netfxnetcom&prof=required
ElFenix 发布了这个对我有用的答案:
This is a very old thread, but it's the first SO post that came up when I searched for this question and I found a more useful solution somewhere else, so I thought I'd post a link to it to help others in the future:
https://social.msdn.microsoft.com/Forums/en-US/c857cad5-2eb6-4b6c-b0b5-7f4ce320c5cd/c-how-to-determine-if-a-tcpclient-has-been-disconnected?forum=netfxnetcom&prof=required
ElFenix posted this answer that worked for me:
简单的回答。你不能。 Tcp 的制作方式不允许这样做。然而,实现此目的的正常方法是以比消息更短的间隔发送 ping。因此,假设每当您从服务器收到消息时,您都会启动一个倒计时 1 分钟的时钟,然后发送一个“ping”命令(如果您在这期间没有收到新消息)。如果您在 30 秒内没有收到对“ping”的响应,则可以断定连接已断开。
理论上,您应该能够在包级别执行此操作(tcp 发送您应该能够检查的 ACK),但我不知道这在 C# 或任何与此相关的编程语言中是否可行,或者是否可以你需要在固件/硬件中做到这一点。
Simple answer. You can't. Tcp is made in a way which doesn't allow this. However, the normal way to achieve this is to send ping's with shorter interval than messages. So, say, that whenever you get a message from the server, you start a clock that count down 1 min, then you send a "ping" command (if you haven't received a new message in between). If you don't receive a response to your "ping" within 30 seconds, you conclude that the connection is broken.
Theoretically, you should be able to do this on a package-level (tcp sends ACK which you should be able to check for), but I don't know if that's possible in C#, or any programming language for that matter, or if you need to do that in firmware/hardware.
如果您输入以下内容,它将是正确的并且可以工作:
it will be correct and working if you type the following:
以防万一其他人需要简单而有效的东西。
这是我想出的代码
每次读取失败但会阻塞时,我得到的是本机错误 10035。
当连接被破坏时,我立即得到了没有数据的返回。
将 readLag.AddSeconds() 设置为略低于我的读取超时将使我知道时间从未流逝,并且我没有得到任何数据。这不应该发生。
一旦这个标准出现,我就跳出循环,线程结束。
希望这对其他人有帮助。
Just in case anyone else needs something simple and effective.
This is the code I came up with
What I got was native error 10035 every time a read failed but would block.
When the connection was trashed, I instantly got a return with no data.
Setting the readLag.AddSeconds() to just below my read timeout would give me a pretty good idea that the time never elapsed, and I got no data. Which should not happen.
Soon as this criteria popped up, I just kick out of the loop and the thread ends.
Hope this helps anyone else out there.
最初的 unix 实现中有一个名为 SO_KEEPALIVE 的套接字选项,它使 TCP 协议偶尔发送探测以确保管道的另一端仍在侦听。它可以与通过 Socket.IOControl 设置探针的频率一起设置
There is a socket option called SO_KEEPALIVE from the oginal unix implemetnation that has the TCP protocol send occasional probes to make sure the other end of the pipe is still listening. It can be set along with how often the probes are set via Socket.IOControl