【问题标题】:Send and receive serialize object on UDP在 UDP 上发送和接收序列化对象
【发布时间】:2014-06-13 02:24:25
【问题描述】:

我正在尝试使用 UDP 将序列化对象从服务器进程发送到 Java 中的客户端进程。问题是客户端在接收方法上被阻止。有人可以帮忙吗?!

这里是发送对象的服务器代码:

  ClientModel C1= new ClientModel(100,"Noor","Noor",38,38,"asd");
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  ObjectOutputStream oos = new ObjectOutputStream(baos);
  oos.writeObject(C1);
  oos.flush();
  byte[] Buf= baos.toByteArray();
  packet = new DatagramPacket(Buf, Buf.length, client, port);
  socket.send(packet);

这里是接收对象的客户端代码:

byte[] buffer = new byte[100000];
packet = new DatagramPacket(buffer, buffer.length );
socket.receive(packet);
System.out.println("packet received");

我只想接收能够重构的对象,但我无法接收数据包本身。

【问题讨论】:

  • 也许你需要刷新缓冲区...

标签: java serialization udp


【解决方案1】:

我不知道你最后想要完成什么,但是使用 UDP 并不是那么容易......主要原因是在 DatagramPacket 对象的描述中:

数据报包用于实现 无连接的数据包传递 服务。每条消息都是从 仅基于一台机器到另一台机器 关于其中包含的信息 包。发送的多个数据包 一台机器到另一台机器可能被路由 不同,并且可能到达任何 命令。包邮不 保证。

使用 udp 的一个很好的教程是 http://download.oracle.com/javase/tutorial/networking/datagrams/clientServer.html

关于您的屏蔽:

从这里接收一个数据报包 插座。当此方法返回时, DatagramPacket 的缓冲区被填满 收到的数据。数据报包 还包含发件人的 IP 地址, 和发件人的端口号 机器。

此方法阻塞,直到数据报 已收到。长度字段 数据报包对象包含 接收消息的长度。如果 消息比数据包的长 长度,消息被截断。

我没有真正测试它,但我很确定 - 根据描述 - datagramsocket.reseive 函数将阻塞直到数据包被填充(在你的情况下直到接收到 100000 个字节)。

我建议您从具有固定已知长度的数据报包开始,您可以在其中传输实际有效负载的大小。比如:

public static void main(String[] args) {
    ClientModel c1 = new ClientModel ();
    c1.data = 123;
    c1.name = "test";

    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(baos);
      oos.writeObject(c1);
      oos.flush();
      // get the byte array of the object
      byte[] Buf= baos.toByteArray();

      int number = Buf.length;;
      byte[] data = new byte[4];

      // int -> byte[]
      for (int i = 0; i < 4; ++i) {
          int shift = i << 3; // i * 8
          data[3-i] = (byte)((number & (0xff << shift)) >>> shift);
      }

      DatagramSocket socket = new DatagramSocket(1233);
      InetAddress client = InetAddress.getByName("localhost");
      DatagramPacket packet = new DatagramPacket(data, 4, client, 1234);
      socket.send(packet);

      // now send the payload
      packet = new DatagramPacket(Buf, Buf.length, client, 1234);
      socket.send(packet);

      System.out.println("DONE SENDING");
    } catch(Exception e) {
        e.printStackTrace();
    }
}

另一方面,您现在知道自己的尺码了:

public static void main(String[] args) {
    try {
      DatagramSocket socket = new DatagramSocket(1234);

      byte[] data = new byte[4];
      DatagramPacket packet = new DatagramPacket(data, data.length );
      socket.receive(packet);

      int len = 0;
      // byte[] -> int
      for (int i = 0; i < 4; ++i) {
          len |= (data[3-i] & 0xff) << (i << 3);
      }

      // now we know the length of the payload
      byte[] buffer = new byte[len];
      packet = new DatagramPacket(buffer, buffer.length );
      socket.receive(packet);

        ByteArrayInputStream baos = new ByteArrayInputStream(buffer);
      ObjectInputStream oos = new ObjectInputStream(baos);
      ClientModel c1 = (ClientModel)oos.readObject();
      c1.print();
    } catch(Exception e) {
        e.printStackTrace();
    }
}

我使用的 CientModel 类:

public class ClientModel implements Serializable{
    private static final long serialVersionUID = -4507489610617393544L;

    String name = "";
    int data = 1;

    void print() {
        System.out.println(data +": " + name);
    }
}

我测试了这段代码,它工作得很好。希望对您有所帮助(我从http://www.tutorials.de/java/228129-konvertierung-von-integer-byte-array.html 得到了字节到整数和周围)

编辑:正如 cmets 中所述,使用 UDP 通常是一个非常糟糕的主意,主要是因为您不知道您的数据包是否以正确的顺序接收,甚至根本不知道。 UDP 不保证这一点。我没有做太多的 udp 编程,但你可以依赖的唯一部分(如果我理解正确的话)是,如果你得到一个数据包并且它适合数据报(65,527 字节 - 见https://en.wikipedia.org/wiki/User_Datagram_Protocol)它将包含整个东西。因此,如果您不关心消息的发送顺序并且您的对象适合数据报,那么您应该没问题。

Edit2:至于代码:不要按原样使用。这只是一个例子,在 UDP 中,你应该只有一种类型的数据包,并且这个数据包的大小是已知的。这样你就不需要发送“大小”。如果您使用如上所示的代码,并且丢弃了一个数据包,则下一个数据包的大小将错误(即第一个数据包被丢弃,突然您正在检查有效负载的第一个字节以获取大小)。

【讨论】:

  • 我也没有经过测试,但是从 C recvfrom(2) 的描述中,很明显,只要读取整个数据报并返回实际读取字节数,从 UDP 连接读取就可以返回(如果是 JDK,它将位于 DatagramPacket.length 字段中)。我建议编写简单的 Java echo 客户端/服务器来测试行为。
  • 数据报可能无序到达或根本不到达。您可能必须确认初始数据包以确认下一个预期大小,然后确认有效负载数据包
  • 所以你在发送实际有效载荷之前发送长度?这将如何改变这种情况?两个源代码摘录中用于传输有效负载的语法保持不变。我很困惑
  • 当然!您的实现的唯一区别在于接收端。您使用字节输入流反序列化接收到的对象。这似乎也对我有用,并且是正确的做事方式。谢谢!
  • 使用 UDP 是个好主意,但这取决于您的应用程序需求。如果您需要广播该对象,那么,要走的路是 UDP,因为 TCP 不支持广播
【解决方案2】:

我并没有真正测试它,但我很确定 - 基于 描述 - datagramsocket.reseive 函数将阻塞 直到数据包被填满(在你的情况下,直到 100000 字节 收到)。

这是错误的。接收函数将阻塞,直到接收到数据报,该数据报可能小于缓冲区大小(通常会如此)。方法 packet.getLength() 会告诉你它有多大。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-29
    • 1970-01-01
    • 2016-02-02
    • 1970-01-01
    • 1970-01-01
    • 2011-11-22
    • 1970-01-01
    相关资源
    最近更新 更多