滞后于java套接字

滞后于java套接字,java,algorithm,sockets,Java,Algorithm,Sockets,我正在用java用套接字制作一个多人蛇游戏。所有的传输都是通过一个服务器完成的,连接到所有的客户端。同样的代码还没有完全完成,但它完成了移动蛇的基本工作,并在特定客户吃了蛇的食物时增加分数 我从服务器端为食物坐标生成随机数,并将其转发给所有客户端。如果客户端按下一个键,则计算请求的移动,并将移动方向发送到服务器,然后服务器将移动转发给所有客户端(包括发送移动的客户端),只有在收到移动信息后,客户端才会对移动的蛇进行更改。因此,每一个移动都是通过网络进行跟踪的,在客户端收到移动请求之前,客户端本身

我正在用java用套接字制作一个多人蛇游戏。所有的传输都是通过一个服务器完成的,连接到所有的客户端。同样的代码还没有完全完成,但它完成了移动蛇的基本工作,并在特定客户吃了蛇的食物时增加分数

我从服务器端为食物坐标生成随机数,并将其转发给所有客户端。如果客户端按下一个键,则计算请求的移动,并将移动方向发送到服务器,然后服务器将移动转发给所有客户端(包括发送移动的客户端),只有在收到移动信息后,客户端才会对移动的蛇进行更改。因此,每一个移动都是通过网络进行跟踪的,在客户端收到移动请求之前,客户端本身不会做出任何移动决策,比如客户端“player1”请求移动

我面临的问题是,即使有两个玩家,在蛇移动一点后,坐标也会有差异

我可以对我的代码应用哪些可能的补救措施来消除蛇的位置之间的明显滞后

这是客户端代码:

package mycode;

import java.awt.Point;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Map;

import javax.swing.JOptionPane;

public class ConnectionManager implements Runnable {
    Socket socket;
    boolean start = false;
    DataInputStream in;
    DataOutputStream out;
    Map<String, Snake> map;

    ConnectionManager(String name, String IP, Map<String, Snake> m) {
        this.map = m;
        try {
            socket = new Socket(IP, 9977);
            in = new DataInputStream(new BufferedInputStream(
                    socket.getInputStream()));
            out = new DataOutputStream(new BufferedOutputStream(
                    socket.getOutputStream()));
            out.writeUTF(name);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, "Could Not Find Server",
                    "ERROR", JOptionPane.ERROR_MESSAGE);
            System.exit(0);
        }
    }

    void populateMap() {
        try {
            String name = in.readUTF();
            System.out.println("Name received: " + name);
            if (name.equals("start_game_9977")) {
                start = true;
                System.out.println("Game Started");
                return;
            } else if (name.equals("food_coord")) {
                Game.foodx = in.readInt();
                Game.foody = in.readInt();
                return;
            }
            map.put(name, new Snake(5));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    boolean start() {
        return start;
    }

    void increaseSnakeLength(String thisname){
        Snake temp = map.get(thisname);
        Point temp1=new Point(0,0);
        temp.length++;
        switch (temp.move) {
        case DOWN:
             temp1= new Point(temp.p[temp.length - 2].x,
                    temp.p[temp.length - 2].y+6);
             break;
        case LEFT:
            temp1= new Point(temp.p[temp.length - 2].x-6,
                    temp.p[temp.length - 2].y);
            break;
        case RIGHT:
            temp1= new Point(temp.p[temp.length - 2].x+6,
                    temp.p[temp.length - 2].y);
            break;
        case UP:
            temp1= new Point(temp.p[temp.length - 2].x,
                    temp.p[temp.length - 2].y-6);
            break;
        default:
            break;
        }
        if(temp1.y>Game.max)
            temp1.y=Game.min;
        if(temp1.x>Game.max)
            temp1.x=Game.min;
        if(temp1.y<Game.min)
            temp1.y=Game.max;
        if(temp1.x<Game.min)
            temp1.x=Game.max;
        temp.p[temp.length-1]=temp1;
    }

    void readMotion() {
        try {
            while (true) {
                if (Game.changedirection) {
                    String mov = "";
                    mov = Game.move.name();
                    // System.out.println(Game.move);
                    out.writeUTF(mov);
                    out.flush();
                    Game.changedirection = false;
                }
                if (Game.foodeaten) {
                    out.writeUTF("food_eaten");
                    out.flush();
                    Game.foodeaten = false;
                }
                Thread.sleep(50);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    void otherRunMethod() {
        try {
            while (true) {
                String mname = in.readUTF();
                String mov = in.readUTF();
                if (mov.equals("Resigned")) {
                    map.remove(mname);
                } else if (mov.length() >= 10) {
                    if (mov.substring(0, 10).equals("food_eaten")) {
                        String[] s = mov.split(",");
                        Game.foodx = Integer.parseInt(s[1]);
                        Game.foody = Integer.parseInt(s[2]);
                        int score = ++map.get(mname).score;
                        increaseSnakeLength(mname);
                        System.out.println(mname + ":" + score+" Length:"+map.get(mname).length);
                    }
                } else {
                    Game.move = Direction.valueOf(mov);
                    map.get(mname).move = Game.move;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        while (true) {
            if (!start) {
                populateMap();
            } else if (start) {
                new Thread(new Runnable() {
                    public void run() {
                        otherRunMethod();
                    }
                }).start();
                readMotion();
                break;
            }
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
封装mycode;
导入java.awt.Point;
导入java.io.BufferedInputStream;
导入java.io.BufferedOutputStream;
导入java.io.DataInputStream;
导入java.io.DataOutputStream;
导入java.net.Socket;
导入java.util.Map;
导入javax.swing.JOptionPane;
公共类ConnectionManager实现Runnable{
插座;
布尔开始=假;
数据输入流输入;
数据输出流输出;
地图;
ConnectionManager(字符串名称、字符串IP、映射m){
这个.map=m;
试一试{
插座=新插座(IP,9977);
in=新的DataInputStream(新的BufferedInputStream(
getInputStream());
out=新的DataOutputStream(新的BufferedOutputStream(
getOutputStream());
out.writeUTF(姓名);
out.flush();
}捕获(例外e){
e、 printStackTrace();
JOptionPane.showMessageDialog(null,“找不到服务器”,
“错误”,作业窗格。错误消息);
系统出口(0);
}
}
void populateMap(){
试一试{
字符串名称=in.readUTF();
System.out.println(“收到的名称:“+Name”);
if(name.equals(“start_game_9977”)){
开始=真;
System.out.println(“游戏开始”);
返回;
}如果(名称等于(“食品合作”)){
Game.foodx=in.readInt();
Game.foody=in.readInt();
返回;
}
地图放置(名称,新蛇(5));
}捕获(例外e){
e、 printStackTrace();
}
}
布尔起始(){
返回启动;
}
void递增Snakelength(字符串thisname){
Snake temp=map.get(此名称);
点temp1=新点(0,0);
温度长度++;
开关(温度移动){
按大小写:
temp1=新点(温度p[temp.length-2].x,
温度p[temp.length-2].y+6);
打破
案例左:
temp1=新点(温度p[temp.length-2]),x-6,
温度p[temp.length-2].y);
打破
案例权利:
temp1=新点(温度p[temp.length-2].x+6,
温度p[temp.length-2].y);
打破
个案:
temp1=新点(温度p[temp.length-2].x,
温度p[temp.length-2].y-6);
打破
违约:
打破
}
如果(temp1.y>Game.max)
temp1.y=Game.min;
如果(temp1.x>Game.max)
temp1.x=Game.min;

if(temp1.y我以前从未实现过网络多人游戏,但我认为最广泛使用的“解决方案”是作弊

我认为它被称为“推算死亡”,尽管snake的工作原理与此完全相同

基本上,你将游戏循环与网络更新分离。让每个客户端保持自己的状态,并简单地预测对手在每一帧的位置。然后当服务器的更新到达时,你可以将对手调整到他们的真实位置。为了隐藏这种差异,我认为将游戏状态呈现为f是很常见的ew毫秒前,而不是当前状态。这样,网络更新有更现实的机会赶上游戏循环,所以它看起来不那么起伏不定


正如我所说,我自己从来没有真正实现过这个,所以YMMV。这是游戏开发中比较难解决的问题之一。

我倾向于添加一个明确的setTcpNoDelay调用(true)。这将确保关闭该选项,从而禁用以通常少量增加延迟的方式提高效率的优化。

此答案是重述对话框以得出解决方案,并指出一些需要研究或尝试的其他领域

主要的软件行为问题是,有多个客户端导致不同的客户端在几次移动后显示不同的蛇位置

在通过评论提出了许多问题和回答之后,问题的发帖人修改了他们的软件,以便服务器将所有蛇的对象发送到所有客户机,从而使所有客户机现在都使用相同的蛇对象。以前,每个客户机都维护自己的蛇对象nake对象数据,仅接收snake数据中的更改或增量。通过此更改,所有客户端现在都通过服务器传输的snake对象进行同步,但是客户端显示的位置稍有不同仍然存在问题,这会在一两分钟后得到纠正,因为每个客户端都会收到所有数据的更新他蛇,客户成为同步的一个
package mycode;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.util.Map;

public class Playerhandler implements Runnable {
    Socket player;
    String thisname;
    Map<String, Socket> map;
    DataInputStream in = null;
    DataOutputStream out = null;
    ObjectInputStream ob;
    Snake snake;

    Playerhandler(Socket player, Map<String, Socket> m) {
        this.player = player;
        this.map = m;
        try {
            in = new DataInputStream(new BufferedInputStream(
                    player.getInputStream()));
            thisname = in.readUTF();
            map.put(thisname, this.player);
            populatePlayers();
            System.out.println("Connected Client " + thisname);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    void populatePlayers() {
        try {
            out = new DataOutputStream(new BufferedOutputStream(
                    player.getOutputStream()));
            for (String name : map.keySet()) {
                out.writeUTF(name);
                out.flush();
            }

            for (String name : map.keySet()) {
                out = new DataOutputStream(new BufferedOutputStream(map.get(
                        name).getOutputStream()));
                out.writeUTF(thisname);
                out.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    void relay(String move) {
        try {
            if (move.equals("food_eaten")) {
                move = move + ","
                        + (Snakeserver.randomGenerator.nextInt(100) * 6) + ","
                        + (Snakeserver.randomGenerator.nextInt(100) * 6);

            }
            for (String name : map.keySet()) {
                out = new DataOutputStream(new BufferedOutputStream(map.get(
                        name).getOutputStream()));
                out.writeUTF(thisname);
                out.flush();
                out.writeUTF(move);
                // System.out.println(Direction.valueOf(move));
                out.flush();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void run() {
        while (true) {
            try {
                relay(in.readUTF());
            } catch (IOException e) {
                // TODO Auto-generated catch block
                System.out.println("Player " + thisname + " Resigned");
                map.remove(thisname);
                relay("Resigned");
                return;
            }
        }
    }

}