多设备(手机)同步 - React Native
P粉151466081
2023-09-05 11:58:38
<p>我正在尝试创建 Lightshow,它将同时在所有用户的移动设备上启动(最多可以有 2000-3000 个用户)。所有用户都将使用互联网(Wi-Fi 或移动数据)。在我们的 FE,我们使用 Apollo 进行订阅,这就像一个魅力。</p>
<p>但问题是,用户 A 可以比用户 B 更快地获取订阅事件。这意味着用户 A 将比用户 B 更早开始玩游戏,这是一个问题,因为我们有 lighshow 架构,而用户可以'不能早点或晚点开始。作为用户,您会立即看到差异,而且看起来不太好。</p>
<p>我看到的是延迟差异可以达到300ms。 android (小米 9) 与 ios (iPhone11) 之间的时间大多约为 180ms+-。如果我们比较两台 iOS 设备 iPhone 11 和 iPhone 13,差异约为 50-100 毫秒。对于所有设备,有没有办法消除这种差异,或者至少将其降低到 40-60 毫秒?</p>
<p>从 BE 开始,我们发送服务器时间(utc+0),灯光秀将以时间戳开始 => 在 FE 中,我添加 10 秒来为所有设备提供解决该功能的时间,因此启动被延迟 -> "时间戳 + 10 秒”。</p>
<p>在 FE 中,我使用库“react-native-ntp-client”在所有类型的设备上获得相同的时间(ios/android,因为我发现每个设备的时间略有不同)。然后我计算“start”和“ntpTime”之间的差异,并将其作为超时提供给我的 setTimeout 函数,这将触发灯光秀的开始。</p>
<p>下面我提供了示例,说明我如何使用灯光秀所在的屏幕。</p>
<pre class="brush:php;toolbar:false;">import dayjs from 'dayjs';
import ntpClient from 'react-native-ntp-client';
export const LightshowScreen: React.FC<
LightshowScreenProps<'Lightshow'>
> = ({route}) => {
const {data, loading} = useSubscription(JOIN_LIGHTSHOW_SUBSCRIPTION, {
variables: {lightshowId: route.params.lightshow.id},
});
useEffect(() => {
const getServerTime = async (lightshowStartAt: number) => {
// Delay start 10s to provide enough time to finish our functions - lightshowStartAt is timestamp from our server
const lightshowStart = dayjs.unix(lightshowStartAt).add(10, 's');
ntpClient.getNetworkTime('time.cloudflare.com', 123, (error, date) => {
if (error) {
console.error('Cant connect', error);
return;
}
console.log('NTP client date - ', date); // Mon Jul 08 2013 21:31:31 GMT+0200 (Paris, Madrid (heure d’été))
let ntpTime = dayjs(date);
// Diff in ms
const diff = lightshowStart.diff(ntpTime);
// After this timeout all devices should start play at the same time
setTimeout(() => {
lightshowStartHandler();
}, diff);
});
};
if (data && data?.joinLightshow.started) {
const lightshowData = data.lightshow;
getServerTime(lightshowData.startedAt);
}
}, [data]);
useEffect(() => {
if (data && data?.joinLightshow?.finished) {
lightshowFinishHandler();
}
}, [data]);
return (
<View style={style.container}>
....
</View>
);
};</pre>
<p>感谢您的所有评论和想法;)</p>
你想做的事情真的很难。同步这样的设备很困难。当您不拥有和控制硬件时,这样做几乎是不可能的。坦率地说,我认为你永远不会得到你想要的。
时间戳永远不会起作用。其一,这些设备不会都有相同的时间。他们都会稍微偏离。您的下一个想法是从中央源(例如您的服务器)向他们发送时间。问题在于,向每个设备发送数据将花费不同的、随机的时间。您可以尝试通过预先计算十几个数据包的往返时间来猜测延迟,但这仍然是一个猜测,对于下一个数据包可能不准确。 NTP 有助于使设备的时间接近同一时间,但没有达到您想要的精度。
即使它确实达到了您想要的准确性 - Android 也不是实时操作系统。 iPhone 则不然。即使您将闹钟设置为 12:00:00,它也不会恰好在 12:00:00.000 触发。在那之后的一段时间,当操作系统有空闲时间、空闲核心并且认为您的应用程序是最重要的调度应用程序时,它将触发。这可能需要几百毫秒。有些操作系统可以给您带来您想要的承诺。它们被称为实时操作系统,通常用于不会出现故障的嵌入式设备,例如医疗设备和昂贵机器的控制器。它们是一种与消费设备使用的完全不同的操作系统编写方法。
我真的建议重新考虑您的需求并更加现实地对待它们。有些技术可以让您得到您想要的东西,但不能通过互联网在消费者操作系统上的随机硬件上获得。
另外,如果你想这样做 - 我真的不建议使用 React Native,它以垃圾收集语言运行解释器,并且具有非常随机的计时。您至少需要用 C 语言编写启动器,因为这是最可预测的方法。
但实际上,请重新考虑您的需求。为什么需要在 50 毫秒内启动?当您通过互联网做事时,如果人们有一秒钟不同步,这真的很重要吗?