当前位置: 首页 > 图灵资讯 > 技术篇> Java中通过数据报包输送对象

Java中通过数据报包输送对象

来源:图灵教育
时间:2024-03-01 15:58:15
Java 1.1 其中一个吸引人的特点是新的 ObjectInputStream 和 ObjectOutputStream 这两个类。有了这个新的 API(ObjectOutputStream 类中的 writeObject(Object o) 方法和 ObjectInputStream 类中的 object readObject(),无论对象图有多复杂,你都可以随时获得运行对象的快照。因为这种快照是通过的 ObjectOutputStream 类(OutputStream 提供类的子类),因此您可以很容易地将其包装在其它输出流中,从而实现所需的任何功能(例如 FileOutputStream)。

Java 1.1 这些新类别使得在线传输运行对象成为可能。因此,对象和被引用的对象必须序列化 -- 也就是说,它可以转换为字节流。幸运的是,在 Java 1.1 内部建筑的类别大多是可序列化的。但有些类别是不可序列化的(Object 类是典型的例子)。但别担心。如果你的类继承是不可序列化的,你也可以使用它 ObjectOutputStream 类中的 defaultWriteObject() 该方法可以序列化,然后可以使用 ObjectInputStream 类中的 defaultReadObject() 解除序列化方法。

一旦序列化,对象就可以在网上传输。以下示例显示了生成可序列化对象并通过流套连接发送它的方法:

//对象输出import java.net.*; import java.io.*;///要发送的样品类型:Factoryclass Factory implements Serializable{private void writeObject(ObjectOutputStream out) throws IOException{out.defaultWriteObject();}private void readObject(ObjectInputStream in)throws IOException, ClassNotFoundException{in.defaultReadObject();}}public class ShowObjOutput{public static void main(String[] arg){try{ObjectOutputStream os;Socket sock = new Socket("panda.cs.uno.edu", 6000); //panda 主机名Factory fa = new Factory();os = new ObjectOutputStream( newBufferedOutputStream(sock.getOutputStream()));os.writeObject(fa);}catch (IOException ex){}}}

下一个示例说明了 ObjectInputStream 如何从流套接字接收对象:

//对象输入import java.net.*; import java.io.*;public class ShowObjInput{public static void main(String[] arg){try{ObjectInputStream is;ServerSocket servSock = new ServerSocket(6000);Sock sock;sock = servSock.accept();is = new ObjectInputStream( newBufferedInputStream(sock.getInputStream()));Factory o = (Factory)is.readObject();}catch (IOException ex){}}}

除紧密耦合的套接字外,Java 还提供了 DatagramSocket 类别支持无连接数据报告和通信。能否使用数据报告通信来完成对象的输入/输出?完成这个功能不像使用流套连接那么简单吗?问题在于 DatagramSocket 未连接到任何流量;为实施发送和接收操作,DatagramSocket 以字节数组为参数。

可以想象,为了构建数据报包,对象必须转换为字节数组。这种转换可能很难完成,如果对象涉及到一个复杂的对象图。许多以前发表的文章讨论了实现对象序列化的方法 -- 即将 Java 将对象打包(序列化)成字节流,将字节流解包为 Java 对象。然而,将常规对象图转换为字节数组可能需要编写大量代码,因为对象图可能非常复杂。

那么,如何避免编写复杂的包装代码呢?以下是一种使用数据报告包传输对象而不编写包装代码的方法。

上图显示了使用数据报告传输对象时的数据流。您可以通过以下七个步骤实现这个数据流,它可以传输任何类型的对象,myObject。

第一步。准备:通过实现 Serializable 接口使您的对象(例如) myObject)可序列化。

步骤二。创建 ByteArrayOutputStream 对象,例如,名称 baoStream。

第三步。用 baoStream 构造一个 ObjectOutputStream 对象,比如 ooStream。

步骤4。通过调用 ooStream 的 writeObject() 方法将对象 myObject 写入 baoStream 中。

第五步。使用 baoStream 的 toByteArray() 方法从 baoStream 检索字节数组缓冲区。

第六步。使用第五步检索到的数组缓冲区结构 DatagramPacket,比方说 dPacket。

第七步。通过调用 DatagramSocket 的 send() 方法发送 dPacket。

接收对象,逆序完成上述步骤 ObjectInputStream 代替 ObjectOutputStream,同时用 ByteArrayInputStream 代替 ByteArrayOutputStream。

用套接字编程时,sendTo 是无连接协议中使用的标准函数。为了传输对象,我重写了这个函数。以下代码示例显示了如何 Sender 类中实现 send 方法:

import java.io.*;import java.net.*; public class Sender{public void sendTo(Object o, String hostName, int desPort){try{InetAddress address = InetAddress.getByName(hostName);ByteArrayOutputStream byteStream = newByteArrayOutputStream(5000);ObjectOutputStream os = new ObjectOutputStream(newBufferedOutputStream(byteStream));os.flush();os.writeObject(o);os.flush();// 检索字节数组byte[] sendBuf = byteStream.toByteArray();DatagramPacket packet = new DatagramPacket(sendBuf, sendBuf.length, address, desPort);int byteCount = packet.getLength();dSock.send(packet);os.close();}catch (UnknownHostException e){System.err.println("Exception: " + e);e.printStackTrace ();}catch (IOException e){ e.printStackTrace(); }}}

下面的代码清单解释了如何 Receiver 类中实现 receive 方法。recvObjFrom 该方法供接收者接收对象。您应该将此方法包含在您的代码中,以接收操作中的对象。

import java.io.*;import java.net.*; public class Receiver{public Object recvObjFrom(){try{byte[] recvBuf = new byte[5000];DatagramPacket packet = new DatagramPacket(recvBuf,recvBuf.length);dSock.receive(packet);int byteCount = packet.getLength();ByteArrayInputStream byteStream = newByteArrayInputStream(recvBuf);ObjectInputStream is = newObjectInputStream(new BufferedInputStream(byteStream));Object o = is.readObject();is.close();return(o);}catch (IOException e){System.err.println("Exception: " + e);e.printStackTrace ();}catch (ClassNotFoundException e){ e.printStackTrace(); }return(null);}}

人们可能会担心字节数组的大小 -- 因为当你结构的时候 ByteArrayOutputStream 或 ByteArrayInputStream 你必须指定数组的大小。由于您不知道运行过程中物体的大小,因此很难指定物体的大小。对象在运行过程中的大小通常是不可预测的。幸运的是,Java 的 ByteArrayInputStream 和 ByteArrayOutputStream 其大小可根据需要自动扩展。

通过使用总结 Java 对于内部序列化代码,我解释了使用数据包传输对象的方法。正如你所看到的,技能是使用字节数组流将对象流化为字节数组。