Java-简单的网络游戏非常落后

Java-简单的网络游戏非常落后,java,networking,lag,Java,Networking,Lag,一天前我已经在codereview上问过这个问题,但我还没有得到任何回应,所以我想我应该试着在这里问一下 让我告诉你我想做什么: // server class public class Server { // networking objects private ServerSocket serverSocket; private Socket clientSocket; private DataOutputStream clientOutputStream;

一天前我已经在codereview上问过这个问题,但我还没有得到任何回应,所以我想我应该试着在这里问一下

让我告诉你我想做什么:

// server class
public class Server {
    // networking objects
    private ServerSocket serverSocket;
    private Socket clientSocket;
    private DataOutputStream clientOutputStream;
    private DataInputStream clientInputStream;
    // game objects
    private Vec2D serverPos, clientPos;
    private GameManager gameManager;
    // run method
    public void run() {
        // intialization try-catch block 
        try {
            // setup sockets
            serverSocket = new ServerSocket(1111);
            clientSocket = serverSocket.accept();
            // setup I/O streams
            clientOutputStream = new DataOutputStream(clientSocket.getOutputStream());
            clientInputStream = new DataInputStream(clientSocket.getInputStream());
        } catch(IOException e) { Util.err(e); }
        // declare & intialize data exchange thread
        Thread dataExchange = new Thread( 
            new Runnable() {
                // run method
                @Override
                public void run() {
                    // I/O try-catch block
                    try {
                        // exchange-loop
                        while(true) {
                            // write x & y, flush
                            synchronized(gameManager) {
                                clientOutputStream.writeInt(serverPos.x);
                                clientOutputStream.writeInt(serverPos.y);
                                clientOutputStream.flush();
                            }
                            // read x & y
                            clientPos.x = clientInputStream.readInt();
                            clientPos.y = clientInputStream.readInt();
                        }
                    } catch(IOException e) { Util.err(e); }
                }
            }
        );
        // setup game data
        serverPos = new Vec2D(10, 10);
        clientPos = new Vec2D(300, 300);
        gameManager = new GameManager(serverPos, clientPos, serverPos);
        // start data exchange thread
        dataExchange.start();
        // start main loop
        while(true) {
            // get keyboard input
            synchronized(gameManager) {    
                gameManager.update();
            }
            // repaint, sleep
            gameManager.repaint();
            Util.sleep(15);        
        }
    }
}
// client class
public class Client {
    // networking objects
    private Socket serverConnection;
    private DataOutputStream serverOutputStream;
    private DataInputStream serverInputStream;
    // game objects
    private Vec2D serverPos, clientPos;
    private GameManager gameManager;
    // run method
    public void run() {
        // intialization try-catch block 
        try {
            // setup socket
            serverConnection = new Socket(InetAddress.getByName("192.168.0.19"), 1111);
            // setup I/O streams
            serverOutputStream = new DataOutputStream(serverConnection.getOutputStream());
            serverInputStream = new DataInputStream(serverConnection.getInputStream());
        } catch(IOException e) { Util.err(e); }
        // declare & intialize data exchange thread
        Thread dataExchange = new Thread( 
            new Runnable() {
                // run method
                @Override
                public void run() {
                    // I/O try-catch block
                    try {
                        // exchange-loop
                        while(true) {
                            // read x & y
                            synchronized(gameManager) {
                                serverPos.x = serverInputStream.readInt();
                                serverPos.y = serverInputStream.readInt();
                            }
                            // write x & y, flush
                            serverOutputStream.writeInt(clientPos.x);
                            serverOutputStream.writeInt(clientPos.y);
                            serverOutputStream.flush();
                        }
                    } catch(IOException e) { Util.err(e); }
                }
            }
        );
        // setup game data
        serverPos = new Vec2D(10, 10);
        clientPos = new Vec2D(300, 300);
        gameManager = new GameManager(serverPos, clientPos, clientPos);
        // start data exchange thread
        dataExchange.start();
        // start main loop
        while(true) {
            // get keyboard input
            synchronized(gameManager) {    
                gameManager.update();
            }
            // repaint, sleep
            gameManager.repaint();
            Util.sleep(15);  
        }
    }
}
弹出一个窗口,询问用户是否要运行服务器或客户端。选择服务器将启动局域网上的服务器。选择客户端将尝试连接到该服务器。一旦服务器运行并且客户端连接,就会弹出一个带有两个方块的窗口。服务器/客户端都可以使用箭头键移动它们的方块

这就是我得到的:

// server class
public class Server {
    // networking objects
    private ServerSocket serverSocket;
    private Socket clientSocket;
    private DataOutputStream clientOutputStream;
    private DataInputStream clientInputStream;
    // game objects
    private Vec2D serverPos, clientPos;
    private GameManager gameManager;
    // run method
    public void run() {
        // intialization try-catch block 
        try {
            // setup sockets
            serverSocket = new ServerSocket(1111);
            clientSocket = serverSocket.accept();
            // setup I/O streams
            clientOutputStream = new DataOutputStream(clientSocket.getOutputStream());
            clientInputStream = new DataInputStream(clientSocket.getInputStream());
        } catch(IOException e) { Util.err(e); }
        // declare & intialize data exchange thread
        Thread dataExchange = new Thread( 
            new Runnable() {
                // run method
                @Override
                public void run() {
                    // I/O try-catch block
                    try {
                        // exchange-loop
                        while(true) {
                            // write x & y, flush
                            synchronized(gameManager) {
                                clientOutputStream.writeInt(serverPos.x);
                                clientOutputStream.writeInt(serverPos.y);
                                clientOutputStream.flush();
                            }
                            // read x & y
                            clientPos.x = clientInputStream.readInt();
                            clientPos.y = clientInputStream.readInt();
                        }
                    } catch(IOException e) { Util.err(e); }
                }
            }
        );
        // setup game data
        serverPos = new Vec2D(10, 10);
        clientPos = new Vec2D(300, 300);
        gameManager = new GameManager(serverPos, clientPos, serverPos);
        // start data exchange thread
        dataExchange.start();
        // start main loop
        while(true) {
            // get keyboard input
            synchronized(gameManager) {    
                gameManager.update();
            }
            // repaint, sleep
            gameManager.repaint();
            Util.sleep(15);        
        }
    }
}
// client class
public class Client {
    // networking objects
    private Socket serverConnection;
    private DataOutputStream serverOutputStream;
    private DataInputStream serverInputStream;
    // game objects
    private Vec2D serverPos, clientPos;
    private GameManager gameManager;
    // run method
    public void run() {
        // intialization try-catch block 
        try {
            // setup socket
            serverConnection = new Socket(InetAddress.getByName("192.168.0.19"), 1111);
            // setup I/O streams
            serverOutputStream = new DataOutputStream(serverConnection.getOutputStream());
            serverInputStream = new DataInputStream(serverConnection.getInputStream());
        } catch(IOException e) { Util.err(e); }
        // declare & intialize data exchange thread
        Thread dataExchange = new Thread( 
            new Runnable() {
                // run method
                @Override
                public void run() {
                    // I/O try-catch block
                    try {
                        // exchange-loop
                        while(true) {
                            // read x & y
                            synchronized(gameManager) {
                                serverPos.x = serverInputStream.readInt();
                                serverPos.y = serverInputStream.readInt();
                            }
                            // write x & y, flush
                            serverOutputStream.writeInt(clientPos.x);
                            serverOutputStream.writeInt(clientPos.y);
                            serverOutputStream.flush();
                        }
                    } catch(IOException e) { Util.err(e); }
                }
            }
        );
        // setup game data
        serverPos = new Vec2D(10, 10);
        clientPos = new Vec2D(300, 300);
        gameManager = new GameManager(serverPos, clientPos, clientPos);
        // start data exchange thread
        dataExchange.start();
        // start main loop
        while(true) {
            // get keyboard input
            synchronized(gameManager) {    
                gameManager.update();
            }
            // repaint, sleep
            gameManager.repaint();
            Util.sleep(15);  
        }
    }
}
服务器的方块以想要的速度移动,但在客户端,他的移动非常不平稳。另一方面,客户端方块似乎以每秒3像素的速度移动(速度太慢)

这就是我要问的:

// server class
public class Server {
    // networking objects
    private ServerSocket serverSocket;
    private Socket clientSocket;
    private DataOutputStream clientOutputStream;
    private DataInputStream clientInputStream;
    // game objects
    private Vec2D serverPos, clientPos;
    private GameManager gameManager;
    // run method
    public void run() {
        // intialization try-catch block 
        try {
            // setup sockets
            serverSocket = new ServerSocket(1111);
            clientSocket = serverSocket.accept();
            // setup I/O streams
            clientOutputStream = new DataOutputStream(clientSocket.getOutputStream());
            clientInputStream = new DataInputStream(clientSocket.getInputStream());
        } catch(IOException e) { Util.err(e); }
        // declare & intialize data exchange thread
        Thread dataExchange = new Thread( 
            new Runnable() {
                // run method
                @Override
                public void run() {
                    // I/O try-catch block
                    try {
                        // exchange-loop
                        while(true) {
                            // write x & y, flush
                            synchronized(gameManager) {
                                clientOutputStream.writeInt(serverPos.x);
                                clientOutputStream.writeInt(serverPos.y);
                                clientOutputStream.flush();
                            }
                            // read x & y
                            clientPos.x = clientInputStream.readInt();
                            clientPos.y = clientInputStream.readInt();
                        }
                    } catch(IOException e) { Util.err(e); }
                }
            }
        );
        // setup game data
        serverPos = new Vec2D(10, 10);
        clientPos = new Vec2D(300, 300);
        gameManager = new GameManager(serverPos, clientPos, serverPos);
        // start data exchange thread
        dataExchange.start();
        // start main loop
        while(true) {
            // get keyboard input
            synchronized(gameManager) {    
                gameManager.update();
            }
            // repaint, sleep
            gameManager.repaint();
            Util.sleep(15);        
        }
    }
}
// client class
public class Client {
    // networking objects
    private Socket serverConnection;
    private DataOutputStream serverOutputStream;
    private DataInputStream serverInputStream;
    // game objects
    private Vec2D serverPos, clientPos;
    private GameManager gameManager;
    // run method
    public void run() {
        // intialization try-catch block 
        try {
            // setup socket
            serverConnection = new Socket(InetAddress.getByName("192.168.0.19"), 1111);
            // setup I/O streams
            serverOutputStream = new DataOutputStream(serverConnection.getOutputStream());
            serverInputStream = new DataInputStream(serverConnection.getInputStream());
        } catch(IOException e) { Util.err(e); }
        // declare & intialize data exchange thread
        Thread dataExchange = new Thread( 
            new Runnable() {
                // run method
                @Override
                public void run() {
                    // I/O try-catch block
                    try {
                        // exchange-loop
                        while(true) {
                            // read x & y
                            synchronized(gameManager) {
                                serverPos.x = serverInputStream.readInt();
                                serverPos.y = serverInputStream.readInt();
                            }
                            // write x & y, flush
                            serverOutputStream.writeInt(clientPos.x);
                            serverOutputStream.writeInt(clientPos.y);
                            serverOutputStream.flush();
                        }
                    } catch(IOException e) { Util.err(e); }
                }
            }
        );
        // setup game data
        serverPos = new Vec2D(10, 10);
        clientPos = new Vec2D(300, 300);
        gameManager = new GameManager(serverPos, clientPos, clientPos);
        // start data exchange thread
        dataExchange.start();
        // start main loop
        while(true) {
            // get keyboard input
            synchronized(gameManager) {    
                gameManager.update();
            }
            // repaint, sleep
            gameManager.repaint();
            Util.sleep(15);  
        }
    }
}
我想我的问题很明显。我所做的就是通过互联网发送2个整数。现代网络游戏发送的数据比这多得多,而且它们几乎没有滞后,所以很明显我做错了什么,但是什么呢

Server.java:

// server class
public class Server {
    // networking objects
    private ServerSocket serverSocket;
    private Socket clientSocket;
    private DataOutputStream clientOutputStream;
    private DataInputStream clientInputStream;
    // game objects
    private Vec2D serverPos, clientPos;
    private GameManager gameManager;
    // run method
    public void run() {
        // intialization try-catch block 
        try {
            // setup sockets
            serverSocket = new ServerSocket(1111);
            clientSocket = serverSocket.accept();
            // setup I/O streams
            clientOutputStream = new DataOutputStream(clientSocket.getOutputStream());
            clientInputStream = new DataInputStream(clientSocket.getInputStream());
        } catch(IOException e) { Util.err(e); }
        // declare & intialize data exchange thread
        Thread dataExchange = new Thread( 
            new Runnable() {
                // run method
                @Override
                public void run() {
                    // I/O try-catch block
                    try {
                        // exchange-loop
                        while(true) {
                            // write x & y, flush
                            synchronized(gameManager) {
                                clientOutputStream.writeInt(serverPos.x);
                                clientOutputStream.writeInt(serverPos.y);
                                clientOutputStream.flush();
                            }
                            // read x & y
                            clientPos.x = clientInputStream.readInt();
                            clientPos.y = clientInputStream.readInt();
                        }
                    } catch(IOException e) { Util.err(e); }
                }
            }
        );
        // setup game data
        serverPos = new Vec2D(10, 10);
        clientPos = new Vec2D(300, 300);
        gameManager = new GameManager(serverPos, clientPos, serverPos);
        // start data exchange thread
        dataExchange.start();
        // start main loop
        while(true) {
            // get keyboard input
            synchronized(gameManager) {    
                gameManager.update();
            }
            // repaint, sleep
            gameManager.repaint();
            Util.sleep(15);        
        }
    }
}
// client class
public class Client {
    // networking objects
    private Socket serverConnection;
    private DataOutputStream serverOutputStream;
    private DataInputStream serverInputStream;
    // game objects
    private Vec2D serverPos, clientPos;
    private GameManager gameManager;
    // run method
    public void run() {
        // intialization try-catch block 
        try {
            // setup socket
            serverConnection = new Socket(InetAddress.getByName("192.168.0.19"), 1111);
            // setup I/O streams
            serverOutputStream = new DataOutputStream(serverConnection.getOutputStream());
            serverInputStream = new DataInputStream(serverConnection.getInputStream());
        } catch(IOException e) { Util.err(e); }
        // declare & intialize data exchange thread
        Thread dataExchange = new Thread( 
            new Runnable() {
                // run method
                @Override
                public void run() {
                    // I/O try-catch block
                    try {
                        // exchange-loop
                        while(true) {
                            // read x & y
                            synchronized(gameManager) {
                                serverPos.x = serverInputStream.readInt();
                                serverPos.y = serverInputStream.readInt();
                            }
                            // write x & y, flush
                            serverOutputStream.writeInt(clientPos.x);
                            serverOutputStream.writeInt(clientPos.y);
                            serverOutputStream.flush();
                        }
                    } catch(IOException e) { Util.err(e); }
                }
            }
        );
        // setup game data
        serverPos = new Vec2D(10, 10);
        clientPos = new Vec2D(300, 300);
        gameManager = new GameManager(serverPos, clientPos, clientPos);
        // start data exchange thread
        dataExchange.start();
        // start main loop
        while(true) {
            // get keyboard input
            synchronized(gameManager) {    
                gameManager.update();
            }
            // repaint, sleep
            gameManager.repaint();
            Util.sleep(15);  
        }
    }
}
Client.java:

// server class
public class Server {
    // networking objects
    private ServerSocket serverSocket;
    private Socket clientSocket;
    private DataOutputStream clientOutputStream;
    private DataInputStream clientInputStream;
    // game objects
    private Vec2D serverPos, clientPos;
    private GameManager gameManager;
    // run method
    public void run() {
        // intialization try-catch block 
        try {
            // setup sockets
            serverSocket = new ServerSocket(1111);
            clientSocket = serverSocket.accept();
            // setup I/O streams
            clientOutputStream = new DataOutputStream(clientSocket.getOutputStream());
            clientInputStream = new DataInputStream(clientSocket.getInputStream());
        } catch(IOException e) { Util.err(e); }
        // declare & intialize data exchange thread
        Thread dataExchange = new Thread( 
            new Runnable() {
                // run method
                @Override
                public void run() {
                    // I/O try-catch block
                    try {
                        // exchange-loop
                        while(true) {
                            // write x & y, flush
                            synchronized(gameManager) {
                                clientOutputStream.writeInt(serverPos.x);
                                clientOutputStream.writeInt(serverPos.y);
                                clientOutputStream.flush();
                            }
                            // read x & y
                            clientPos.x = clientInputStream.readInt();
                            clientPos.y = clientInputStream.readInt();
                        }
                    } catch(IOException e) { Util.err(e); }
                }
            }
        );
        // setup game data
        serverPos = new Vec2D(10, 10);
        clientPos = new Vec2D(300, 300);
        gameManager = new GameManager(serverPos, clientPos, serverPos);
        // start data exchange thread
        dataExchange.start();
        // start main loop
        while(true) {
            // get keyboard input
            synchronized(gameManager) {    
                gameManager.update();
            }
            // repaint, sleep
            gameManager.repaint();
            Util.sleep(15);        
        }
    }
}
// client class
public class Client {
    // networking objects
    private Socket serverConnection;
    private DataOutputStream serverOutputStream;
    private DataInputStream serverInputStream;
    // game objects
    private Vec2D serverPos, clientPos;
    private GameManager gameManager;
    // run method
    public void run() {
        // intialization try-catch block 
        try {
            // setup socket
            serverConnection = new Socket(InetAddress.getByName("192.168.0.19"), 1111);
            // setup I/O streams
            serverOutputStream = new DataOutputStream(serverConnection.getOutputStream());
            serverInputStream = new DataInputStream(serverConnection.getInputStream());
        } catch(IOException e) { Util.err(e); }
        // declare & intialize data exchange thread
        Thread dataExchange = new Thread( 
            new Runnable() {
                // run method
                @Override
                public void run() {
                    // I/O try-catch block
                    try {
                        // exchange-loop
                        while(true) {
                            // read x & y
                            synchronized(gameManager) {
                                serverPos.x = serverInputStream.readInt();
                                serverPos.y = serverInputStream.readInt();
                            }
                            // write x & y, flush
                            serverOutputStream.writeInt(clientPos.x);
                            serverOutputStream.writeInt(clientPos.y);
                            serverOutputStream.flush();
                        }
                    } catch(IOException e) { Util.err(e); }
                }
            }
        );
        // setup game data
        serverPos = new Vec2D(10, 10);
        clientPos = new Vec2D(300, 300);
        gameManager = new GameManager(serverPos, clientPos, clientPos);
        // start data exchange thread
        dataExchange.start();
        // start main loop
        while(true) {
            // get keyboard input
            synchronized(gameManager) {    
                gameManager.update();
            }
            // repaint, sleep
            gameManager.repaint();
            Util.sleep(15);  
        }
    }
}

我去掉了一堆代码——我希望现在不会让人困惑。谢谢你的帮助

如果您使用的是套接字,您可能会发现它对于实时对话来说比较滞后,因为它们是通过TCP内置的,必须确认消息并保持ping以查看连接是否仍然有效

也许您应该使用DatagramSocket,它在UDP协议上工作。不同之处在于UDP只是吐出一些东西,而不必麻烦保持连接的活动状态,甚至不需要尝试知道消息是否到达

使用示例:


编辑:为什么不尝试只在服务器中的位置更改时发送int?可能是服务器发送了太多的int,以至于客户端有一个满是相同值的缓冲区,当您逐个int读取而不是清空缓冲区时,您会有一种滞后的假象。

我没有阅读所有代码,但我确实注意到“客户端”和“服务器”两者都有线程在一个紧密的循环中读写更新

这有三个问题:

  • 如果客户端(或服务器)没有改变当前位置,那么它就没有必要告诉另一端当前位置

  • 因为客户端和服务器都严格地“先写后读,然后再写…”这两个线程进入锁定步骤,并且每个写/读周期都需要网络往返

  • 您在持有锁的同时正在执行部分工作,另一个线程正在抓取同一个锁并执行屏幕更新

因此,您需要安排:

  • 只有当职位发生实际变化时,才会发送职位更新,并且
  • 读和写发生在不同的线程上

@cyroxx发现了另一个同样会导致滞后的问题。

代码中的一个问题是
while(true)
循环:

    while(true) {
        // get keyboard input
        synchronized(gameManager) {    
            gameManager.update();
        }
        // repaint, sleep
        gameManager.repaint();
        Util.sleep(15);        
    }

这样,您发送的更新要么太多(当没有人按键时),要么太少(因为无论发生什么情况,您总是等待15毫秒)。如果您监听键盘事件,如果有,则将其传播到另一端,另一端可以作为对此“更改”事件的反应进行更新。您可能会发现实现这一点的方法很有用。

您的问题有很多代码。为了更快地得到更好的帮助,贴上一个“门把手”,我扔掉了一些我认为不必要的东西。TCP是我认为的问题,它得到了很多认可,这就是为什么它需要太多时间,使用UDP,阿门:)这会有什么不同吗?它现在真的很滞后,我很难相信我所要做的就是更改协议。@jesuplusplus11我没有看到任何错误,即使只是一个TCP连接也不应该那么慢。。。那个IP是直接连接吗?现代游戏通常做的也是插入数据和动画之间的差距。我想我对现代游戏如何进行联网做了一个假设。我不太清楚你的意思是IP是直接连接吗?UDP数据包的传递是不保证的,所以消息可能会丢失。@JesupLusplus11这里是API>,你可以循环readInt调用,直到出现EOF异常,或者用一个好的缓冲区调用read(byte[])方法,使用该字节数组创建一个nio ByteBuffer对象,调用ByteBuffer.asIntBuffer方法并从中读取INT:)我将尝试其他人建议的方法。好的建议,谢谢。我会按照你的建议去做,我知道如何提高绩效。