簡體   English   中英

為什么客戶端程序通過Java TCP套接字從服務器獲取不正確的數據?

[英]Why client program gets incorrect data from server through Java TCP-socket?

我正在嘗試使用套接字和基本圖形庫(Swing / AWT)用Java實現一個簡單的多人游戲。 游戲的基本思想是,玩家可以使用箭頭鍵在屏幕上移動正方形圖像,並看到其他玩家同時移動正方形。

我編寫了服務器和客戶端程序的代碼,到目前為止,我僅用2個玩家測試了游戲。 當玩家不在同一時間移動時,游戲運行正常,但是當他們同時移動時,會出現以下錯誤:

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

因此,該錯誤可以追溯到我的客戶端程序中的第111行,在該行中,我嘗試根據ID為數組元素設置新值。 出於某種原因,我從服務器獲取的ID是這個隨機大數字,因此設置操作失敗,從而導致此錯誤。 我在這里不明白的是,由於目前可以提供給客戶端的最大ID是7,所以服務器怎么可能給我這么大的ID? 我的意思是,它突然從哪里得到這么多的數字? 應用程序邏輯是否有問題,或者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;
    }
}

現在開始,在執行任何其他操作之前,請先閱讀網絡教程

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

兩個流共享同一個流? 這不是一個好主意。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM