在this MSDN Magazine August 2005 article中,Daryn Kiely解释了构建TCP服务器的三种方法。
第三个模型,即异步模型,是最符合我需求的模型,但我对其内部工作的一些细节有一些麻烦。
在该示例中,当通过调用Start()
启动服务器时,代码会创建十个接受连接的ThreadPools:
public void Start()
{
SetupServerSocket();
for (int i = 0; i < 10; i++)
_serverSocket.BeginAccept(AsyncCallback(AcceptCallback), _serverSocket);
}
我不明白为什么你不能只使用一个_serverSocket.BeginAccept
,为什么只能使用十个。
在ReceiveCallback()
中,如果我们收到零字节,则关闭连接。为什么?当收到零字节时,始终认为客户端关闭了连接?在我在这里做的测试中,当我的客户端关闭连接时,我得到一个在SocketException中捕获的异常。我在这里错过了什么吗?
在socket中收到一些东西后,它会被放回BeginReceive。为什么我们需要再次开始接收?这不应该是自动的吗?
使用255字节的缓冲区 据我所知,如果消息的长度大于缓冲区大小,则会在多个接收中分段。我应该设置一个足够大的缓冲区大小来保证没有消息会被分段,或者我必须提供代码来处理消息碎片(在单个缓冲区中加入多个接收)?
谢谢!
答案 0 :(得分:3)
问题1 - 关于接受连接的线程数
在该示例中,当通过调用Start()启动服务器时,代码会创建十个接受连接的ThreadPools:
public void Start()
{
SetupServerSocket();
for (int i = 0; i < 10; i++)
_serverSocket.BeginAccept(AsyncCallback(AcceptCallback), _serverSocket);
}
我不明白为什么你不能只使用一个_serverSocket.BeginAccept,以及为什么数字十。
没有理由像这样产生几个BeginAccept
。只需在BeginAccept
中调用新的AcceptCallback
。
问题2 - 关于收到零字节
在ReceiveCallback()中,如果我们收到零字节,则关闭连接。为什么?当收到零字节时,始终认为客户端关闭了连接?在我在这里做的测试中,当我的客户端关闭连接时,我得到一个在SocketException中捕获的异常。我在这里错过了什么吗?
是。 0字节表示正常断开连接。即另一方首先使用Shutdown
然后使用Close
而不是直接关闭。
任何其他类型的断开连接都会产生异常。
问题3 - 关于再次使用BeginReceive的需要
在socket中收到一些东西后,它会被放回BeginReceive。为什么我们需要再次开始接收?这不应该是自动的吗?
没有。例如,当您关闭服务器时,您不想再次阅读。其他客户可能只想接收一次,处理数据然后断开连接。
问题4 - 关于缓冲区大小
使用255字节的缓冲区。 据我所知,如果消息的长度大于缓冲区大小,则会在多个接收中分段。我应该设置一个足够大的缓冲区大小来保证没有消息会被分段,或者我必须提供代码来处理消息碎片(在单个缓冲区中加入多个接收)?
一个发送byte[]
的人总是可以进行多次接收。单个接收还可以包含两个发送的byte[]
缓冲区。这就是TCP的工作原理。
您通常使用一个接收缓冲区,然后使用另一个接收缓冲区来构建消息。或者使用更大的缓冲区并告诉Receive在方法调用中使用更大的offset
来完成消息
答案 1 :(得分:2)
好吧,我要去吧:
1)那里的单BeginAccept
意味着你的服务器套接字将不会接受连接,直到在BeginAccept
中调用下一个AcceptCallback
,而BeginAccept
没有被立即调用但是而是在完成一些工作之后。我认为数字10是任意的 - 它就像假脱机10接受或实质上能够同时占用多达10个传入连接请求。如果你不需要处理很多客户,我想单个{{1}}仍然有用。
2)我不知道你使用了什么客户端,但我已经使用了与浏览器的套接字连接,当他们关闭连接时,他们喜欢发送和清空段(或者我看到的调试)关闭通知。有人可以纠正我,如果这不再是真的,但似乎是一个很好的做法来检查。
3)嗯,有点不自动。最好循环它以确保你不会错过任何东西。
4)据我所知,Windows没有TCP缓冲区大小的最大值,除非由admin指定。所以你可以增加它,但也许可以看看:What is a good buffer size for socket programming? - 在评论中说你不应该尝试在一次接收中获取所有消息,因为它毕竟是一个流协议。 / p>