WebSocket在Native模式下发送数据Bug

使用websocket发送字节流,先构造一个大缓存区,然后每次都在该缓冲区填充数据,填充完后把有效数据发送出去,大概写法如下:

let buf=new ArrayBuffer(512);
//只发送从第10个字节开始的20个字节长度的数据
let pack=new Uint8Array(buf,10,20);
sock.send(pack);

该段代码在浏览器里面可以正确的发送20个字节的数据,但在native下,发送了512个字节的数据!即把大的缓冲区发出去了。

阅读了一下C++代码发现jswarpper/v8/object.cpp中的getTypedArrayData函数有问题,这个函数没有考虑到Uint8Array在ArrayBuffer中的偏移和长度,原始代码如下:

    bool Object::getTypedArrayData(uint8_t** ptr, size_t* length) const
    {
        assert(isTypedArray());
        v8::Local<v8::Object> obj = const_cast<Object*>(this)->_obj.handle(__isolate);
        v8::Local<v8::Uint8Array> arr = v8::Local<v8::Uint8Array>::Cast(obj);
        v8::ArrayBuffer::Contents content = arr->Buffer()->GetContents();
        *ptr = (uint8_t*)content.Data(); //内部的buffer
        *length = content.ByteLength(); //内部的buffer数据长度

        return true;
    }

这个段代码得到的数据和长度是Uint8Array内部ArrayBuffer的数据和长度,未考虑到UInt8Array只是ArrayBuffer中一部分的情况。

这段代码是不是应该这样写呢?

    bool Object::getTypedArrayData(uint8_t** ptr, size_t* length) const
    {
        assert(isTypedArray());
        v8::Local<v8::Object> obj = const_cast<Object*>(this)->_obj.handle(__isolate);
        v8::Local<v8::Uint8Array> arr = v8::Local<v8::Uint8Array>::Cast(obj);
        v8::ArrayBuffer::Contents content = arr->Buffer()->GetContents();
        //*ptr = (uint8_t*)content.Data();
        //*length = content.ByteLength();

	*ptr = ((uint8_t*)content.Data())+arr->ByteOffset();
	*length = arr->ByteLength();

        return true;
    }

经测试,修改之后,服务器收到的数据长度不再是固定长度512,而是实际数据长度。

1赞

cocos2d-x-lite v1.7 分支解决了这个问题。
这个问题会在1.7.1中修复,你也可以手动合并这两个PR:

https://github.com/cocos-creator/cocos2d-x-lite/pull/974

https://github.com/cocos-creator/cocos2d-x-lite/pull/989

或者直接同步v1.7分支代码。

好,原来你们已经发现了!这么晚了你们还在加班,辛苦!!

@dumganhar

昨晚游戏进行真机Native测试,websocket这块也遇到了奇怪的问题,搞得很懵逼,websocket send以后,服务器接收不到

cc.log(‘执行WebSocket发送…’);
conn.send(requestData);
cc.log(‘WebSocket发送成功…’);

日志打印发送成功,但是服务器端没有收到。
在chrome下测试没问题,用手机的浏览器测试也没问题。

是不是这个问题呢。

我也是构建的ArrayBuffer发送的。

你是用什么版本?

1.7.0 - rc1

你有合并以上两个补丁么?

没有,不想手工同步PR,不过都要到1.7.1了呀,1.7.0正式版还没有发呢

直接调试C++的Websocket的send函数,看看数据最终有没有发出。

或者你不要使用一个大的ArrayBuffer,切割小的Uint8Array即可。
直接构建一个完整的ArrayBuffer或者完整的Uint8Array,不要使用偏移,应该是没问题的。

+1 For this.
学点调试能力吧。

let finalBuffer = new ArrayBuffer(7 + contentBuffer.length);
let dataView = new DataView(finalBuffer);
dataView.setUint8(0, this.aa);
dataView.setUint16(1, this.bb, false);
dataView.setUint32(3, this.cc, false);
if (contentBuffer.length > 0) {
    let content = new Uint8Array(finalBuffer, 7);
    content.set(contentBuffer);
}

finalBuffer 是最终发送的内容

你在jsb_websocket.cpp的
static bool WebSocket_send(se::State& s)
函数中断点调试一下吧。

或者你给我一个demo,我看看在1.7.1中是否还有问题。

@dumganhar

其中一个问题找到了(昨晚还遇到了其他native请求的奇怪的问题),
ws.send()后如果立即 ws.close();
在浏览器下服务器会收到请求,如果是native下,就收不到请求。

难道浏览器下的 ws.send 是同步的,native下是异步的?

Demo:
NewProject.rar (133.3 KB)

send当然是异步的。

你说的native是native哪个平台?

Android Native
如果是异步的就没问题了,我这边改改逻辑,但奇怪的是浏览器环境虽然立即close,也是100%可发送,Android Native下是0%可发送。

不清楚底层websocket send和close的实现逻辑。

这个问题我加了异步逻辑后就没问题了,还是Android Native那个定时器无法启动的问题比较紧急,可以先看那个,而且我也有demo。

我确保绑定层没问题了,component中用schedule的问题,具体需要@jare再看了。