通过套接字流发送对象

Posted

技术标签:

【中文标题】通过套接字流发送对象【英文标题】:Sending objects through socket streams 【发布时间】:2019-04-21 07:01:35 【问题描述】:

试图编写一个允许客户端相互发送对象的程序。我目前正在使用 ObjectOuptutStream 通过套接字发送它,每当我尝试从对象流中读取或写入对象时,它都会给出异常:java.io.NotSerializableException。我在网上搜索了这个异常,我得到的主要解决方案是在您正在发送或从流中读取的对象上实现 Serializable 接口。我做了,但仍然收到此异常。

这是对象类:

public class Event implements Serializable 

    private static final long serialVersionUID = 1L;

    Integer from;
    Vector<Integer> timestamp;

    public Event(int identifier, Vector<Integer> timestamp) 
        this.from = identifier;
        this.timestamp = timestamp;
    

    int getFromID() 
        return from;
    

    Vector<Integer> getTimestamp() 
        return timestamp;
    


这是客户端类中写入其他套接字的部分

    Random rand = new Random();

    int temp;
    while (eventCount < 100) 
        System.out.println("Generating Event");
        int choice = rand.nextInt(5);
        if (choice == 0) 
            temp = timestamp.get(identifier);
            ++temp;
            timestamp.set(identifier, temp);
         else 
            int randC = rand.nextInt(outputClients.size());
            ClientSocket cc = outputClients.get(randC);
            cc.out.writeObject(new Event(identifier, timestamp));
        
        System.out.println("Done Generating Event");
    

这是读取对象的线程

public class ClientConnection extends Thread 
    Socket socket;
    ObjectOutputStream out;
    ObjectInputStream in;
    Random rand = new Random();
    public ClientConnection(Socket s) 
        this.socket = s;
        try 
            out = new ObjectOutputStream (socket.getOutputStream());
            in = new ObjectInputStream (socket.getInputStream());
         catch (IOException e) 
            e.printStackTrace();
        
    

    // execute the event
    private void executeEvent(int from, Vector<Integer> x) 
        int temp;
        synchronized (timestamp) 
            for (int i = 0; i < timestamp.size(); ++i) 
                if (x.get(i) > timestamp.get(i)) 
                    timestamp.set(i, x.get(i));
                
            
            temp = timestamp.get(from);
            ++temp;
            timestamp.set(from, temp);
        
    

    @Override
    public void run () 
        while (true) 
            System.out.println("Reading events");
            if (!isAlive)  
                break;
            
            try 
                Event event = (Event) in.readObject();
                executeEvent(event.getFromID(), event.getTimestamp());
             catch (ClassNotFoundException e) 

             catch (IOException e) 
                e.printStackTrace();
            
            System.out.println(timestamp);
        
    

这是完整上下文的 Client 类(假设所有适当的包都已导入)

public class Computer 

static final int MAX_SYSTEMS = 2;                   // MAX SYSTEMS
static Vector<Integer> timestamp = new Vector<Integer>();
static int[] timestamp1 = new int[MAX_SYSTEMS];     // Time-stamp
static int identifier;                              // Computer ID
static int eventCount = 0;                          // Event Counts
static boolean isAlive = true;                      // Check if the computer is alive

Socket sockToServer;
PrintWriter outputToServer;
BufferedReader inputFromServer;
String textFromServer;

ServerSocket ss;

static ArrayList<ClientSocket> outputClients = new ArrayList<ClientSocket>();
static ArrayList<ClientConnection> inputClients = new ArrayList<ClientConnection>();

Log log;

public static void main(String[] args) throws IOException 
    new Computer("127.0.0.1", 8000);


public Computer(String hostname, int port) throws IOException 
    // Instantiate server socket
    int socketPort = port + identifier + 1;
    System.out.println(socketPort);
    ss = new ServerSocket(socketPort);

    System.out.println("Server Socket Instantiated");

    // Creating sockets (with streams) to write to stream
    for (int i = 0; i < MAX_SYSTEMS; ++i) 
        if (i != identifier) 
            Socket thing1 = new Socket(hostname, port + i + 1);
            ClientSocket cs = new ClientSocket (thing1);
            outputClients.add(cs);
        
    

    log.write("Client Sockets Instantiated\n");

    // Create threads for reading objects and updating timestamp
    for (int i = 0; i < MAX_SYSTEMS - 1; ++i) 
        ClientConnection clientConn = new ClientConnection(ss.accept());
        clientConn.start();
        inputClients.add(clientConn);
    

    log.write("Server connected to clients");

    Random rand = new Random();

    // Writing Events

    int temp;
    while (eventCount < 100) 
        System.out.println("Generating Event");
        int choice = rand.nextInt(5);
        if (choice == 0) 
            temp = timestamp.get(identifier);
            ++temp;
            timestamp.set(identifier, temp);
         else 
            int randC = rand.nextInt(outputClients.size());
            ClientSocket cc = outputClients.get(randC);
            cc.out.writeObject(new Event(identifier, timestamp));
        
        System.out.println("Done Generating Event");
    

    log.write("Computer finished generating events. Continue listening...\n");

    outputToServer.println("Finish");

    // Wait for Tear Down Message
    while (true) 
        try 
            textFromServer = inputFromServer.readLine();
            if (textFromServer.equals("Tear Down")) 
                isAlive = false;
                break;
            
         catch (IOException e) 
            e.printStackTrace();
        
    

    log.write("Computer shutting off....");

    for (int i = 0; i < outputClients.size(); ++i) 
        ClientSocket sc = outputClients.get(i);
        sc.socket.close();
    

    sockToServer.close();



// client socket class (organizing)
public class ClientSocket 
    Socket socket;
    ObjectOutputStream out;
    ObjectInputStream in;

    public ClientSocket(Socket s) 
        try 
            this.socket = s;
            this.out = new ObjectOutputStream(socket.getOutputStream());
         catch (IOException e) 
            e.printStackTrace();
        
        System.out.println("Client Socket Created");
    


public class Event implements Serializable 

    private static final long serialVersionUID = 1L;

    Integer from;
    Vector<Integer> timestamp;

    public Event(int identifier, Vector<Integer> timestamp) 
        this.from = identifier;
        this.timestamp = timestamp;
    

    int getFromID() 
        return from;
    

    Vector<Integer> getTimestamp() 
        return timestamp;
    



// send event thread
public class ClientConnection extends Thread 
    Socket socket;
    ObjectOutputStream out;
    ObjectInputStream in;
    Random rand = new Random();
    public ClientConnection(Socket s) 
        this.socket = s;
        try 
            out = new ObjectOutputStream (socket.getOutputStream());
            in = new ObjectInputStream (socket.getInputStream());
         catch (IOException e) 
            e.printStackTrace();
        
    

    // execute the event
    private void executeEvent(int from, Vector<Integer> x) 
        int temp;
        synchronized (timestamp) 
            for (int i = 0; i < timestamp.size(); ++i) 
                if (x.get(i) > timestamp.get(i)) 
                    timestamp.set(i, x.get(i));
                
            
            temp = timestamp.get(from);
            ++temp;
            timestamp.set(from, temp);
        
    

    @Override
    public void run () 
        while (true) 
            System.out.println("Reading events");
            if (!isAlive)  
                break;
            
            try 
                Event event = (Event) in.readObject();
                executeEvent(event.getFromID(), event.getTimestamp());
             catch (ClassNotFoundException e) 

             catch (IOException e) 
                e.printStackTrace();
            
            System.out.println(timestamp);
        
    
   

TL;DR 尝试使用 Object(Output/Input)Stream 对象通过套接字读取和写入对象。当我这样做时,我得到了 NotSerializableException,即使我在从流中写入和读取的类中实现了 Serializable 接口。

感谢所有帮助!

(编辑:堆栈跟踪)

java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: timetableexchange.Computer
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
    at java.io.ObjectInputStream.readSerialData(Unknown Source)
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.readObject(Unknown Source)
    at timetableexchange.Computer$ClientConnection.run(Computer.java:239)
Caused by: java.io.NotSerializableException: timetableexchange.Computer
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
    at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
    at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.writeObject(Unknown Source)
    at timetableexchange.Computer.<init>(Computer.java:128)
    at timetableexchange.Computer.main(Computer.java:39)
java.io.NotSerializableException: timetableexchange.Computer
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
    at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
    at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.writeObject(Unknown Source)
    at timetableexchange.Computer.<init>(Computer.java:128)
    at timetableexchange.Computer.main(Computer.java:39)

【问题讨论】:

包括完整的堆栈跟踪。 您是否尝试过实现“readObject”和“writeObject”方法? @StephenC 抱歉。在原始帖子中包含堆栈跟踪。 @JasonArmstrong 我不明白你的意思。我添加了 readobject 和 writeobject 来分别读取和写入对象。你的意思是它们需要通过 Serializable 接口来实现吗? 我没有看到它们在上面的 Event 类中实现。另外,要考虑的另一件事是 Externalizable 接口。 【参考方案1】:

现在您已经提供了堆栈跟踪...。我看到了问题!

您已将Event 创建为Computer 的内部类。这意味着Event 具有到封闭Computer 实例的隐式链接......它将与Event 实例一起序列化。

但是Computer 不是Serializable

一个(可能不正确的)解决方案是让Computer 实现Serializable。但这意味着您将发送一个 Computer 的实例以及每个单独序列化的 Event ...这就是为什么它可能是错误的。

更好的解决方案是将Event 声明为static,以便它引用封闭的Computer。据我所知,它不需要是“内部”类。它可能只是一个“嵌套”类,甚至是***类。

建议:当您使用嵌套类和内部类时,请确保正确缩进它们...以便其他人更容易发现发生了什么。

【讨论】:

你完全正确!太感谢了!这是有道理的,因为事件类是计算机类的一部分,计算机类可能需要实现 Serializable 接口。现在我将 Event Class 移到了***别,它起作用了!非常感谢!

以上是关于通过套接字流发送对象的主要内容,如果未能解决你的问题,请参考以下文章

通过套接字接收数组列表时出现 NullPointerException

如何通过 java 套接字发送 Json 对象?

通过套接字发送 Python 对象

C++ 如何通过套接字发送对象?

通过 ZeroMQ 以字符串形式接收对象然后通过另一个套接字以零副本发送它的正确方法是啥?

为啥我的 powershell 没有读取我通过网络流发送的内容