在java中进行socketing时出现巨大延迟

时间:2014-03-19 19:30:28

标签: java multithreading sockets delay

(更新代码) 我正在努力让客户与服务器进行通信(我已经制作了简单的客户端 - 服务器应用程序,比如聊天室)。通信已创建,但存在很大的延迟(我将坐标从客户端发送到服务器)。它超过10秒(有时甚至更多)。可能是什么问题呢? 客户:

    public class GameComponent extends Canvas implements Runnable {
    private static final long serialVersionUID = 1L;

    private static final int WIDTH = 320;
    private static final int HEIGHT = 240;
    private static final int SCALE = 2;

    private boolean running;

    private JFrame frame;
    Thread thread;

    public static final int GRID_W = 16;
    public static final int GRID_H = 16;

    private Socket socket;
    private DataInputStream reader;
    private DataOutputStream writer;

    private HashMap<Integer, OtherPlayer> oPlayers;
    private ArrayList<OtherPlayer> opList;
    private int maxID = 1;

    private int ID;

    Player player;

    public GameComponent() {
        //GUI code..

        oPlayers = new HashMap<Integer, OtherPlayer>();  //Hash map to be able to get players by their ID's
        opList = new ArrayList<OtherPlayer>();  //And an array list for easier drawing

        setUpNetworking();

        start();
    }

    public void start() {
        if (running)
            return;
        running = true;
        thread = new Thread(this);
        player = new Player(GRID_W * 2, GRID_H * 2);
        thread.start();
    }

    public void stop() {
        if (!running)
            return;
        running = false;
    }

    public void run() {  //The main loop, ticks 60 times every second
        long lastTime = System.nanoTime();
        double nsPerTick = 1000000000D / 60D;

        int frames = 0;
        int ticks = 0;

        long lastTimer = System.currentTimeMillis();
        double delta = 0;

        while (running) {
            long now = System.nanoTime();
            delta += (now - lastTime) / nsPerTick;
            lastTime = now;

            boolean shouldRender = true;

            while (delta >= 1) {
                ticks++;
                tick(delta);
                delta -= 1;
                shouldRender = true;
            }

            try {
                Thread.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (shouldRender) {
                frames++;
                render();
            }

            if (System.currentTimeMillis() - lastTimer >= 1000) {
                lastTimer += 1000;
                frames = 0;
                ticks = 0;
            }
        }
    }

    private void tick(double delta) {  //main logic
        player.move();
        try {
            writer.writeInt(ID);     //I send the player data here (id, x, y)
            writer.writeInt(player.getX());
            writer.writeInt(player.getY());
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private void render(Graphics2D g2d) {
        //rendering the stuff

        for (OtherPlayer i : opList) {  //drawing a black rectangle for every other player
            g2d.fillRect(i.getX(), i.getY(), GRID_W, GRID_H);
        }
    }

    private void render() {
        //more rendering...
    }

    public static void main(String[] args) {
        new GameComponent();
    }

    class TKeyListener implements KeyListener {
        //movement methods...
    }

    private void setUpNetworking() {  //This is where I make my message reader and data IO
        try {
            socket = new Socket("127.0.0.1", 5099);
            reader = new DataInputStream(socket.getInputStream());
            writer = new DataOutputStream(socket.getOutputStream());
            Thread rT = new Thread(new msgReader());
            rT.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    class msgReader implements Runnable {  //where I read messages
        public void run() {
            try {
                ID = reader.readInt();   //when I connect, I get an id from the server

                while(true) {   //my main loop
                    int oid = reader.readInt();   //get the read data id
                    int ox, oy;

                    ox = reader.readInt();   //get the read player's x and y
                    oy = reader.readInt();

                    if (oid != ID){   //If not reading myself
                        if (oPlayers.containsKey(oid)) {   //If a player with this id exists
                            OtherPlayer op = (OtherPlayer) oPlayers.get(oid);
                            op.setX(ox);  //set it's x, y
                            op.setY(oy);
                        } else {  //if it doesn't exist, create him
                            OtherPlayer op = new OtherPlayer(ox, oy);
                            opList.add(op);
                            oPlayers.put(oid, op);
                        }
                    }
                    maxID = reader.readInt();  //Allways read the highest current id from server
                }
            } catch(Exception ex) {
                ex.printStackTrace();
            }
        }
    }
}

服务器:

public class ServerBase {
    ServerSocket serverSocket;
    ArrayList<DataOutputStream> clients;
    private int id = 1;
    SyncSend ss = new SyncSend();

    class ClientHandler implements Runnable {
        private Socket soc;
        private DataInputStream reader;
        private int x;
        private int y;
        private int id;
        private boolean run = true;

        public ClientHandler(Socket s) {
            soc = s;
            try {
                reader = new DataInputStream(soc.getInputStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public void run() {
            try {               
                while (run) {
                    id = reader.readInt();
                    x = reader.readInt();
                    y = reader.readInt();

                    if (id == 2)
                        System.out.println("x: " + x + " y: " + y);

                    int[] tmb = {id, x, y};
                    ss.sendEveryone(tmb);
                }
            } catch (Exception e) {
                run = false;
                clients.remove(this);
            }
        }
    }

    class SyncSend {
        public synchronized void sendEveryone(int[] a) throws SocketException {
            ArrayList<DataOutputStream> cl = (ArrayList<DataOutputStream>) clients.clone();
            Iterator<DataOutputStream> it = cl.iterator();
            while(it.hasNext()){
                try {
                    DataOutputStream writer = (DataOutputStream) it.next();
                    writer.writeInt(a[0]);
                    writer.writeInt(a[1]);
                    writer.writeInt(a[2]);
                    writer.writeInt(id-1);
                    writer.flush();
                } catch (Exception ex) {
                    throw new SocketException();
                }
            }
        }
    }


    public void init() {
        clients = new ArrayList<DataOutputStream>();

        try {
            serverSocket = new ServerSocket(5099);

            while(true) {
                Socket clientSocket = serverSocket.accept();
                DataOutputStream clientWriter = new DataOutputStream(clientSocket.getOutputStream());
                clients.add(clientWriter);
                clientWriter.writeInt(id);
                id++;

                Thread t = new Thread(new ClientHandler(clientSocket));
                t.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new ServerBase().init();
    }
}

导致延误的原因是什么?我一直在寻找几个小时的原因,但没有成功。

3 个答案:

答案 0 :(得分:2)

您很可能需要在客户端调用flush()。即使这不是你当前的问题,也许这是一个好主意。

Streams可能会缓冲其内容,这意味着他们可能无法将数据发送到目的地(无论是磁盘还是通过电线连接到服务器),您拨打的时间write(或writeInt in这个案例)。相反,他们可能要等到他们获得足够数量的数据才能使转移“值得”。如果他们不以这种方式行事,他们最终会做出许多效率低下的小转会。所有这一切的缺点是,您可能需要调用flush告诉流您已完成数据发送一段时间,并且流应该继续并启动传输。

答案 1 :(得分:0)

尝试将代码放到几个线程中,然后调用线程,我的意思是你不需要等待每个套接字,只需同时运行所有这些...或类似的东西:)

例如在端口扫描程序中,您应该使用许多线程来加速搜索......

答案 2 :(得分:0)

请注意,您对ss.sendEveryone(tmb)ss的呼叫已同步。我假设这是一个static变量,它包含对所有客户端的引用。这意味着如果有多个客户端同时发送数据,那么很多同时会发生sendEveryone的调用,并且它们将排队等待其他客户端完成,然后这些线程可以返回并再次从客户端读取更多数据。

作为诊断练习,您可能希望删除此来电并查看是否仍有问题。