【问题标题】:network delays and Application->ProcessMessages()网络延迟和 Application->ProcessMessages()
【发布时间】:2011-07-22 12:00:46
【问题描述】:

我正在编写一个在我的 C++Builder 项目中使用的网络 DLL。此 DLL 可用于远程 FTP 服务器。当recv() 被调用时,我注意到一个奇怪的行为。有时它返回 0。但在另一个线程中,当在同一个套接字上调用 recv() 时,会按预期接收数据。

这是什么意思?我还注意到在 DLL 线程内调用 Application->ProcessMessage() 可以加快数据接收速度。

但是有什么问题吗? ProcessMessages() 不只是处理窗口消息还是我遗漏了什么?

谢谢

【问题讨论】:

  • recv函数的返回值是多少?如果是SOCKET_ERROR,请致电WSAGetLastError() 并报告。
  • @SS 它返回 0,而在另一个线程中它返回接收到的数据。插座是 100% 相同的
  • 套接字处于非阻塞模式。我也尝试打开和关闭 nodelay - 同样的情况。
  • @Sergey:如果recv()返回0,表示连接被对方​​关闭。如果recv() 返回SOCKET_ERROR,则WSAGetLastError() 不应返回0,除非您在recv() 退出后但在调用WSAGetLastError() 之前重置调用线程的错误状态。
  • @Remy 不表示当系统收到 PSH 数据包且没有更多数据包时 recv()=0?

标签: c++ networking network-programming winsock c++builder


【解决方案1】:

如果我对您的理解正确,并且您尝试在同一 SOCKET 上并行线程上 recv,那么不要这样做,没有任何好处。你recv 的数据已经被底层系统缓冲并且你正在访问它,你可以做的是为recv 创建多个缓冲区,这样当它返回数据时,你可以将一个缓冲区传递给“上级别”用于处理并将另一个用于新的recv 调用。您也可以只使用一个带有通知的大缓冲区来处理什么用于处理以及什么部分用于接收。系统可能具有禁止在同一个套接字上进行多次读取的锁,因此一个recv 的结果是0。如果没有,您最终可能会得到一些几乎随机拆分的数据。

编辑:完整而长的解释

我认为使用多个线程从单个套接字读取是没有用的 套接字是软件监管的东西。您的网络设备不会创建任何“连接”,它只是处理接收到的数据并将它们包装/解包到IP(或任何其他 支持的 Internet 层)数据包(前一个取决于网络设备,其中一些几乎完全是由操作系统模拟的软件,实际上只执行基本的“写入 tx-read rx”服务,但对我们来说是相同的处理)。 WinSock2 服务识别具有特定数据的数据包(正如您已经注意到的),以便您可以同时使用一个网络设备 与多个对等方进行通信。 WinSock2 在将流量分发给您之前会主动监控流量。换句话说:当您即将成功获得recv 的数据时 已经存在并且底层系统已经检查了您在recv 中用作参数的套接字,并且仅将已标记为数据的数据交给您 对于那个插座。从一个套接字读取多个线程(没有几乎没用的MSG_PEEK)将使系统,如果它没有锁,复制未知数量的字节 到线程一中recv 中提供的位置,并将指向数据的内部指针永久地增加复制的字节数,然后,在整个数据可用之前 recv 在位置 1 被复制,另一个线程将启动并复制未知数量的字节,因此也会将指向数据的内部指针增加那么多字节。 理想情况下,此类读取的结果将是从线程 1 中提供的位置存储的数据的一半,另一半从线程 2 中提供的位置开始。由于理想的结果是不确定的(系统为这两个线程分配的时间不是保证相等)你最终会得到未排序的数据而没有任何排序方式 它,因为底层系统用于了解哪些数据属于哪个套接字的信息将无法提供给您。

由于您的系统很可能比您的网络设备更快,我支持我的两种解决方案,第一种是首选,因为我一直使用这种方法来传输大小数据块:

  1. 为每个连接的套接字创建一个读取线程和一个循环缓冲区,缓冲区的大小取决于您希望接收的块的大小以及您需要进一步处理这些内容的时间,保存当前读取位置,保存“处理计数”,当接收到数据时通知线程/线程它应该处理缓冲区中的数据,保存正在用于读取的数据的位置,如果有缓冲区空间未处理,则继续recv否则等到有(必须执行此操作以防您的计算机在某处阻塞,在正常情况下不应该)。当它们访问“to_process_count”和“当前读取位置”变量时,您必须将接收线程与处理线程/线程同步,因为它们会告诉 您可以在循环缓冲区中重用哪些字节。

  2. 为每个所需的读取线程创建并连接一个套接字,以便系统知道如何自行调节数据

您所说的从单个套接字读取的随机线程也可以通过以下方案实现:

1 线程枚举套接字以查看是否有可用数据 当数据可用时,它使用一些互斥锁来等待,如果某个线程已经处于读取状态,则启动一个新线程来读取处理现有数据

或者可以通过这样的方式实现

一旦成功完成recv,线程就会执行recv(是的,数据在缓冲区中)它会从某个线程池启动另一个线程来执行recv并继续处理数据并结束本身

这些是我可以想象的“在单个套接字上使用多个线程读取”是可以实现的唯一方法。是的,不会有多个线程同时调用recv

抱歉,这篇文章很长,拼写和语法错误,希望对您有所帮助

【讨论】:

  • 是的,这就是我认为的问题,但是我可以对我应该在线程 1 中接收但仅在线程 2 中接收的 recv() 数据做些什么?我正在使用并行线程与 FTP 交谈,当然还有关键部分,但有时我无法在线程 1 中接收来自 FTP 的响应,在我发送()我的命令的地方,回复将在线程 2 中。我可以在这里做什么?
  • 如果您绝对想与 FTP 并行通信,那么每个套接字只有一个读取线程(一个有效的原因是同时需要多个东西并且您不想路由)数据)您将需要为每个线程创建和连接一个套接字。我从来没有尝试过,因为我一直是我的应用程序中服务器和客户端的开发人员,并且总是自己路由数据,但这似乎是要走的路
  • 所以您认为在并行线程中使用套接字是危险的或错误的,或者您的意思是这是更快的方法?我真的认为我只需要调用一些可能是两个函数,我就可以在发送请求的线程中得到响应。您对系统缓冲区的看法是正确的,或者在套接字顶部创建一些层,每次线程切换时都会更改。我尝试了 TCP_NODELAY/nagle 开/关,SO_KEEPALIVE 开/关,改变缓冲区大小,SO_OOBINLINE 开/关,但它不影响这种情况,但确实改变了其他一些东西。
  • 我也尝试过 SwitchToThread()、Sleep()。我知道解决方案应该在实践中,因为这是在随机线程中使用相同套接字的非常广泛使用的技术。所以问题是用户应用程序可以在 Windows 环境中调用什么来解决缓冲/这个问题。
  • 谢谢你的解释,但是socket recv() 在临界区附近,所以只有一个线程可以执行recv() - 所以数据应该是非随机形式的,但在协议场景中。但是我仍然遇到一个线程(在时间 1)无法接收任何内容而另一个线程(在时间 1 也)可以接收数据的情况。仍然无法解决这个问题......
【解决方案2】:

确保套接字正确绑定到您在recv 函数中使用的句柄。 除非有接收数据的通道,否则无法加快数据接收速度。

【讨论】:

  • 事实上,套接字是 100% 相同的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-05
  • 2017-10-03
  • 1970-01-01
  • 2016-10-31
  • 2015-09-05
  • 1970-01-01
  • 2014-01-04
相关资源
最近更新 更多