我是公众号「线下聚会游戏」的作者,开发了一些小游戏,可以和朋友联机一起玩,而且不用下载。
其中的联机功能,就是通过WebSocket实现的,中途也踩了一些坑,分享给大家。
这个参数就是错误码,表明了关闭连接的原因:
WebSocket断开连接时,会发送一个错误码给另一方。如果是浏览器主动断开连接,浏览器发错误码给服务器。如果是服务器断开连接,服务器发错误码给浏览器。
所有错误码可参考 MDN: CloseEvent Code。
在浏览器中,调用ws.close()
函数关闭连接时,默认错误码是1005,含义是 no status code was provided even though one was expected。
这是容易犯错的,可能很多人认为它的默认值是1000(正常关闭)。结果服务器收到的却是1005。
如果前端关闭是正常关闭,你可以使用ws.close(1000)
。
如果前端关闭不是正常关闭,你需要自定义一个异常错误码,范围是4000-4999。
此外,如果你在开发一个框架,那么你可用的错误码范围是3000-3999。
ws.binaryType
有2种值:blob
和arraybuffer
。
blob
是它的默认值。
如果你收到了二进制数据:当ws.binaryType
为blob
时,event.data是Blob类型,你需要调用await event.data.arrayBuffer()
获取ArrayBuffer类型的数据。
如果你收到了二进制数据,当ws.binaryType
为arraybuffer
时,event.data是ArrayBuffer类型。
我的《联机桌游合集》刚上线时,有个使用iOS的朋友告诉我,她无法进入游戏,重试了多次也不行。
但是我已经用我手头的安卓、iPad、iPhone、Mac、Windows全都测试过一遍了。
经过排查,才发现是她的iOS14中Safari浏览器搞的鬼。虽然我没有设置ws.binaryType
为arraybuffer
,
但是因为Safari检测到是二进制数据,就直接把event.data
转换为了ArrayBuffer类型,不是Blob类型,导致我调用await event.data.arrayBuffer()
时出错了。
如果你ws收到的数据都是二进制格式,在调用const ws = new WebSocket()
后,立马设置ws.binaryType = 'arraybuffer'
。
但是如果你ws可能收到二进制数据,也可能收到文本数据,建议参考MDN官方案例,设置ws.binaryType
为arraybuffer
,但是加个条件判断:
const ws = new WebSocket("ws://localhost:8080");
// Change binary type from "blob" to "arraybuffer"
ws.binaryType = "arraybuffer";
ws.onmessage = (event) => {
if(event.data instanceof ArrayBuffer) {
// binary frame
console.log(event.data);
} else {
// text frame
console.log(event.data);
}
});
我在Mac环境下,使用Safari浏览器和Chrome浏览器的WebSocket,有2种不同的现象:
如果后端发送给前端的消息中,包含了\n
换行符。在Chrome中,会触发多次onmessage
事件,各个消息是被Chrome基于\n
分割开了,分割后的消息按顺序依次触发onmessage
来处理。在Safari中,只触发了一次onmessage
事件,Safari没有帮我们分隔消息。
事实上,在WebSocket消息中,\n
换行符本身就是区分消息的特殊符号。如果需要短时间内连续发送多条消息给客户端,一种常见的优化手段就是把这些消息一次性发送过去,用\n
分割。
Chrome做的很好,帮我们分割好了。但是像Safari这种浏览器没有帮我们分割,为了兼容性,我们也需要处理下。
如果后端有「批量发送」的机制,就在onmessage
事件中,把消息按\n
分割后,再依次处理。如果后端没有实现「批量发送」的机制,则可以忽略。
ws.onmessage = (event) => {
event.data.split('\n').forEach((message) => {
// 处理各个message
});
};
这里的坑不是特别大,但如果你要做压力测试,那就可能会遇到坑。你需要知道:
Chrome有个特点:如果你同时建立多个WebSocket连接,只会一个一个建立。等前一个ws建立连接成功,后一个ws开始建立连接。
引申阅读:这不是Chrome的特点,而是在 RFC6455 中规定的:
If the client already has a WebSocket connection to the remote host (IP address) identified by /host/ and port /port/ pair, even if the remote host is known by another name, the client MUST wait until that connection has been established or for that connection to have failed. There MUST be no more than one connection in a CONNECTING state.
更多细节,详见 RFC6455 Page 14、Page 15。
如果你想测试后台服务同时被多个客户端连接,是否存在并发问题时,不要用同一个Chrome Tab来测。可以开多个Tab和多个浏览器,或者用Safari测试,也可以用NodeJS来测试。
因为在Safari上:如果你同时建立多个WebSocket连接,是同时发送ws连接请求的(当然注意ws同时连接数有上限,做压测时,一个Tab没必要一次性连太多,是没用的)。
我是HullQin,公众号线下聚会游戏的作者(欢迎关注我,交个朋友)。转发本文前需获得作者HullQin授权。我独立开发了一些小游戏,是个网页,可以很方便的跟朋友联机玩UNO、飞行棋、斗地主、五子棋、一夜狼、狼人杀、象棋、德国心脏病、达芬奇密码等游戏,不收费无广告。还开发了《Dice Crush》参加Game Jam 2022。喜欢可以关注我噢~我有空了会分享做游戏的相关技术,会在这个专栏里分享:《教你做小游戏》。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。