我想用服务器和多个客户端创建一个简单的游戏。服务器将具有多个Hashmap和Arraylist。服务器将广播这些值给客户端,然后客户端可以逐个修改它们并发送回服务器,然后服务器会将更新后的值广播给所有客户端。
首先,我创建了服务器-客户端聊天应用程序。当客户端向服务器发送String消息时,Server会将该String消息添加到其Arraylist中,并将该arraylist广播给所有客户端。我使用了线程,以便多个客户端可以同时发送消息,但是我还没有应用线程安全。
让我们来解决问题。客户端第一次将String发送到服务器时,服务器会很好地打印它,添加到它的arraylist中,然后将其广播给所有客户端,所有客户端也可以看到。但是下一次客户端发送String消息时,服务器接受它,将其添加到arraylist并广播它,但是这一次所有客户端都获得旧的arraylist(仅添加了一个String的列表被首先添加)。我在广播之前已经打印了arraylist,它显示了修改后的值,但是在客户端,它仅显示了一个条目的列表。
服务器代码的一部分
public class ServerGUI extends javax.swing.JFrame {
public static final int SERVER_PORT = 4000;
private ServerSocket ss;
ArrayList<String> al;
ArrayList<ClientHandler> clients;
public ServerGUI() {
initComponents();
setVisible(true);
al = new ArrayList<>();
clients = new ArrayList<>();
initNet();
}
private void initNet() {
Socket ds = null;
try {
ss = new ServerSocket(SERVER_PORT, 1);
while (true) {
ds = ss.accept();
clients.add(new ClientHandler(ds));
}
} catch (Exception e) {
System.out.println("shutting down server......");
}
}
class ClientHandler extends Thread {
private Socket ds;
private ObjectOutputStream out;
private ObjectInputStream in;
public ClientHandler(Socket ds) throws Exception {
this.ds = ds;
out = new ObjectOutputStream(ds.getOutputStream());
in = new ObjectInputStream(ds.getInputStream());
start();
}
public ObjectOutputStream getOut() {
return out;
}
public void run() {
try {
while (true) {
acceptData(in);
broadcastData();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("Finally called. socket closed");
if (ds != null) {
try {
ds.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
private void acceptData(ObjectInputStream in) throws Exception {
System.out.println("acceptData called by " + Thread.currentThread().getName());
String s = (String) in.readObject();
al.add(s);
jta.setText(al.toString());
}
private void broadcastData() throws Exception {
System.out.println("broadcast called by " + Thread.currentThread().getName());
System.out.println("al is : \n" + al);
for (ClientHandler clnt : clients) {
clnt.getOut().writeObject(al);
clnt.getOut().flush();
}
}
客户代码的一部分
public class ClientGUI extends javax.swing.JFrame {
public static final int SERVER_PORT = 4000;
public static final String SERVER_IP = "127.0.0.1";
private Socket s1;
private ObjectOutputStream out;
private ObjectInputStream in;
private ArrayList<String> al;
public ClientGUI() {
initComponents();
setVisible(true);
initNet();
}
private void initNet() {
try {
s1 = new Socket(SERVER_IP, SERVER_PORT);
out = new ObjectOutputStream(s1.getOutputStream());
in = new ObjectInputStream(s1.getInputStream());
System.out.println("connected to server");
new ReadData();
} catch (Exception e) {
e.printStackTrace();
}
}
class ReadData extends Thread {
public ReadData() {
start();
}
public void run() {
System.out.println("client thread started");
try {
while (true) {
al = (ArrayList<String>) in.readObject();
System.out.println("client read completed, al is "+al);
jta.setText(al.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void textFieldActionPerformed(java.awt.event.ActionEvent evt) {
try {
out.writeObject(jtf.getText());
out.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
这是正常现象。如果您多次将同一个对象(您的ArrayList)发送到给定对象ObjectOutputStream
,则流将在第一次发送完整的对象,而在下一次仅发送对该对象的引用。这样一来,就可以发送对象图而不会占用太多带宽,也不会陷入无限循环,因为a
引用b
也引用a
。
为了确保ArrayList中发送第二次,你需要调用reset()
的ObjectOutputStream
。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句