【问题标题】:Questions about TCP Async server from MSDN Magazine来自 MSDN 杂志的关于 TCP 异步服务器的问题
【发布时间】:2012-10-26 10:29:14
【问题描述】:

this MSDN Magazine August 2005 articleDaryn Kiely 中解释了构建 TCP 服务器的三种方法。

第三种模型,即异步模型,最适合我的需求,但我对其内部工作的一些细节有一些了解。

问题 1 - 关于接受连接的线程数

在示例中,当通过调用Start() 启动服务器时,代码会创建十个接受连接的线程池:

public void Start()
{
    SetupServerSocket();
    for (int i = 0; i < 10; i++)
        _serverSocket.BeginAccept(AsyncCallback(AcceptCallback), _serverSocket);
}

我不明白为什么你不能只使用一个_serverSocket.BeginAccept,为什么数字十。

问题 2 – 大约收到零字节

ReceiveCallback() 中,如果我们收到零字节,我们将关闭连接。为什么?当接收到零字节时总是认为客户端关闭了连接?在我在这里进行的测试中,当我的客户端关闭连接时,我得到一个异常,该异常被 SocketException 捕获。我在这里遗漏了什么吗?

问题 3 – 关于重新开始接收的需要

在套接字中接收到某些内容后,它会被放回 BeginReceive。为什么我们需要重新开始接收?这不应该是自动的吗?

问题 4 – 关于缓冲区大小

使用了 255 字节的缓冲区。我知道如果一条消息的长度大于缓冲区大小,它会在多个接收中分散。我应该设置一个足够大的缓冲区大小以保证不会有消息碎片化,还是必须提供代码来处理消息碎片化(将多个接收连接到一个缓冲区中)?

【问题讨论】:

  • 回复:问题 4 - 即使您使用大缓冲区,也不能保证一侧发送和另一侧接收之间的任何匹配。编码的唯一安全方法是假设 any 消息,无论多么小,都可能是碎片化的。和/或任何Receive 可能会用来自多个消息的部分填充缓冲区

标签: c# networking tcp tcpserver


【解决方案1】:

问题 1 - 关于接受连接的线程数

在示例中,当通过调用 Start() 启动服务器时,代码会创建十个接受连接的线程池:

public void Start()
{
    SetupServerSocket();
    for (int i = 0; i < 10; i++)
        _serverSocket.BeginAccept(AsyncCallback(AcceptCallback), _serverSocket);
}

我不明白为什么你不能只使用一个 _serverSocket.BeginAccept,以及为什么数字十。

没有理由像这样生成多个BeginAccept。只需在 AcceptCallback 中调用新的 BeginAccept 即可。

问题 2 – 大约收到零字节

在 ReceiveCallback() 中,如果我们收到零字节,我们将关闭连接。为什么?当接收到零字节时总是认为客户端关闭了连接?在我在这里进行的测试中,当我的客户端关闭连接时,我得到一个异常,该异常被 SocketException 捕获。我在这里遗漏了什么吗?

是的。 0 字节表示正常断开连接。即对方先用Shutdown再用Close,而不是直接close。

任何其他类型的断开连接都会产生异常。

问题 3 – 关于重新开始接收的需要

在套接字中接收到某些内容后,它会被放回 BeginReceive。为什么我们需要重新开始接收?这不应该是自动的吗?

没有。例如,当您关闭服务器时,您不想再次阅读。其他客户端可能只想接收一次、处理数据然后断开连接。

问题 4 – 关于缓冲区大小

使用了 255 字节的缓冲区。 我知道如果一条消息的长度大于缓冲区大小,它会在多个接收中分散。我应该设置一个足够大的缓冲区大小以保证不会有消息碎片化,还是必须提供代码来处理消息碎片化(将多个接收连接到一个缓冲区中)?

一个发送的byte[] 总是可以被分成多个接收。单个接收还可以包含两个发送的byte[] 缓冲区。这就是 TCP 的工作原理。

您通常使用一个接收缓冲区,然后使用另一个来构建消息。或者使用更大的缓冲区并通过在方法调用中使用更大的offset 来告诉 Receive 完成消息

【讨论】:

  • 我反对说 TCP 可能会分割或合并消息,因为 没有消息。这个词对于 TCP 没有意义。
  • 我提到了一个应用程序级别的消息,但为了澄清这一点,我改成了byte[]
  • 单个接收还可以包含 片段 两个 或更多 缓冲区(您可以接收发送 1 的结束、发送 2 的全部、发送 2 的开始发送 3,例如)
  • 阅读我的旧答案:stackoverflow.com/questions/6178883/…
  • 对于问题 3 .. BeginReceive() 并不意味着“开始接收循环”,它只是同步 Receive() 的异步版本的前半部分。我从 OP 那里了解到,该术语误导了他。
【解决方案2】:

好的,我去试试:

1) 单个BeginAccept 意味着您的服务器套接字将不会接受连接,直到在AcceptCallback 中调用下一个BeginAccept,这不是立即调用,而是在完成一些工作之后调用.我认为数字 10 是任意的 - 这就像假脱机 10 接受或本质上能够同时接收多达 10 个传入连接请求。如果您不需要处理很多客户,我想单个 BeginAccept 仍然可以工作。

2)我不知道你用的是什么客户端,但我已经使用了与浏览器的套接字连接,当他们关闭连接时,他们确实喜欢发送和清空段(或者这就是我看到的调试)作为关闭通知。如果这不再正确,有人可以纠正我,但似乎是检查这一点的好习惯。

3) 嗯,它不是自动的。最好循环它以确保您不会错过任何内容。

4) 据我所知,Windows 没有 TCP 缓冲区大小的最大值,除非管理员指定。所以你可以假设增加它,但也许看看:What is a good buffer size for socket programming? - 在 cmets 中说你不应该尝试在一次接收中获取所有消息,因为它毕竟是一个流协议。

【讨论】:

  • TCP 没有消息,因此浏览器无法发送空消息。 Socket.Receive 永远不会返回有效的 0 字节块。
  • 已编辑为分段,但实际上不是一个词的坚持者,它可能只是一个 FIN 控制标志,但浏览器无法为 Http 连接发送它,因为它们肯定会为 WebSocket 连接发送,并且两者都是只是美化了 TCP。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-22
  • 2010-10-22
  • 1970-01-01
  • 2013-03-25
  • 2021-07-06
  • 1970-01-01
相关资源
最近更新 更多