Java套接字-从线程更新实例变量列表

Java套接字-从线程更新实例变量列表,java,sockets,Java,Sockets,我有一个简单的XO游戏,其中有多个客户端相互玩XO国际象棋。我建立了一个服务器,它可以与连接的每个客户端进行通信。(当客户机连接到服务器时,我创建一个新类(实现Runnable)处理该套接字,然后启动线程…我有一个列表存储外部类中的所有客户机)当客户机想要启动游戏时,他为自己创建一个新服务器,并将房间信息(他的姓名、IP地址、端口…)发送到服务器。在服务器中,我有一个列表,它将该房间添加到列表中,并将其发送给其他客户端 public class ServerController { pr

我有一个简单的XO游戏,其中有多个客户端相互玩XO国际象棋。我建立了一个服务器,它可以与连接的每个客户端进行通信。(当客户机连接到服务器时,我创建一个新类(实现Runnable)处理该套接字,然后启动线程…我有一个列表存储外部类中的所有客户机)当客户机想要启动游戏时,他为自己创建一个新服务器,并将房间信息(他的姓名、IP地址、端口…)发送到服务器。在服务器中,我有一个列表,它将该房间添加到列表中,并将其发送给其他客户端

    public class ServerController {
private ServerView view;
private ServerSocket myServer;
private int serverPort = 8881;
private List<ExchangeData> list;
private volatile List<Room> rooms;


public ServerController(ServerView view) {
    this.view = view;
    openServer(serverPort);
    view.showMessage("TCP server is running...");
    list = new ArrayList<ExchangeData>();
    rooms = new ArrayList<Room>();
    while (true) {
        listening();
    }

}

private void openServer(int portNumber) {
    ....
}

private void listening() {

    try {
        Socket clientSocket = myServer.accept();

        ExchangeData ex = new ExchangeData(clientSocket);
        Thread thread = new Thread(ex);
        thread.start();
        list.add(ex);

    } catch (IOException e) {
        System.out.print(e.toString());
    }
}

class ExchangeData implements Runnable {
    private Socket clientSocket;
    private ObjectOutputStream oos;
    private ObjectInputStream ois;
    private boolean run = true;

    public ExchangeData(Socket socket) {
        try {
            clientSocket = socket;
            oos = new ObjectOutputStream(clientSocket.getOutputStream());
            ois = new ObjectInputStream(clientSocket.getInputStream());
        } catch (Exception ex) {
            System.out.println(ex.toString());
        }


    }

    public void closeConnection() {
        .....
    }



    public void sendData(Object o) {
        try {

            oos.writeObject(o);
        } catch (Exception ex) {
            view.showMessage(ex.toString());
        }
    }

    public void run() {
        try {

            while (run) {
                Object data = ois.readObject();
                if (data instanceof Object[]) {
                    Object[] o = (Object[]) data;
                    String s = o[0].toString();

                     if (s.equalsIgnoreCase("Open Room")) {

                        Room r = (Room) o[1];
                        String username = r.getUsername();
                        InetSocketAddress isa = r.getIsa();
                        **rooms.add(r);**
                        List<Room> l = new ArrayList<Room>(rooms);

                        for (ExchangeData ex : list) {
                            ex.sendData(username + " Open A room at : "
                                    + isa);
                            **ex.sendData(rooms);**

                        }

                    } else if (s.equalsIgnoreCase("Close Room")) {
                        Room room = (Room) o[1];
                        rooms.remove(room);
                        List<Room> l = new ArrayList<Room>(rooms);
                        for (ExchangeData ex : list) {
                            ex.sendData(l);
                        }
                    } 

                } else if (data instanceof String) {
                    String s = data.toString();
                    if (s.equalsIgnoreCase("Quit")) {
                        stopThread();
                        list.remove(this);
                        closeConnection();

                    } 
                }

            }

        } catch (Exception ex) {
            try {
                stopThread();
                list.remove(this);
            } catch (ExceptionInInitializerError e) {
                e.printStackTrace();

            }
        }
    }
}
公共类服务器控制器{
私有服务器视图;
私有服务器套接字myServer;
专用int服务器端口=8881;
私人名单;
私人名单室;
公共服务器控制器(服务器视图){
this.view=视图;
openServer(服务器端口);
showMessage(“TCP服务器正在运行…”);
列表=新的ArrayList();
房间=新的阵列列表();
while(true){
倾听();
}
}
私有void openServer(int端口号){
....
}
私人监听{
试一试{
套接字clientSocket=myServer.accept();
ExchangeData ex=新的ExchangeData(客户端套接字);
螺纹=新螺纹(ex);
thread.start();
列表。添加(ex);
}捕获(IOE异常){
System.out.print(例如toString());
}
}
类ExchangeData实现可运行{
私有套接字clientSocket;
私有对象输出流oos;
私有对象输出ois;
私有布尔运行=true;
公共交换数据(插座){
试一试{
clientSocket=socket;
oos=newObjectOutputStream(clientSocket.getOutputStream());
ois=newObjectInputStream(clientSocket.getInputStream());
}捕获(例外情况除外){
System.out.println(例如toString());
}
}
公共连接(){
.....
}
公共void sendData(对象o){
试一试{
oos.writeObject(o);
}捕获(例外情况除外){
view.showMessage(例如toString());
}
}
公开募捐{
试一试{
while(运行){
对象数据=ois.readObject();
if(对象[]的数据实例){
对象[]o=(对象[])数据;
字符串s=o[0]。toString();
如果(s.equalsIgnoreCase(“开放式房间”)){
房间r=(房间)o[1];
字符串username=r.getUsername();
InetSocketAddress isa=r.getIsa();
**加上(r)**
列表l=新阵列列表(房间);
用于(交换数据交换:列表){
ex.sendData(用户名+“在以下位置打开文件室:”
+isa);
**例如发送数据(房间)**
}
}否则,如果(s.equalsIgnoreCase(“封闭室”)){
房间=(房间)o[1];
房间。移除(房间);
列表l=新阵列列表(房间);
用于(交换数据交换:列表){
例如sendData(l);
}
} 
}else if(字符串的数据实例){
字符串s=data.toString();
如果(s.equalsIgnoreCase(“退出”)){
止动螺纹();
列表。删除(此);
closeConnection();
} 
}
}
}捕获(例外情况除外){
试一试{
止动螺纹();
列表。删除(此);
}捕获(初始化错误除外){
e、 printStackTrace();
}
}
}
}
}

问题是当我执行rooms.add(r)时,其他线程似乎看不到“更新”。例如: 1) 第一个玩家打开一个房间-房间大小为1; 2) 第二个播放器打开一个新的文件室-文件室大小现在是2(我尝试在方法sendData(对象o)的旁边添加一些代码行,它打印文件室的大小,所有文件室的大小都是2。但是当执行ObjectoutputStream.write(o)行时,它实际上会写入线程(第一个播放器)之前保存的文件室值,即1。
如果我像“Closing Room”一样创建新列表,如list l=new ArrayList(rooms),然后发送此文件,它会起作用。我不明白为什么?请有人向我解释一下。对不起,我的英语不好:(.

当您通过给定的ObjectOutputStream多次发送同一对象时,该流第一次写入完整的对象状态,但之后只向该对象发送一些引用

这允许发送具有循环引用的对象的复杂图形,而不会消耗太多带宽,也不会进入无限循环(发送引用B的A,发送引用A的B,发送引用B的A,等等)


因此,如果您想发送列表的新副本,您必须首先使用ObjectOutputStream方法。

哇,非常感谢您,我已经花了好几天时间阅读了关于线程安全、并发性等的内容。希望我能投票支持您。