为什么客户端程序通过Java TCP-socket从服务器获取不正确的数据?

时间:2015-10-12 09:38:24

标签: java swing sockets tcp

我正在尝试使用套接字和基本图形库(Swing / AWT)使用Java实现一个简单的多人“游戏”。游戏的基本思想就是玩家可以使用箭头键在屏幕上移动方形图像,并看到其他玩家同时移动他们的方块。

我编写了一个服务器和一个客户端程序,到目前为止,我只用了2个玩家来测试游戏。当玩家不同时移动时游戏运行正常,但当他们同时移动时,会发生以下错误:

Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: 19140540
    at simplegameclient.Window.setCoordinates(SimpleGameClient.java:111)

因此,错误可以追溯到我的客户端程序中的第111行,我正在尝试根据ID为数组元素设置新值。由于某种原因,我从服务器获取的ID是随机大数,因此设置操作失败导致此错误。我在这里不明白的是,服务器怎么可能给我这个大ID,因为目前可能给客户端的最大可能ID是7?我的意思是,它突然在哪里获得了这么大的数字?应用程序逻辑是否有问题或TCP丢失数据包,错误是由此产生的?

来源见下文。我知道这可能不是非常有效和漂亮的代码,因为我还在学习网络的东西。因此,我希望人们能够专注于主要问题而不是小问题。

SimpleGameServer.java

package simplegameserver;

import java.io.*;
import java.net.*;
import deliverable.Packet;

public class SimpleGameServer {

    public static final int MAX_USERS = 8;
    public static int USERS_ONLINE = 0;
    static ServerSocket serverSocket;
    static Socket socket;
    static DataInputStream in;
    static DataOutputStream out;
    static ObjectOutputStream oout;
    static User[] users = new User[MAX_USERS];

    public static void main(String[] args) throws IOException {
        System.out.println("Starting server...");
        serverSocket = new ServerSocket(7777);
        System.out.println("Server started.");
        while (true) {
            socket = serverSocket.accept();
            System.out.println("Connection from: " + socket.getInetAddress());
            if (USERS_ONLINE == MAX_USERS) {
                out = new DataOutputStream(socket.getOutputStream());
                out.writeUTF("Server full. Try again later.");
                socket.close();
                continue;
            }
            for (int i=0; i<MAX_USERS; i++) {
                if (users[i] == null) {
                    USERS_ONLINE++;
                    in = new DataInputStream(socket.getInputStream());
                    out = new DataOutputStream(socket.getOutputStream());
                    oout = new ObjectOutputStream(socket.getOutputStream());
                    users[i] = new User(in,out,users,i,oout);
                    Thread t = new Thread(users[i]);
                    t.start();
                    break;
                }
            }
        }
    }  
}

class User implements Runnable {

    private DataInputStream in;
    private DataOutputStream out;
    private User[] users = new User[SimpleGameServer.MAX_USERS];
    private int id;
    private int idin, xin, yin;
    private ObjectOutputStream oout;

    User(DataInputStream in, DataOutputStream out, User[] users, int id, ObjectOutputStream oout) {
        this.in = in;
        this.out = out;
        this.users = users;
        this.id = id;
        this.oout = oout;
    }

    @Override
    public void run() {
        System.out.println("Thread started for new user.");
        try {
            out.writeInt(id);
            System.out.println("Id sent");
        } catch (IOException e) {
            e.printStackTrace();
        }
        while (true) {
            try {
                idin = in.readInt();
                xin = in.readInt();
                yin = in.readInt();
                Packet p = new Packet(idin,xin,yin);
                for (int i=0; i<SimpleGameServer.MAX_USERS; i++) {
                    if (users[i] != null) {
                        try {
                            users[i].oout.writeObject(p);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (IOException e) {
                this.users[id] = null;
                break;
            }
        }
        SimpleGameServer.USERS_ONLINE--;
    }
}

SimpleGameClient.java

package simplegameclient;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.*;
import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.JLabel;
import deliverable.Packet;

class Square {

    private int x,y,dx=0,dy=0;

    Square() {
        this.x = 20;
        this.y = 20;
    }

    public void move() {
        x+= dx;
        y+= dy;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public boolean hasMoved() {
        if (dx != 0 || dy != 0) {
            return true;
        }
        else {
            return false;
        }
    }

    public void keyPressed(KeyEvent e) {

        int key = e.getKeyCode();

        if (key == KeyEvent.VK_LEFT) {
            dx = -1;
        }
        if (key == KeyEvent.VK_RIGHT) {
            dx = 1;
        }
        if (key == KeyEvent.VK_UP) {
            dy = -1;
        }
        if (key == KeyEvent.VK_DOWN) {
            dy = 1;
        }
    }

    public void keyReleased(KeyEvent e) {

        int key = e.getKeyCode();

        if (key == KeyEvent.VK_LEFT) {
            dx = 0;
        }
        if (key == KeyEvent.VK_RIGHT) {
            dx = 0;
        }
        if (key == KeyEvent.VK_UP) {
            dy = 0;
        }
        if (key == KeyEvent.VK_DOWN) {
            dy = 0;
        }
    }
}

class Window extends JPanel implements Runnable, KeyListener {

    private Image image;
    private Square square;
    private DataOutputStream out;
    int[] x = new int[8];
    int[] y = new int[8];
    int uid;

    JLabel locationDisplay;

    Window(DataOutputStream out, int id) {
        ImageIcon ii = new ImageIcon(getClass().
                    getClassLoader().getResource("resources/square.png"));
        this.image = ii.getImage();
        this.uid = id;
        this.out = out;
        this.square = new Square();

        locationDisplay = new JLabel("");
        add(locationDisplay);

        System.out.println("Window initialized.");
    }

    public void setCoordinates(int id, int new_x, int new_y) {
        this.x[id] = new_x;
        this.y[id] = new_y;
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(image, square.getX(), square.getY(), this);
        locationDisplay.setText("X: " + square.getX() + " Y: " + square.getY()); 
        for (int i=0; i<8; i++) {
            g.drawImage(image, x[i], y[i], this);
        }
    }

    @Override
    public void run() {

        addKeyListener(this);
        setFocusable(true);

        // Send starting location to server
        try {
            out.writeInt(uid);
            out.writeInt(square.getX());
            out.writeInt(square.getY());
        } catch (Exception e) {
            e.printStackTrace();
        }

        while (true) {
            square.move();
            if (square.hasMoved()) { 
                // A button is being pressed sending data to server
                try {
                    out.writeInt(uid);
                    out.writeInt(square.getX());
                    out.writeInt(square.getY());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            repaint();
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        square.keyReleased(e);
    }

    @Override
    public void keyPressed(KeyEvent e) {
        square.keyPressed(e);
    }

    @Override
    public void keyTyped(KeyEvent e) {   
    }
}

public class SimpleGameClient extends JFrame {

    public static final int MAX_USERS = 8;
    static Socket socket;
    static DataInputStream in;
    static DataOutputStream out;
    static ObjectInputStream oin;
    private Window window;
    private int uid;

    SimpleGameClient() {
        initUI();
    }

    public void initUI() {
        try {
            System.out.println("Connecting to server...");
            socket = new Socket("localhost", 7777);
            System.out.println("Connected successfully.");
            oin = new ObjectInputStream(socket.getInputStream());
            in = new DataInputStream(socket.getInputStream());
            uid = in.readInt();

            out = new DataOutputStream(socket.getOutputStream());

            window = new Window(out, uid);
            Input input = new Input(in, window, uid, oin);

            add(window);
            setSize(400, 300);
            setResizable(false);
            setTitle("Simple Test Game");
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setLocationRelativeTo(null);

            Thread t = new Thread(input);
            t.start();
            Thread t2 = new Thread(window);
            t2.start();

        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Unable to start client.");
        }
    }

    public static void main(String[] args) {

        SimpleGameClient sgc = new SimpleGameClient();
        sgc.setVisible(true); 
    }
}

class Input implements Runnable {

    private DataInputStream in;
    private ObjectInputStream oin;
    private Window window;
    private int uid;

    Input(DataInputStream in, Window w, int uid, ObjectInputStream oin) {
        this.in = in;
        this.window = w;
        this.uid = uid;
        this.oin = oin;
    }

    @Override
    public void run() {
        while (true) {
            try {
                try {
                    Packet p = (Packet) oin.readObject();
                    int id = p.getID();
                    int x = p.getX();
                    int y = p.getY();
                    if (id != uid) { // if it's not our own id
                        window.setCoordinates(id, x, y);
                    }
                } catch (ClassNotFoundException ex) {
                    Logger.getLogger(Input.class.getName()).log(Level.SEVERE, null, ex);
                }
            } catch (IOException e) {
                System.out.println("Server not responding. Closing...");
                e.printStackTrace();
                System.exit(1);
            }
        }
    }
}

Packet.java

package deliverable;

import java.io.Serializable;

public class Packet implements Serializable {
    private int id;
    private int x, y;

    public Packet(int id, int x, int y) {
        this.id = id;
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }
    public int getY() {
        return y;
    }
    public int getID() {
        return id;
    }
}

1 个答案:

答案 0 :(得分:4)

现在来吧,在你做其他事情之前先阅读networking tutorial

out = new DataOutputStream(socket.getOutputStream());
oout = new ObjectOutputStream(socket.getOutputStream());

两个流共享同一个流?不是个好主意。