c检测到tcp断开连接
本文关键字:断开 连接 tcp 检测 | 更新日期: 2023-09-27 18:01:04
我有两个简单的应用程序:
-
服务器应用程序,在特定的tcp端口上等待客户端连接。然后听他说什么,发回一些反馈,并断开客户的连接。
-
一个连接到服务器应用程序的Form应用程序,然后说一些话,然后等待反馈并断开与服务器的连接,然后在表单中显示反馈。
尽管服务器应用程序似乎运行正常(我用Telnet测试过它,我看到了反馈,我看到反馈后直接发生断开连接(,但表单应用程序似乎没有注意到与服务器的断开连接。(TcpClient.Connected似乎即使在服务器断开连接后也保持不变(
我的问题是:为什么是TcpClient。连接保持真实,我如何知道服务器是否/何时断开连接?
这是我的完整代码:
表格申请:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace Sender
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void sendButton_Click(object sender, EventArgs e)
{
TcpClient tcpClient = new TcpClient();
tcpClient.Connect(IPAddress.Parse("127.0.0.1"), 81);
responseLabel.Text = "waiting for response...";
responseLabel.Invalidate();
// write request
NetworkStream networkStream = tcpClient.GetStream();
byte[] buffer = (new ASCIIEncoding()).GetBytes("Hello World! ");
networkStream.Write(buffer, 0, buffer.Length);
networkStream.Flush();
// read response
Thread readThread = new Thread(new ParameterizedThreadStart(ReadResponse));
readThread.Start(tcpClient);
}
void ReadResponse(object arg)
{
TcpClient tcpClient = (TcpClient)arg;
StringBuilder stringBuilder = new StringBuilder();
NetworkStream networkStream = tcpClient.GetStream();
bool timeout = false;
DateTime lastActivity = DateTime.Now;
while (tcpClient.Connected && !timeout)
{
if (networkStream.DataAvailable)
{
lastActivity = DateTime.Now;
while (networkStream.DataAvailable)
{
byte[] incomingBuffer = new byte[1024];
networkStream.Read(incomingBuffer, 0, 1024);
char[] receivedChars = new char[1024];
(new ASCIIEncoding()).GetDecoder().GetChars(incomingBuffer, 0, 1024, receivedChars, 0);
stringBuilder.Append(receivedChars);
}
}
else
{
if (DateTime.Now > lastActivity.AddSeconds(60))
timeout = true;
}
System.Threading.Thread.Sleep(50);
}
Invoke((MethodInvoker)delegate
{
responseLabel.Text = "Response from Listener:'n" + stringBuilder.ToString();
responseLabel.Invalidate();
});
if (timeout)
{
Console.Write("A timeout occured'n");
networkStream.Close();
tcpClient.Close();
}
}
}
}
服务器应用程序:
using System.Net;
using System.Net.Sockets;
using System.Text;
using System;
using System.Threading;
namespace Listener
{
class Program
{
static void Main(string[] args)
{
var tcpListener = new TcpListener(IPAddress.Any, 81);
tcpListener.Start();
Thread clientThread = new Thread(new ParameterizedThreadStart(Listen));
clientThread.Start(tcpListener);
}
static void Listen(object arg)
{
TcpListener tcpListener = (TcpListener)arg;
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClient));
clientThread.Start(tcpClient);
}
}
static void HandleClient(object arg)
{
TcpClient tcpClient = (TcpClient)arg;
StringBuilder stringBuilder = new StringBuilder();
ASCIIEncoding encoder = new ASCIIEncoding();
DateTime lastActivity = DateTime.Now;
// read request
NetworkStream networkStream = tcpClient.GetStream();
int timeout = 5; // gives client some time to send data after connecting
while (DateTime.Now < lastActivity.AddSeconds(timeout) && stringBuilder.Length==0)
{
if (!networkStream.DataAvailable)
{
System.Threading.Thread.Sleep(50);
}
else
{
while (networkStream.DataAvailable)
{
lastActivity = DateTime.Now;
byte[] incomingBuffer = new byte[1024];
networkStream.Read(incomingBuffer, 0, 1024);
char[] receivedChars = new char[1024];
encoder.GetDecoder().GetChars(incomingBuffer, 0, 1024, receivedChars, 0);
stringBuilder.Append(receivedChars);
}
}
}
string request = stringBuilder.ToString();
// write response
string response = "The listener just received: " + request;
byte[] outgoingBuffer = encoder.GetBytes(response);
networkStream.Write(outgoingBuffer, 0, outgoingBuffer.Length);
networkStream.Flush();
networkStream.Close();
tcpClient.Close();
}
}
}
TcpClient/NetworkStream在连接关闭时不会收到通知。唯一可用的选项是在写入流时捕获异常。
几年前,我们开始使用套接字而不是tcp客户端。与tcpclient相比,socket更可用。
有几种方法可以使用
民意调查是之一
http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.poll.aspx
您也可以检查Write本身的结果。它会给出实际写入的字节数。
Connected属性本身仅反映上次操作时的状态。其文档指出:"Connected属性的值反映了最近一次操作时的连接状态。如果需要确定连接的当前状态,请进行非阻塞的零字节Send调用。如果调用成功返回或抛出WAEWOULDBLOCK错误代码(10035(,则套接字仍处于连接状态;否则,套接字将不再连接。"。">
http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.connected.aspx
还有另一种方法可以检测TCP连接的正确状态,不必使用Socket
。您可以保留TcpClient
并使用IPGlobalProperties
这个想法就是从这个答案中得出的。
注意:中有一个错误。NET 5在调用CCD_ 4时导致内存泄漏。中不存在此错误。NET Framework中进行了修复。NET 6。(https://github.com/dotnet/runtime/issues/64735)
public class CTcpipConnection
{
public enum EnumState
{
NotInitialized,
NotReady,
Idle,
Connecting,
Connected,
Disconnecting
}
public string IpAddress { get; } // will be set in ctor
public int LocalPort { get; } // will be set in Connect() and Disconnect()
public int Port { get; } // will be set in ctor
EnumState GetState ()
{
var ipGlobProp = IPGlobalProperties.GetIPGlobalProperties ();
var tcpConnInfos = ipGlobProp.GetActiveTcpConnections ();
TcpConnectionInformation tcpConnInfo = null;
for (int index = 0; index < i_tcpConnInfos.Length; index++)
{
if (i_tcpConnInfos[index].LocalEndPoint.Port == LocalPort
&& i_tcpConnInfos[index].RemoteEndPoint.Port == Port)
{
tcpConnInfo = i_tcpConnInfos[index];
break;
}
}
if (tcpConnInfo == null)
return EnumState.Idle;
var tcpState = tcpConnInfo.State;
switch (tcpState)
{
case TcpState.Listen:
case TcpState.SynSent:
case TcpState.SynReceived:
return EnumState.Connecting;
case TcpState.Established:
return EnumState.Connected;
case TcpState.FinWait1:
case TcpState.FinWait2:
case TcpState.CloseWait:
case TcpState.Closing:
case TcpState.LastAck:
return EnumState.Disconnecting;
default:
return EnumState.NotReady;
}
}
private void Connect ()
{
m_tcpclient.Connect (IpAddress, Port);
m_netstream = m_tcpclient.GetStream ();
var ipendpoint = m_tcpclient.Client.LocalEndPoint as IPEndPoint;
LocalPort = ipendpoint.Port;
}
private void Disconnect ()
{
if (m_netstream != null)
{
m_netstream.Flush ();
m_netstream.Close (500);
}
m_tcpclient.Close ();
LocalPort = 0;
m_tcpclient = new TcpClient ();
}
}
如果您进行无限超时的阻塞读取,即
NetworkStream.ReadTimeout = -1
在这种情况下,当连接丢失时,Read方法将返回零
// Reading everything from network to memory stream
var s = new MemoryStream();
var buf = new byte[client.ReceiveBufferSize];
do
{
var n = stream.Read(buf, 0, buf.Length);
if(n == 0)
{
return; // connection is lost
}
s.Write(buf, 0, n);
}
while(s.Length < packetsize);