objectOutputStream 的 readObject() 方法没有接收到写入到 Client 的 inputStream 上的对象?

问题描述

目的是将坐标从一个客户端(正在绘制)传输到所有连接到服务器的客户端,然后在各自的面板上绘制,但客户端中的 readobject() 似乎从未获取数据。输入确实正确发送到服务器,但来自服务器,其他客户端无法收听。

当使用 BufferedStream 或 DataInput/OutputStream any Other 时,输入似乎滞后或行为不端。所以更喜欢ObjectOutputStream。 Snapshot of the Program.

客户端在后台线程 listenData 中监听输入。

客户端代码 -

公共类客户端扩展 JFrame {

private static final long serialVersionUID = 1L;
private Coordinates crdntsToSend;
private Coordinates crdntsReceived;
private JPanel contentPane;
private Socket socket;
private ObjectOutputStream toServerPipe;
private ObjectInputStream fromServerPipe;
private JPanel paintPanel;

public Clients(String name,int port,String ip) {
    setResizable(false);
    setTitle("Skribbl");
    setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE);
    setSize(600,550);
    createConnection(ip,port);
    listen();
}

private void createConnection(String ip,int port)
{
    try {
        socket = new Socket(ip,port);
        toServerPipe = new ObjectOutputStream(socket.getoutputStream());
        fromServerPipe = new ObjectInputStream(socket.getInputStream());
        toServerPipe.flush();
        userInterfaceSetter();
    } catch (IOException e) {
        System.out.println("Port Not Found!!!");
        return;
    }
}

private void userInterfaceSetter()
{
    try {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    } catch (Exception e1) {
        e1.printstacktrace();
    } 
    getContentPane().setLayout(null);
    contentPane = new JPanel();
    contentPane.setBorder(new EmptyBorder(5,5,5));
    setContentPane(contentPane);
    GridBagLayout gbl_contentPane = new GridBagLayout();
    gbl_contentPane.columnWidths = new int[] {0,0};
    gbl_contentPane.rowHeights = new int[]{0,0};
    gbl_contentPane.columnWeights = new double[]{1.0,Double.MIN_VALUE};
    gbl_contentPane.rowWeights = new double[]{1.0,0.0,Double.MIN_VALUE};
    contentPane.setLayout(gbl_contentPane);
    
    paintPanel = new JPanel();
    paintPanel.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) {
            drawOnPanel(e.getX(),e.getY());
        }
    });
    paintPanel.addMouseMotionListener(new MouseMotionAdapter() {
        @Override
        public void mouseDragged(MouseEvent e) {
            drawOnPanel(e.getX(),e.getY());
        }
    });
    paintPanel.setBackground(new Color(175,238,238));
    GridBagConstraints gbcPaintPanel = new GridBagConstraints();
    gbcPaintPanel.insets = new Insets(10,10,10);
    gbcPaintPanel.fill = GridBagConstraints.BOTH;
    gbcPaintPanel.gridx = 0;
    gbcPaintPanel.gridy = 0;

    contentPane.add(paintPanel,gbcPaintPanel);
    
    JLabel lblscore = new JLabel("score: ");
    GridBagConstraints gbclblscore = new GridBagConstraints();
    gbclblscore.insets = new Insets(0,0);
    gbclblscore.anchor = GridBagConstraints.WEST;
    gbclblscore.gridx = 0;
    gbclblscore.gridy = 1;
    contentPane.add(lblscore,gbclblscore);
}

private void drawOnPanel(int x,int y)
{
    Graphics g = paintPanel.getGraphics();
    g.filloval(x,y,7,7);
    sendData(x,y);
}

private void sendData(int x,int y) {
    try {
        crdntsToSend = new Coordinates(x,y);
        toServerPipe.writeObject(crdntsToSend);
        toServerPipe.flush();
    } catch (IOException e) {
        e.printstacktrace();
    }
}
private void listen()
{
    SwingWorker<Void,String> listenData = new SwingWorker<Void,String>() {
        boolean go = true;
        String[] receivedData;
        String str;
        @Override
        protected Void doInBackground() throws Exception {
            while(go)
            {
                crdntsReceived = (Coordinates) fromServerPipe.readobject();
                str= crdntsReceived.toString();
                publish(str);
            }
            return null;
        }

        @Override
        protected void process(List<String> chunks) {
             receivedData = str.split(" ");
             drawOnPanel(Integer.parseInt(receivedData[0]),Integer.parseInt(receivedData[1]));
        }
        
    };
    listenData.execute();
}

}

ServerHandler 代码(服务器线程)-

package theskribbl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.socket;

public class HandleClient implements Runnable 
{
  private Socket[] socket;
  private ObjectInputStream fromClients;
  private ObjectOutputStream toClients;
  private static int count = 0;
  private int temp;
  public HandleClient(Socket[] socket,int count)
  {
  HandleClient.count = count;
  temp=count;
  this.socket=socket;
  try 
  {
      fromClients = new ObjectInputStream(socket[count].getInputStream());
      toClients = new ObjectOutputStream(socket[count].getoutputStream());
      toClients.flush();
  } 
  catch (IOException e) 
  {
        e.printstacktrace();
   }
   }
    @Override
    public void run() 
    {
    int i;
    Coordinates coordinate;
    while(true) 
    {
        i=0;
        try 
    {
          coordinate = (Coordinates) fromClients.readobject();
          System.out.println(coordinate.toString());
          while(i<=count) 
    {
              if(i!=temp)
              {
                    toClients = new ObjectOutputStream(socket[i].getoutputStream());
                    toClients.writeObject(coordinate);
                    toClients.flush();
              }
              i++;
          }
        }
        catch (Exception e) 
    {
            System.out.println("Something Went Wrong");
            return;
        }
    } 
    } 
    }

客户代码 - https://repl.it/@GirirajSingh/skribbl#Clients.java

登录客户端的代码 - https://repl.it/@GirirajSingh/skribbl#Login.java

处理客户端(服务器端)的代码 - https://repl.it/@GirirajSingh/skribbl#HandleClient.java

服务器代码 - https://repl.it/@GirirajSingh/skribbl#ServerClass.java

解决方法

ObjectOutputStreamObjectInputStream 构造函数调用必须匹配 - 创建 ObjectOutputStream 时,它会发送一个标头,在创建 ObjectInputStream 时必须接收该标头。

我没有时间研究代码,但看起来每次服务器发送内容时,输出流都是在循环中构建的。客户每次收到坐标时都会重新打开输入吗?

查看 ObjectOutputStreamObjectInputStream 的文档以获得更好的解释。最好从一个(非常)简单的原型开始:一个服务器,一个客户端,然后只发送几个(测试 = 固定)坐标。


doInBackGround() 可能抛出了一个异常(在 SwingWorker 内)!建议通过在 get() 终止后(在其覆盖的 SwingWorker 内)调用 done() 方法来检查这一点 - 如果计算抛出异常,这将抛出一个 ExecutionException ,其中将包含有关异常的信息。

SwingWorker 中添加一些类似如下的代码:

@Override
protected void done() {
    try {
        get();
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

注意:这段代码不是为了解决问题,只是为了显示一个可能的错误

,

您所说的标头不匹配,实际上它们是匹配的,因为它们是通过同一个 Socket 初始化的。 同样的过程使用 DataOutputStream/DataInputStream(write()/read() ) 工作,但在这个过程中数据正在泄漏,因为如果鼠标快速拖动,数据会在两者之间丢失。所以这就是我想使用 ObjectOutputStream 的 read/writeObject() 的原因。