通过Websockets传输的数据顺序混乱

3
我一直在学习如何通过Websockets实时传输和显示数据。在这个上下文中,“实时”仅意味着从传感器(带图像处理的相机)读取数据的时间为每0.03秒到1秒之间。每个数据点由时间和值(t,v)组成,并被编码为double类型(尽管在这种情况下时间总是整数,但我不会假设它总是这样)。
服务器端使用Alchemy Websockets实现(C#),因为我发现它非常容易理解/修改,以适应我的需求。
我利用了在{{link2:此处}}和{{link3:此处}}找到的Websockets示例,以及Alchemy附带的示例。
我正在使用HighCharts实时显示数据, 但我也将其打印到一个div中以进行调试(独立的示例,不会相互干扰)。
目前它已经工作得非常好了,但当我发送数据过快时就会出现明显的问题(明确一下,每秒或每两秒发送有关点的数据会产生漂亮的图形,看起来没有任何问题 - 当我更快地调用炼金术服务器“send”函数时,问题变得更加明显)。
数据似乎是以错误的顺序进入,导致出现有趣的“混乱”效果。

Mangled Data Points

我将开始深入研究服务器端缓冲区中包含的数据包顺序(当新用户连接并且服务器已经运行时,服务器会被指示发送一定数量的“历史”点-这会导致像上面展示的那样的明显问题),以及通过查看时间戳来确定客户端接收顺序。
这个错误是不一致的,每次重新加载页面都会导致不同的“混乱”数据集。这让我怀疑websocket通信或者涉及到alchemy服务器的某些东西可能是原因。
如果需要,我会附上完整的代码,但现在它还比较杂乱,所以我更多地寻找故障排除的想法。
我已经了解到这不是web socket的预期行为,因为它是建立在TCP之上的。
有什么建议/想法可以供参考吗?
谢谢!
编辑:我进行了另一个测试,检查每次刷新页面时有多少数据点是无序的。数字如下:
1 2 3 25 6 5 10 11 96 2 8
非常不一致(从未为0)。确实很有趣!
这个结果是通过排除图表组件,仅使用Websockets和数组来存储数据得出的。
更新:
我决定开始分析数据的顺序,发现使用相同的数据集时似乎随机接收到了无序点。我实现了一个“插入”函数,将考虑无序数据包。结果(加上一点主题更改)看起来很不错!

Yes I know it's not "random data".

一个开放性问题仍然存在:websocket是否有可能无序地传递信息?或者我的服务器端实现(或Alchemy)出了问题。我有时间会进一步调查。

解决方案!

我找到答案了!经过多次测试,我意识到我的Connection对象(负责监视数据集中的新数据并根据连接的配置发送数据)是使用Timer对象实现的。这是我从一个示例中学到的(通常我大多数异步任务都使用Thread对象)。

随着Timer对象加速,它开始异步地执行它以前对Tick函数的调用。这意味着极少数情况下,由于Alchemy Send函数的延迟,对它的Tick函数的一次调用会比另一次调用稍微快一些。这会导致轻微的乱序问题。

我将通信循环的实现从Timer对象更改为Thread对象,从而强制实现同步,使无序的数据包消失了!


2
你确定它是TCP吗?一个套接字可以是TCP或UDP,而这是UDP的完全可接受的行为。 - Scott Chamberlain
1
你在客户端获取数据时,存储数据的方式是什么?许多集合不保证放入对象的顺序。 - Scott Chamberlain
我被告知Websockets使用TCP。也许必须设置某个配置才能告诉它使用TCP?至于元素的存储,我只是使用标准的JavaScript数组对象(使用push()添加和shift()移除)。 - user986122
noVNC使用WebSockets传输大量图像数据,任何乱序数据都会导致损坏和失败。它被广泛使用,我从未见过或收到过乱序数据或事件的报告。我怀疑您的实现或对Alchemy的使用允许数据被重新排序。 - kanaka
3个回答

1

Websockets使用TCP,而TCP保证数据按顺序传递。

我希望浏览器也能按顺序触发websocket消息事件。在我的测试中,似乎是这样的。

使用这个简单的Node应用程序进行测试:

var sio = require('socket.io')
    , http = require('http')
    , express = require('express')
    , app = express()
    , srv = http.createServer(app)
    , io = sio.listen(srv)
    , fs = require('fs')
    , idx = fs.readFileSync('index.html').toString()
    ;

app.get('/', function(req, res) {
    res.send(idx);
});

var i = 0;
setInterval(function() {
    io.sockets.emit('count', i++);
}, 1);

srv.listen(888);

这只是尽可能快地发送WebSocket消息,每个消息都增加一个数字。客户端:
<script src="/socket.io/socket.io.js"></script>
<script>
var last = 0;
var socket = io.connect('/');
socket.on('count', function(d) {
    if (d-1 != last) console.warn('out of order!', last, d);
    last = d;
});
</script>

如果接收到的消息不是前一条消息加1的数字,则会发出控制台警告。

在Chrome和Firefox中,我看到了零个乱序消息。

我还尝试在接收到消息事件中阻塞一段时间(for (var i = 0; i < 1000000; i++) { }),以模拟会导致消息排队的工作。消息事件仍按顺序触发。

换句话说,这与您的设置无关。最有可能的是,Alchemy服务器实际上以不同的顺序发送消息。


好的领导!感谢测试!我今天将调查服务器端。肯定让人放心的是,我应该按顺序得到它们。我的修复方法非常低效! - user986122

0

我不知道问题是什么时候发布的。我也有类似的问题。我使用Alchemy客户端发送小数据,那没有问题。有很多聊天服务的例子。但当我发送大于4 KB的文件(不精确)时,就会出现问题。我尝试找出发生了什么。我编写了一个程序通过Alchemy客户端发送从0到7000的数字,并从UserContext.DataFrame(onreceive)接收,会发生DataFrame.ToString在大约508位置上会获得额外的"\0\0\0\0"。然后,在这个位置之后,数据顺序将不正确。我使用来自nuget的2.2.1版本。我在GiHub上读到了2.0版。源代码不可用。所以,太旧且没有参考价值。


0

不要使用计时器 这样在同步任务中具有异步回调的对象。使用一个 线程 并以这种方式运行通信循环。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接