Java 在简单的聊天程序中,服务器发送字符串数组列表,但客户端接收旧值
我想用一台服务器和多个客户端创建一个简单的游戏。服务器将有几个哈希映射和数组列表。服务器将向客户端广播这些值,然后客户端可以逐个修改这些值并发送回服务器,然后服务器将向所有客户端广播更新的值 首先,我创建了服务器-客户端聊天应用程序。当客户端向服务器发送字符串消息时,服务器会将该字符串消息添加到其Arraylist中,并将该Arraylist广播给所有客户端。我使用了线程,这样多个客户端可以同时发送消息,但我还没有应用线程安全 让我们来讨论这个问题。当客户机第一次向服务器发送字符串时,服务器会很好地打印它,添加到它的arraylist中,然后将它广播到所有客户机,所有客户机也可以看到它。但下一次当客户端发送字符串消息时,服务器会接受它,添加到arraylist并广播它,但这次所有客户端都会得到旧的arraylist(只有一个首先添加的字符串的列表)。广播之前我已经打印了arraylist,它显示修改后的值,但在客户端,它只显示一个条目的列表 服务器代码的一部分Java 在简单的聊天程序中,服务器发送字符串数组列表,但客户端接收旧值,java,multithreading,sockets,client-server,Java,Multithreading,Sockets,Client Server,我想用一台服务器和多个客户端创建一个简单的游戏。服务器将有几个哈希映射和数组列表。服务器将向客户端广播这些值,然后客户端可以逐个修改这些值并发送回服务器,然后服务器将向所有客户端广播更新的值 首先,我创建了服务器-客户端聊天应用程序。当客户端向服务器发送字符串消息时,服务器会将该字符串消息添加到其Arraylist中,并将该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();
}
}
公共类ServerGUI扩展了javax.swing.JFrame{
公共静态最终int服务器_端口=4000;
专用服务器;
ArrayList al;
ArrayList客户;
公共服务器GUI(){
初始化组件();
setVisible(真);
al=新的ArrayList();
clients=newarraylist();
initNet();
}
私有void initNet(){
套接字ds=null;
试一试{
ss=新的服务器套接字(服务器端口,1);
while(true){
ds=ss.accept();
添加(新ClientHandler(ds));
}
}捕获(例外e){
System.out.println(“关闭服务器…”);
}
}
类ClientHandler扩展线程{
专用插座ds;
私有对象输出流输出;
私有对象输入流;
公共ClientHandler(套接字ds)引发异常{
这是1.ds=ds;
out=newObjectOutputStream(ds.getOutputStream());
in=newObjectInputStream(ds.getInputStream());
start();
}
public ObjectOutputStream getOut(){
返回;
}
公开募捐{
试一试{
while(true){
接受数据(in);
广播数据();
}
}捕获(例外e){
e、 printStackTrace();
}最后{
System.out.println(“最终调用.套接字关闭”);
如果(ds!=null){
试一试{
ds.close();
}捕获(IOE异常){
e、 printStackTrace();
}
}
}
}
}
私有void acceptData(ObjectInputStream in)引发异常{
System.out.println(“由“+Thread.currentThread().getName()调用的acceptData”);
.readObject()中的字符串s=(字符串);
等。添加(s);
jta.setText(al.toString());
}
私有void broadcastData()引发异常{
System.out.println(“由“+Thread.currentThread().getName()调用的广播”);
System.out.println(“al为:\n”+al);
用于(ClientHandler clnt:客户){
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();
}
}
公共类ClientGUI扩展了javax.swing.JFrame{
公共静态最终int服务器_端口=4000;
公共静态最终字符串服务器\u IP=“127.0.0.1”;
专用插座s1;
私有对象输出流输出;
私有对象输入流;
私人ArrayList al;
公共客户端GUI(){
初始化组件();
setVisible(真);
initNet();
}
私有void initNet(){
试一试{
s1=新套接字(服务器IP、服务器端口);
out=newObjectOutputStream(s1.getOutputStream());
in=newObjectInputStream(s1.getInputStream());
System.out.println(“连接到服务器”);
新的ReadData();
}捕获(例外e){
e、 printStackTrace();
}
}
类ReadData扩展线程{
公共读取数据(){
start();
}
公开募捐{
System.out.println(“客户端线程已启动”);
试一试{
while(true){
.readObject()中的al=(ArrayList);
System.out.println(“客户端读取完成,al为”+al);
jta.setText(al.toString());
}
}捕获(例外e){
e、 printStackTrace();
}
}
}
private void textFieldActionPerformed(java.awt.event.ActionEvent evt){
试一试{
out.writeObject(jtf.getText());
out.flush();
}捕获(例外e){
e、 printStackTrace();
}
}
这是正常行为。如果发送相同的对象(您的ArrayList)多次发送到给定的ObjectOutputStream
,流将在第一次发送完整的对象,并且在下一次只发送对此对象的引用。这就是允许发送对象图而不消耗太多带宽,也不进入无限循环的原因,因为a
引用b
which还引用了a
要确保再次发送ArrayList,需要调用ObjectOutputStream