Android 实时多人游戏 UDP 通过 wifi 延迟

joc*_*lyn 5 java networking android udp lag

问题介绍

大家好,我正在开发我的 Android 游戏“Space Corsair”的实时多人游戏模块。

它基于 UDP,每个设备都与处理游戏逻辑的中央 Java 服务器进行通信。客户端仅发送玩家输入并从服务器接收对象位置和事件。

我的第一个 POC 在模拟器、Nexus 7 和 HTC Desire 上运行良好。使用相同的代码版本,我的 Nexus 4 遇到一些网络问题(有时它可以工作,但大多数时候我都遇到问题):n4 似乎不是连续读取服务器数据包,而是缓冲它们并将它们传递到我的应用程序批量:玩家体验的巨大问题。

网络图

第一张截图:玩游戏时的正常 rx/tx 图

正常RT/TX

第二张截图:异常 rt/tx,客户端在峰值中接收到所有数据

rt/tx 有问题

测试协议

我在工作设备上使用 6 毫秒 ping 的本地 wifi 网络运行测试,我尝试增加数据包大小(现在为 508 字节,以确保一个数据包不会被丢弃),每 75 毫秒(而不是 500)执行一次 ping 操作以保持连接起来后,我在手机设置中禁用了 WiFi 电池优化。

奇怪的是,当我通过 USB 上传新版本的代码时,90% 的情况下工作正常。然后,当我完全退出游戏并从主屏幕重新启动游戏时,我出现了延迟。我尝试重新启动 Nexus 4,但启动时出现同样的问题。

网络代码

这是我的读者主题

public class UDPHandler implements Runnable {

    private boolean running = false;
    public static final Integer PACKET_SIZE = 508;

    private final PacketRecievedListener listener;

    public UDPSender sender;

    public UDPHandler(PacketRecievedListener listener) {
        this.listener = listener;
        this.sender = new UDPSender();
    }


    @Override
    public void run() {


        running = true;

        byte[] receiveData = new byte[PACKET_SIZE];

        DatagramSocket serverSocket = getLocalDatagramSocket();

        Logs.i("Udp with ip " + MultiUtils.getIPAddress(true) + " listening on port " + serverSocket.getLocalPort());

        DatagramPacket receivePacket = new DatagramPacket(receiveData, PACKET_SIZE);

        while (running) {
            try {

                serverSocket.receive(receivePacket);
                listener.onPacketRecieved(receivePacket.getData(), receivePacket.getAddress());
                receivePacket.setLength(PACKET_SIZE);

            } catch (SocketException e) {
                Logs.e("",e);
            } catch (IOException e) {
                Logs.e("",e);
            }
        }
    }


    /**
     * Used to bind reader to sender socket
     * Wait the sender to be ready and return the socket
     * @return
     */
    private DatagramSocket getLocalDatagramSocket() {


        while (UDPSender.socket == null) {

            try {
                Logs.i("Trying to obtain socket");
                Thread.sleep(500);
            } catch (InterruptedException e) {
                Logs.e("",e);
            }


        }
        return UDPSender.socket;
    }


    public void stop() {
        this.running = false;
    }


}
Run Code Online (Sandbox Code Playgroud)

以及创建套接字的代码(读者重用发送者套接字)

public void initSocket(){


    if (socket == null){

        try {
            DatagramChannel channel = DatagramChannel.open();
            socket = channel.socket();
            socket.bind(new InetSocketAddress(UDPSender.LOCAL_LISTENER_PORT));
            socket.setReuseAddress(true);


        } catch (SocketException e) {
            Logs.e("echec dans le socket",e);
        } catch (IOException e) {
            Logs.e("echec dans le socket", e);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)