通过TCP/IP接收一个对象

4

我要编写一个基于TCP/IP的程序,我需要通过客户端或服务器发送对象。当我想要发送或接收字符串时,一切都正常,但是当我尝试读取一个对象时出现了问题:

private Socket client;

public ThreadedClient(Socket client) {
    this.client = client;
}

@Override
public void run() {
        try {
            ObjectInputStream objIn = new ObjectInputStream(client.getInputStream());
            while(true){
                try {
                    Object fromClient = objIn.readObject();

                } catch (ClassNotFoundException e) {e.printStackTrace();}
            }
        } catch (IOException e) {e.printStackTrace();}
    }

我收到了一个异常:
java.io.StreamCorruptedException: invalid stream header: 306E6165
    at java.io.ObjectInputStream.readStreamHeader(Unknown Source)
    at java.io.ObjectInputStream.<init>(Unknown Source)
    at org.bihe.serverSocket.ThreadedClient.run(Server.java:137)
    at java.lang.Thread.run(Unknown Source)

它指的是这一行:

    ObjectInputStream objIn = new ObjectInputStream(client.getInputStream());

这是我的服务器代码:

            ServerSocket ss = new ServerSocket(8800);
            while(true){
                Socket newClient = ss.accept();

                System.out.println(">>>> Client number " + (++counter) + " connected.");
                OutputStream outputStream = newClient.getOutputStream();
                PrintWriter sender = new PrintWriter(outputStream);
                sender.println(true);
                sender.flush();
                ThreadedClient client = new ThreadedClient(newClient);
                clients.add(client);
                new Thread(client).start();

客户端代码:

sc = new Socket("127.0.0.1", 8800);
            InputStream inputStream = sc.getInputStream();
            Scanner scanner = new Scanner(inputStream);
            boolean s = scanner.nextBoolean();
            if(s){
                System.out.println("Client connected successfully.");
                return true;
            }else{
                System.out.println("Ohhh, Some problem happened, try again later!");
            }

有人可以解释一下发生了什么,这个异常是什么以及为什么我收到了这个异常吗?


3
展示发射器代码。 - inquisitive
我加了一些来自我的项目的代码,你还需要更多吗? - Naeem Baghi
3个回答

5
如果想要在网络上传输对象,您必须对对象进行序列化。
请参考以下问题: 如何在Java中通过TCP发送对象
Java 序列化:Serialization 序列化
您可以按照以下方式实现:
import java.net.*;  
import java.io.*;  

class testobject implements Serializable {  
  int value;  
  String id;  

  public  testobject(int v, String s ){  
       this.value=v;  
       this.id=s;  
    }  

}  

public class SimpleServer  {  
  public static void main(String args[]) {  
  int port = 2002;  
  try {  
    ServerSocket ss = new ServerSocket(port);  
    Socket s = ss.accept();  
    InputStream is = s.getInputStream();  
    ObjectInputStream ois = new ObjectInputStream(is);  
    testobject to = (testobject)ois.readObject();  
    if (to!=null){System.out.println(to.id);}  
    System.out.println((String)ois.readObject());  
    is.close();  
    s.close();  
    ss.close();  
}catch(Exception e){System.out.println(e);}  
}  
}  

import java.net.*;  
import java.io.*;  
   public class SimpleClient {  
   public static void main(String args[]){  
     try{  
     Socket s = new Socket("localhost",2002);  
     OutputStream os = s.getOutputStream();  
     ObjectOutputStream oos = new ObjectOutputStream(os);  
     testobject to = new testobject(1,"object from client");  
     oos.writeObject(to);  
     oos.writeObject(new String("another object from the client"));  
     oos.close();  
     os.close();  
     s.close();  
   }catch(Exception e){System.out.println(e);}  
  }  
}  

这里没有证据表明被发送的对象不可序列化。 - user207421
1
我想发送一个实现了Serializable并且有serialversionUID的客户端,有时我想向服务器发送一个字符串,但它不会在ObjectInputStream objIn = new ObjectInputStream(client.getInputStream());之后执行。我认为这与Serializable无关,我理解你的代码,但我不明白为什么会收到这个异常。 - Naeem Baghi
@AttilaBujáki,你没有回答我的问题,但是你的代码很有帮助,我感谢你的时间。 - Naeem Baghi

3

去掉发送和接收布尔值,这是多余的。如果创建连接时出现问题,套接字将不会被创建:而是抛出异常。你正在混淆使用同一套接字上的多个流。不要这样做。

在您的read-object循环中,您需要单独捕获EOFException,并在获取它时关闭套接字并退出循环。如果遇到任何其他IOException,请记录它,关闭套接字,并退出循环。


我找到了我的问题,你是对的,我删除了不必要的代码,所以我理解了问题。这是因为我试图通过printwriter发送一个字符串,但当我改变代码后,我尝试通过objectInputStream接收该字符串,导致异常,我将printwriter更改为objectOutputStream,问题得到解决,谢谢大家,但我不明白为什么你们说当我遇到EOFException时应该关闭循环,我有一个类似Twitter的项目,并且我处于起始点。当客户端运行时,我应该发送和接收对象,为什么我要关闭套接字? - Naeem Baghi
1
  1. 不是因为你在同一个套接字上使用了多个流,而是因为它只适用于没有缓冲的流...但这仍然不是一个好主意。
  2. 当你收到EOFException时,意味着对等方已关闭连接。没有更多的数据了。永远不会再有任何数据了。如果你继续读取,你只会不断地得到EOFException。不要这样做。跳出循环。
- user207421
第一部分,我应该怎么做?您说通过ArrayList<Object>和ObjectOutputStream发送我想要的所有内容不是一个好主意,然后我捕获ArrayList的第一个成员(它是一个字符串),并通过其值将第二个成员(可能是客户端或字符串)发送到服务器端执行某些操作?您有更好的想法吗?第二部分:我明白了,谢谢,这意味着客户端已注销,所以我应该关闭套接字,对吗? - Naeem Baghi
1
  1. 我说了什么就是什么。不要发送和接收布尔值。不要使用ScannerPrintWriter。通过单个流完成所有操作。
  2. 我多次提到过关闭套接字。
- user207421

1
如果你想要获得良好的性能并发送对象,那么你应该使用Google Protobuf。它允许你在简单的.proto文件中定义消息。然后你可以使用捆绑的编译器生成Java类,这些类将被序列化和发送。
另一个更好的选择是使用Netty而不是普通的Java sockets。这可以避免你编写大量样板代码,并定义简单的序列化/反序列化管道。请查看user-guide

不回答问题。OP没有表明他愿意改变技术。 - user207421
因为他可能不知道任何替代方案。我的解决方案更容易实现,性能也更好。 - Jakub Kubrynski
OP并没有表明他正在寻找替代方案。你的解决方案实际上并没有解决问题。这里没有证据表明你甚至知道问题是什么。 - user207421
我不知道你指的是哪个“消息”,但如果你想知道问题是什么,请看我的回答。 - user207421
好的 - 现在我看到你改变了你的评论。 - Jakub Kubrynski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接