我前一天已经在codereview上问了这个,但我还没有得到任何答复,所以我想在这里问一下。
让我告诉你我想做什么:
弹出一个窗口,询问用户是否要运行服务器或客户端。选择服务器将启动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);
}
}
}
我摆脱了一堆代码 - 我希望现在不要混淆。谢谢你的帮助!
答案 0 :(得分:4)
您正在使用套接字,也许您认为它对于实时对话来说是滞后的,因为它们是通过TCP构建的,必须确认消息并继续ping以查看连接是否仍然存在。
也许您应该使用适用于UDP协议的DatagramSocket。不同之处在于,UDP只是在没有保持连接存在的情况下发送内容,甚至试图知道消息是否到达。
使用示例:http://docs.oracle.com/javase/tutorial/networking/datagrams/clientServer.html
编辑:为什么不在服务器中的位置发生变化时才尝试发送该int?可能服务器发送的内容太多了,你的客户端有一个充满相同值的缓冲区,当你用int读取int而不是清空缓冲区时,你会有一种迟钝的假感觉。
答案 1 :(得分:2)
我没有阅读所有代码,但我注意到“客户端”和“服务器”都有线程,可以在紧密循环中读取和写入更新。
这有三个问题:
如果客户端(或服务器)没有更改,则客户端(或服务器)无需告知另一端当前位置。
因为客户端和服务器都严格“写入然后读取然后写入...”两个线程进入锁定步骤,并且每个写入/读取周期都需要网络往返。
< / LI>你正在做一部分工作,同时拿着一把锁,还有另一个线程抓住同一个锁并进行屏幕更新。
所以你需要安排:
@cyroxx发现了另一个也会导致懒散的问题。
答案 2 :(得分:2)
代码中的问题是while(true)
循环:
while(true) {
// get keyboard input
synchronized(gameManager) {
gameManager.update();
}
// repaint, sleep
gameManager.repaint();
Util.sleep(15);
}
这样,您发送的更新太多(没有人按任何键)或更新太少(因为无论发生什么情况,您总是等待15毫秒)。如果你听了键盘事件会更好,如果有的话,将它传播到另一侧 - 然后另一方可以更新为对这个“更改”事件的反应。您可能会发现Observer pattern对实现此功能很有用。