【问题标题】:Keeping TCP connection alive保持 TCP 连接处于活动状态
【发布时间】:2019-09-01 18:13:48
【问题描述】:

这可能是一个愚蠢的问题,但请听我说完。

在我的远程服务器上,我正在运行具有以下代码的程序:

private const int BufferSize = 1024;
private static readonly byte[] BytesBeingReceived = new byte[BufferSize];

static void Main(string[] args)
{
    TcpListener tcpListener = new TcpListener(localaddr: IPAddress.Any, port: 8080);
    tcpListener.Start();

    clientSocket = tcpListener.AcceptSocket();
    clientSocket.Receive(BytesBeingReceived); // Receives header first

    ReceiveAndUnzip(GetZippedFolderSizeInBytes()); 
    // GetZippedFolderSizeInBytes() reads the header, which contains information about the folder size

    Console.WriteLine("Press any key to exit the program.");
    Console.ReadLine();
}

正如预期的那样,一旦程序连接到客户端套接字并从客户端接收所有数据(到目前为止,客户端始终是我的本地计算机),程序就会退出。

但是,我希望在服务器可用的情况下保持程序运行(即不重新启动、休眠、关闭等)。从客户端收到所有数据后,我想保持TcpListener 处于活动状态并等待可能更多的数据。如果客户端断开连接,TcpListener 仍应保持活动状态并等待未来的挂起连接。

对我如何做到这一点有任何建议吗?

更新:

这里是GetZippedFolderSizeInBytes()的实现:

private static int GetFileSizeInBytes()
{
    return Convert.ToInt32(Encoding.ASCII.GetString(BytesBeingReceived)
                                         .Replace("\0", Empty)
                                         .Split(new[] { Environment.NewLine, ":" }, StringSplitOptions.RemoveEmptyEntries)[1]);
}

这里是ReceiveAndUnzip(int numBytesExpectedToReceive)方法的实现:

private static void ReceiveAndUnzip(int numBytesExpectedToReceive)
{
    int numBytesLeftToReceive = numBytesExpectedToReceive;

    using (MemoryStream zippedFolderStream = new MemoryStream(new byte[numBytesExpectedToReceive]))
    {
        while (numBytesLeftToReceive > 0)
        {
            Array.Clear(BytesBeingReceived, 0, BufferSize);
            int numBytesReceived = clientSocket.Receive(BytesBeingReceived, SocketFlags.Partial);
            zippedFolderStream.Write(
                BytesBeingReceived, 
                0, 
                numBytesLeftToReceive < BufferSize ? numBytesLeftToReceive : BufferSize);
            numBytesLeftToReceive -= numBytesReceived;
        }

        zippedFolderStream.Unzip(afterReadingEachDocument: DoMoreStuff);
    }
}

【问题讨论】:

  • 嗯,这里第一个明显的错误在Receive 行。它返回“接收的字节数”。但你没有存储或使用它。 没有保证它与缓冲区的大小相同。
  • 感谢您的评论。我将使用 ReceiveAndUnzip() 方法的实现来更新我的帖子。
  • 是的,您在该方法中正确执行此操作,但您在 Main 中未正确执行此操作。 // Receives header first 可能完全是谎言,因为它可能只提供了一个字节。
  • @Damien_The_Unbeliever 感谢您的评论。现在,唯一连接到我的服务器的客户端始终是我的本地计算机,并且我确保始终首先发送标头。当我还在原型设计阶段时,现在没有错误处理;我了解您的担忧和错误处理将在稍后添加:-)
  • 不是错误处理,而是正确性。如果您的标头包含多个字节,则保证您可以一次性收到所有信息,即使在另一端,它是一次性发送的。 TCP 是字节流,而不是消息。如果您希望接收到整个缓冲区的字节(GetFileSizeInBytes 假设整个缓冲区是有效的),您必须执行与 ReceiveAndUnzip 相同的循环行为以确保您获得所有这些字节.

标签: c# .net tcpclient tcplistener


【解决方案1】:

您必须更改代码以循环接受新客户。如果您希望客户端连接,您可能希望使用一些异步版本的 Receive 和 Accept 并生成一些任务以循环处理客户端 - 这实际上不是最佳方式,但它应该可以工作。

网络编程是一个棘手的问题。这里有一些提示。


1。消息碎片整理

您无法保证客户端中的单个 Send 调用将导致服务器上的单个 Receive。您的消息可以分成片段,或者如果您快速发送几条消息,它们都可以合并在一起,并通过一个Receive调用来接收。

该问题的一个解决方案是首先在消息前面加上一个标题,例如4字节整数。这个整数是您的消息头,仅包含有关您的消息多长时间的信息(以字节为单位)。

您想要做的是接收,直到您获得这个 4 字节整数标头,然后接收消息的其余部分 - 您现在知道消息有多长,所以您会收到直到您收到完整的消息。对每条新消息重复此过程。

这称为碎片整理碎片整理。您可以通过多种方式来实现它:分隔符、以固定大小标头为前缀的消息,以及这两者之间的一些混合。

2。保活/心跳机制

在 tcp 中存在半开半闭连接的问题。简而言之,它只是意味着您无法确定连接是否仍然活动 - 如果您不交换任何消息,则对等/客户端已连接。当对等方/客户端断开连接时,您的 Receive 调用应返回 0 - 这意味着连接已关闭,但 TCP 的工作方式并非总是如此,您必须考虑这样可以避免各种相关的问题。

您可以实现的最简单的 Keep-Alive 机制只是一些超时逻辑,例如在 2 分钟内未收到任何消息意味着对等/客户端已断开连接。或者添加每 X 秒发送的额外心跳消息,如果在某个时间范围内未发送,则使对等方/客户端超时。

3。使用一些经过良好测试的库

如果您没有实现任何自定义协议,并且不想自己解决一些聪明人已经解决的所有 TCP 问题,那么请搜索库。

【讨论】:

猜你喜欢
  • 2014-03-24
  • 2012-01-03
  • 2021-09-13
  • 2013-08-27
  • 1970-01-01
  • 2013-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多