我正在尝试使用套接字和基本图形库(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;
}
}
答案 0 :(得分:4)
现在来吧,在你做其他事情之前先阅读networking tutorial。
out = new DataOutputStream(socket.getOutputStream());
oout = new ObjectOutputStream(socket.getOutputStream());
两个流共享同一个流?不是个好主意。