【问题标题】:Java Sockets - Clients receiving different ObjectsJava Sockets - 接收不同对象的客户端
【发布时间】:2017-08-23 13:57:17
【问题描述】:

所以我想使用 Sockets 用 Ja​​va 制作一个纸牌游戏。我有这个“PlayerState”对象,它保留了所有在大厅或准备玩的玩家。问题是即使服务器为所有客户端发送相同的对象,所有客户端都会收到带有错误数据的不同对象......让我解释一下

所以这是服务器中发送对象的方法:

private void sendToAll(PlayersState playersState){
    resetLists(playersState.getLobbyPlayers(),playersState.getReadyPlayer());
    Iterator it = clientOutputStreams.iterator();

    while (it.hasNext())
        try {
            ObjectOutputStream writer = (ObjectOutputStream) it.next();
            writer.writeObject(playersState);
            writer.flush();
            System.out.println(playersState.getLobbyPlayers());
        } catch (Exception ex) {
        }
}

这是 CLIENT 中接收对象的方法

private void whileChatting() throws IOException {
        sendMessage("&nume"+nume);
        do {
            try {
                Object object = input.readObject();

                if(object instanceof String)
                    showMessage("\n" + (String) object);

                else if(object instanceof PlayersState){
                    System.out.println(((PlayersState) object).getLobbyPlayers());
                    PlayersState actualPlayerState = new PlayersState();
                    actualPlayerState.setReadyPlayer(((PlayersState) object).getReadyPlayer());
                    actualPlayerState.setLobbyPlayers(((PlayersState) object).getLobbyPlayers());
                    resetLists(actualPlayerState.getLobbyPlayers(),actualPlayerState.getReadyPlayer());
                }

            } catch (ClassNotFoundException var2) {
                showMessage("Unknown data received!");
            }
        } while(!message.equals("SERVER - END"));
    }

这是对象(在客户端和服务器中都可用)

package sample;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;


public class PlayersState implements Serializable{
    private static final long serialVersionUID = 7526472295622776147L;
    private List<String> LobbyPlayers;
    private List<String> ReadyPlayer;

    public PlayersState(){
        LobbyPlayers = new ArrayList<>();
        ReadyPlayer = new ArrayList<>();
    }

    public void removeFrom(String name, List<String> list){
        if(list.contains(name))
            list.remove(name);
    }

    public void addTo(String name, List<String> list){
        list.add(name);
    }

    public List<String> getLobbyPlayers() {
        return LobbyPlayers;
    }

    public void setLobbyPlayers(List<String> lobbyPlayers) {
        LobbyPlayers = lobbyPlayers;
    }

    public List<String> getReadyPlayer() {
        return ReadyPlayer;
    }

    public void setReadyPlayer(List<String> readyPlayer) {
        ReadyPlayer = readyPlayer;
    }
}

如果我一个接一个地打开 3 个客户端,假设 user1 user2 和 user3 来自 SERVER 的 println 的输出将是:

[user1, user2, user3]
[user1, user2, user3]
[user1, user2, user3]

这里一切正常。但这是客户收到的:

客户端 1(用户 1):

[user1]
[user1]
[user1]

客户端 2(用户 2)

[user1,user2]
[user1,user2]

客户端 3(用户 3)

[user1,user2,user3]

为什么客户端没有收到所有相同的数据?

【问题讨论】:

  • 服务器发送给客户端时playersState是否发生变化。
  • 你这是什么意思?
  • 您是否在服务器中使用多个线程?如果是这样,您如何同步对PlayersState 实例的访问?
  • 是的,我使用多个线程,但这是我的第一个此类项目,我不知道如何同步

标签: java sockets stream


【解决方案1】:

PlayersState 不是一个可变类,所以你应该在多线程同步它。像这样:

tresetLists(playersState.getLobbyPlayers(),playersState.getReadyPlayer());
    Iterator it = clientOutputStreams.iterator();

    byte[] sendBytes=null;
    synchronized (playersState) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(playersState);
        sendBytes = bos.toByteArray();
        oos.close();
    }

    while (it.hasNext())
        try {
            ObjectOutputStream writer = (ObjectOutputStream) it.next();
            writer.write(sendBytes);
            writer.flush();
            System.out.println(playersState.getLobbyPlayers());
        } catch (Exception ex) {
        }

在上面的代码中,为了提高性能和减少锁的持有时间,我们锁定了playersState,并将它串入sendBytes数组一次。 当然,你应该随时同步playersState,比如在其他线程上调用getLobbyPlayersgetReadyPlayer

【讨论】:

  • 我应该在哪里添加锁变量?
  • 我更新了我的答案,不要使用ReentrantLock,只使用synchronized。当你访问PlayersState的同一个实例时,你应该同步它。
  • 现在服务器在 java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2917) 在 java.io.ObjectInputStream.readObject0(ObjectInputStream.java: 1502) 在 java.io.ObjectInputStream.readObject(ObjectInputStream.java:422) 在 sample.Main$newClient.run(Main.java:258) 在 java.lang.Thread.run(Thread.java:748) >
  • PlayerState 绝对是可变的。也许你的意思是写“......不是一个 immutable 类......”另外,我在 OPs 代码中根本没有看到任何线程的证据,所以我不确定如何你已经断定这是一个同步问题。
  • 所以我尝试用一​​个简单的 Map> 改变整个对象,结果是一样的......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-14
  • 1970-01-01
  • 2015-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多