2013-04-22 99 views
2

我已经在codereview上提前一天提出过这个问题,但是我还没有收到任何回复,所以我尽管在这里试着问一下。Java - 简单的网络游戏极其缓慢

让我告诉你我想要做:

弹出一个窗口,询问用户是否要运行一个服务器或客户端。选择服务器将启动LAN上的服务器。选择客户端将尝试连接到该服务器。一旦服务器正在运行并且客户端已连接,一个窗口会弹出两个方块。服务器/客户端都可以使用箭头键移动它们的正方形。

这就是我得到:

在想要的速度移动服务器的广场,但他的动作是在客户端的侧面非常连贯。另一方面,客户方正似乎以每秒约3个像素的速度移动(方式太慢)。

这是我在问:

我想我的问题是很明显的。我所做的只是通过互联网发送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.java:

// 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

你的问题有很多代码。为了更快地获得更好的帮助,请发布[SSCCE(链接)](http://sscce.org)。 – Doorknob 2013-04-22 13:05:16

+2

@ Doorknob我摆脱了一些我认为不需要的东西。 – Aaron 2013-04-22 13:12:46

+0

TCP是我认为的问题,它得到了很多的承认,这就是为什么它需要太多的时间,使用UDP,阿门:) – anshulkatta 2013-04-22 13:23:44

回答

4

您正在使用套接字,也许您会发现实时会话滞后,因为它们是通过TCP构建的,因此必须确认该消息并持续查看连接是否仍然存在。

也许你应该使用DatagramSocket,它使用UDP协议。不同之处在于UDP只是吐出了一些东西,而没有让连接继续存在,甚至不知道消息是否到达。使用

例如:http://docs.oracle.com/javase/tutorial/networking/datagrams/clientServer.html

编辑:你为什么不尝试发送这个int服务器的变化仅在位置?可能服务器发送了太多的整数,以至于你的客户端有一个充满相同值的缓冲区,并且你通过int读取int而不是清空缓冲区,因为你有假的感觉是laggy。

+0

这将使所有的区别,但?它现在*真*滞后,而且我很难相信我所要做的就是改变协议。 – Aaron 2013-04-22 13:15:13

+0

@ JesusPlusPlus11我没有看到任何错误,即使只是一个TCP连接不应该那么慢......是IP的直接连接?另外现代游戏通常做的是内插数据并为中间的间隙生成动画。 – Khanser 2013-04-22 13:25:34

+0

我想我对现代游戏如何进行联网做了一个假设。我不太确定你的意思是* IP是直接连接吗?* – Aaron 2013-04-22 13:28:32

2

我还没有读完所有的代码,但我确实注意到“客户端”和“服务器”都有线程,可以在紧密的循环中读取和写入更新。

有三个问题是:

  • 没有告诉另一端的当前位置,如果它没有改变点的客户端(或服务器)。

  • 由于客户端和服务器都严格地“写入然后读取然后写入...”,两个线程进入锁定步骤,​​并且每个写入/读取周期需要网络往返。

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

所以,你需要安排是:

  • 时的位置,实际上改变的位置更新仅发送,
  • 读写发生在不同的线程。

@cyroxx发现了另一个问题,也会导致延迟。

+0

很好的建议,谢谢。我会做你建议的这些事情,我会看到他们如何提高表现。 – Aaron 2013-04-22 13:31:25

2

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

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

这样,你发送要么太多更新(当没有人按下某个键)或过少的更新(因为你总在等待15毫秒,不管发生什么)。如果您倾听键盘事件,并且如果有键盘事件传播到另一侧,则会更好 - 另一侧可以更新为对此“更改”事件的反应。你可能会发现Observer pattern对实现这一点很有用。

+0

我会尝试以及其他人建议的事情。 – Aaron 2013-04-22 13:29:50