Java套接字-从线程更新实例变量列表
我有一个简单的XO游戏,其中有多个客户端相互玩XO国际象棋。我建立了一个服务器,它可以与连接的每个客户端进行通信。(当客户机连接到服务器时,我创建一个新类(实现Runnable)处理该套接字,然后启动线程…我有一个列表存储外部类中的所有客户机)当客户机想要启动游戏时,他为自己创建一个新服务器,并将房间信息(他的姓名、IP地址、端口…)发送到服务器。在服务器中,我有一个列表,它将该房间添加到列表中,并将其发送给其他客户端Java套接字-从线程更新实例变量列表,java,sockets,Java,Sockets,我有一个简单的XO游戏,其中有多个客户端相互玩XO国际象棋。我建立了一个服务器,它可以与连接的每个客户端进行通信。(当客户机连接到服务器时,我创建一个新类(实现Runnable)处理该套接字,然后启动线程…我有一个列表存储外部类中的所有客户机)当客户机想要启动游戏时,他为自己创建一个新服务器,并将房间信息(他的姓名、IP地址、端口…)发送到服务器。在服务器中,我有一个列表,它将该房间添加到列表中,并将其发送给其他客户端 public class ServerController { pr
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方法。哇,非常感谢您,我已经花了好几天时间阅读了关于线程安全、并发性等的内容。希望我能投票支持您。