gpt4 book ai didi

sockets - 使用NetworkStream类检测客户端TCP断开连接

转载 作者:行者123 更新时间:2023-12-03 11:52:38 28 4
gpt4 key购买 nike

我的一个 friend 来找我一个问题:在连接的服务器端使用NetworkStream类时,如果客户端断开连接,则NetworkStream无法检测到它。

剥离下来,他的C#代码看起来像这样:

List<TcpClient> connections = new List<TcpClient>();
TcpListener listener = new TcpListener(7777);
listener.Start();

while(true)
{
if (listener.Pending())
{
connections.Add(listener.AcceptTcpClient());
}
TcpClient deadClient = null;
foreach (TcpClient client in connections)
{
if (!client.Connected)
{
deadClient = client;
break;
}
NetworkStream ns = client.GetStream();
if (ns.DataAvailable)
{
BinaryFormatter bf = new BinaryFormatter();
object o = bf.Deserialize(ns);
ReceiveMyObject(o);
}
}
if (deadClient != null)
{
deadClient.Close();
connections.Remove(deadClient);
}
Thread.Sleep(0);
}

该代码有效,因为客户端可以成功连接,服务器可以读取发送给它的数据。但是,如果远程客户端调用tcpClient.Close(),则服务器不会检测到断开连接-client.Connected保持为true,而ns.DataAvailable为false。

搜索Stack Overflow提供了一个答案-由于未调用Socket.Receive,因此套接字未检测到断开连接。很公平。我们可以解决此问题:

foreach (TcpClient client in connections)
{
client.ReceiveTimeout = 0;
if (client.Client.Poll(0, SelectMode.SelectRead))
{
int bytesPeeked = 0;
byte[] buffer = new byte[1];
bytesPeeked = client.Client.Receive(buffer, SocketFlags.Peek);
if (bytesPeeked == 0)
{
deadClient = client;
break;
}
else
{
NetworkStream ns = client.GetStream();
if (ns.DataAvailable)
{
BinaryFormatter bf = new BinaryFormatter();
object o = bf.Deserialize(ns);
ReceiveMyObject(o);
}
}
}
}

(为简洁起见,我省略了异常处理代码。)

该代码有效,但是,我不会将此解决方案称为“优雅”。解决此问题的另一种优雅的方法是为每个TcpClient生成一个线程,并允许BinaryFormatter.Deserialize(néeNetworkStream.Read)调用进行阻止,以正确检测到断开连接。但是,这确实有为每个客户端创建和维护线程的开销。

我感到自己缺少一些 secret 的,很棒的答案,该答案将保留原始代码的清晰度,但避免使用其他线程来执行异步读取。虽然,也许NetworkStream类从未设计用于这种用法。谁能给我一些启示?

更新:我只是想澄清一下,我很想看看.NET框架是否具有涵盖NetworkStream使用(即轮询和避免阻塞)的解决方案-显然可以做到; NetworkStream可以很容易地包装在提供功能的支持类中。框架本质上要求您使用线程来避免对NetworkStream.Read进行阻塞,或者窥探套接字本身以检查是否断开连接,这似乎很奇怪,几乎就像是一个错误。或潜在缺乏功能。 ;)

最佳答案

服务器是否希望通过同一连接发送多个对象?如果没有,那么我看不到这段代码将如何工作,因为没有发送分隔符来表示第一个对象在哪里开始,下一个对象在哪里结束。

如果仅发送一个对象并且此后关闭连接,则原始代码将起作用。

必须进行网络操作才能确定连接是否仍处于 Activity 状态。我要做的是,与其直接从网络流中反序列化,不如将其缓冲到MemoryStream中。这将使我能够检测到何时断开连接。我还将使用消息框架来界定流上的多个响应。

        MemoryStream ms = new MemoryStream();

NetworkStream ns = client.GetStream();
BinaryReader br = new BinaryReader(ns);

// message framing. First, read the #bytes to expect.
int objectSize = br.ReadInt32();

if (objectSize == 0)
break; // client disconnected

byte [] buffer = new byte[objectSize];
int index = 0;

int read = ns.Read(buffer, index, Math.Min(objectSize, 1024);
while (read > 0)
{
objectSize -= read;
index += read;
read = ns.Read(buffer, index, Math.Min(objectSize, 1024);
}

if (objectSize > 0)
{
// client aborted connection in the middle of stream;
break;
}
else
{
BinaryFormatter bf = new BinaryFormatter();
using(MemoryStream ms = new MemoryStream(buffer))
{
object o = bf.Deserialize(ns);
ReceiveMyObject(o);
}
}

关于sockets - 使用NetworkStream类检测客户端TCP断开连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1895832/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com