【问题标题】:Messages being delayed when using websockets使用 websocket 时消息被延迟
【发布时间】:2019-02-01 14:23:14
【问题描述】:

我有一个使用 Websocket TCP 的程序:客户端是 Chrome 中的扩展,服务器是用 C++ 编写的应用程序。
当我将小数据从client 发送到server 时,它工作正常。但是当我发送大量数据(例如源 html 页面)时,它会稍微延迟。


例如:

  • 客户端发送:1,2,3
    • 服务器收到:1,2
  • 客户端发送:4
    • 服务器接收:3
  • 客户端发送:5
    • 服务器接收:4

这似乎是一个延迟。

  1. 这是我的代码客户端:

var m_cWebsocket = new WebSocket("Servername"); 
if (m_cWebsocket == null) { return false; } 
m_cWebsocket.onopen = onWebsocketOpen(m_cWebsocket); m_cWebsocket.onmessage = onWebsocketMessage; 
m_cWebsocket.onerror = onWebsocketError; 
m_cWebsocket.onclose = onWebsocketError; 
I using m_cWebsocket.send(strMsg) to send data. 
  1. 服务器代码

while (true) { recv(sSocket, szBufferTmp, 99990, 0); //recv(sSocket, szBufferTmp, 99990, MSG_PEEK); //一些过程 }

【问题讨论】:

  • 您好,您能告诉我们一些您用来通过 websocket 发送和接收数据的代码吗?

标签: javascript c++ sockets websocket


【解决方案1】:

由于您没有发布任何代码来显示您的 TCP 服务器或客户端的实现,我只能推测并尝试解释这里发生的情况可能

这意味着我在下面概述的潜在问题和解决方案可能适用于您,也可能不适用于您,但无论如何,这些信息仍对将来可能会发现此问题的其他人有所帮助。


TL;DR:(很可能)服务器太慢,服务器没有正确等待完整的“tcp 数据包”被缓冲,或者服务器不知道什么时候正确启动和停止并在等待它认为是由缓冲区大小等定义的“完整数据包”时取消同步。

在我看来,您从客户端推送数据的速度比服务器可以读取的服务器快,或者更可能的是,服务器正在缓冲来自当前 TCP Stream 的一组字节数并等待缓冲区在输出附加数据之前填充。

如果您通过localhost 发送此内容,那么您不太可能接近流的限制,我希望用 C++ 编写的服务器能够跟上 javascript 客户端的速度。

所以这让我相信问题实际上是 C++ 端的流缓冲区。

现在,由于服务器无法知道您正在发送什么数据以及发送了多少数据,因此 TCP 流通常会利用一个流缓冲区来连续地从套接字读取数据,直到缓冲区已填充到已知大小,或者直到它看到预定义的“停止字符”。这通常类似于“行尾”或\n 字符,有时是\n\r(换行符、回车符),具体取决于您的操作系统。

由于您没有指定接收数据的方式,我将假设您创建了一个特定大小的charbyte 缓冲区。我对我的 C++ 套接字信息很生疏,所以我可能是错的,但我确实相信 C++ tcp 流上也有一个默认的“读取超时”。

这意味着您可能遇到 2 个问题中的 1 个。

情况 1) 在输出数据之前,您正在等待 byte/char 缓冲区填满。问题是,它就像一辆公共汽车,只有在所有座位都坐满后才会离开车站。如果你没有填满所有的座位,你的服务器就只是坐着等待,直到它得到更多的数据来完全填满并输出你的数据。

情况 2) 您遇到了套接字读取超时,因此该函数在输出数据之前没有获取所有数据。这就像一辆按时钟运行的公共汽车。每 10 分钟那辆公共汽车离开车站,不管那辆公共汽车是满还是空,它都会离开,下一班车会接任何迟到的人。在您的情况下,TCP 流无法以足够快的速度将1, 2 and 3 加载到总线上,因此总线上仅留下1, 2,因为在 20 毫秒未接收数据后,服务器将退出函数并输出数据。然而,在下一个循环中,3 在流缓冲区的顶部等待准备搭乘下一个总线。 Stream 将加载3,等待这 20 毫秒完成,然后在重复此循环之前退出。

我认为第一种情况更有可能发生,因为我希望服务器要么开始追赶,要么进一步落后,因为 2 个服务器开始同步,或者内部 TPC 流缓冲区填满服务器越来越落后。

这里的重点,您需要一些方法来同步客户端和服务器连接。我建议在消息开始和结束时将“开始字节”和“结束字节”发送到单个,这样您就不会过早退出该功能。
或者发送一个起始字节,然后是数据包大小(以字节为单位),然后填充缓冲区,直到缓冲区具有正确的字节数。此外,您还可以包含一个结束字节以进行一些基本的错误检查。

这是一个非常复杂的话题,如果没有您提供任何代码,很难真正给您一个好的答案,但这也应该对将来可能遇到类似问题的任何人有所帮助。

编辑我回去重新阅读了您的问题并注意到您说它仅涉及大量数据,所以我认为我最初的假设是错误的,并且更有可能是情况 2,因为客户端向您的服务器发送数据的速度比服务器读取数据的速度要快,因此可能会阻塞连接,并且客户端只能在服务器已清空其 TCP 流缓冲区的一部分。

把它想象成一管水。插座(管)只能在充满之前接受(填充)如此多的数据(水)。不过,一旦你让一些水从底部流出,你就可以再把它填满一点。它适用于小文件的唯一原因是文件太小而无法填满整个试管。

其他想法:你可以在这个问题中看到我是如何在 C# 中解决这个问题的:Continuously reading from serial port asynchronously properly

还有我之前遇到的另一个类似问题(同样是在 C# 中):How to use Task.WhenAny with ReadLineAsync to get data from any TcpClient

不过,我已经有一段时间没有使用 TCP 流了,所以我很抱歉,我不记得协议的所有利基细节和警告,但希望这些信息足以让你了解解决你的问题。


完全免责声明,自从我上次接触 C++ TCP 套接字以来已经有 2 年多了,并且从那时起就使用其他语言(如 C# 和 JavaScript)的套接字/websockets,所以我可能对 C++ 的行为有一些错误TCP 套接字,但核心信息仍应适用。如果我有任何错误,cmets 中的某个人很可能会得到正确的信息。

最后,欢迎堆栈溢出!

【讨论】:

  • 是的,当我使用 recv(sSocket, szBufferTmp, 99990, MSG_PEEK);我可以在输入队列中看到数据。但是当我使用 recv(sSocket, szBufferTmp, 99990, 0) 接收数据时,我的函数在这里被阻塞。那么我该如何解决呢。
  • @Khanh 我需要更多信息才能为您提供帮助。客户端发送代码是什么样的?服务器代码是什么样的?如果您不想阻塞,您应该查看我提供的一些链接,这些链接演示了从套接字异步读取。虽然我只是在黑暗中拍摄,但没有全面了解您的代码。
猜你喜欢
  • 1970-01-01
  • 2019-07-18
  • 1970-01-01
  • 1970-01-01
  • 2015-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-11
相关资源
最近更新 更多