关于网络通信中如何传输对象的问题

      我们不管在用TCP/IP连接或是UDP连接传输数据时有时会遇到一个问题。如果对于一个对象拥有很多个属性值,比如说一个学生类对象有年龄,性别,学校,学号等等的属性以及一些方法。服务器端有个Student的类,我们需要把学生的这些数据发送给客户端,而我们的服务器上存储了成千上万个Student类对象,那么我们应该怎么传输呢?

解决方法一:

      采用某种协议将这些数据分批发送,我们一般在java连接中传输用到的流是DataOutputStream(以下简称dos),对于每一个学生对象,根据他们的年龄,性别,学校,学号这四个属性我们可以采取XMPP的传输方式构建一个String——"<student><age>[年龄数据]</age><sex>[性别数据]</sex><school>[学校数据]</school><id>[学号数据]</id></student>",然后用dos.writeBytes(String s)方法直接传输过去,客户端再将收到的数据自行解析最后还原。

      在我咨询的帮助中,大多数人也是建议这一种方法。不过我始终觉得这一种方法太过于麻烦,首先是协议得斟酌一番,然后对于Student类中的方法我们无法传输过去,如果在客户端需要用到这些方法时,必须在客户端自定义和服务器的Student类内容一样的类(称为ClientStudent),然后再通过接收到的数据new ClientStudent();我是一个怕麻烦的人╮(╯▽╰)╭,所以有没有简单一点的方法呢?有的,就是第二种。

解决方法二:

      我们在服务器端要发送成千上万个Student对象(st1,st2......),为何我们不直接将st1等发送给客户端呢?这里有几个问题,首先是我们查阅DataOutputStream类的API,并没有在里面发现write这种数据类型的方法,如果我们需要发送这种数据类型,那么就只能选择以下两种方法:①将st1对象转变成byte数组,然后再通过dos的writeByte()方法传递过去;②构建一个ObjectOutputStream(以下简称oos),然后调用它的writeObject(st1)方法直接传递过去。由于这两种方法都需要实现序列化,所以本质上是一样的,在接下来的过程中我们就以writeObject()方法为例。那么什么是序列化呢?

      百度百科的解释是:序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。

      搞不懂没关系,java的类实现序列化非常简单,只需要implements Serializable接口就行了,而且并不需要实现任何方法,因为该接口里没有定义方法。

      以Student类为例,我们写成public class Student implements Serializable{}就行了。

      实现了序列化之后就可以将数据传输过去了,并且我们在客户端也能够接受到一致的数据。接收到数据之后是不是就OK了呢?别急,我们还需要做一件事,就是将接收到的数据反序列化成Student对象。我一开始是将服务器定义的Student类原封不动的复制给客户端,然后再进行强制转换Student st = (Student) 从流中读取的Object对象;(好像和第一种方法差不多呵~~),但是这样一运行程序就立马报错了,会抛出类型无法转换的问题。大家应该想得通吧,虽然我们定义的内容是一样的,但是在程序看来这完全就是两码子东西,必然会报错的啊(区别于第一种方法,第一种方法是通过读取到的基本数据类型重新构造对象)。那么怎么让程序认识这个类呢?这是最后一个难题,解决了他我们传输对象的问题就完全解决了!我们会发现一个有趣的现象,如果我们writeObject中传输的对象是java中原本有的类,比如说Font类,然后在客户端进行强制转换,程序并不会报错。为什么呢?现在我们转回来看上面的问题~聪明的读者也许已经想到解决方案了,那就是将我们实现序列化的Student类打包成jar文件,然后服务器和客户端都导入这一个文件,这样就能正常的识别类了,强制转换也就不会报错了。至此,任务已成功解决!

后记思考:

一、两种方法各有千秋,也许第一种效率会高点,因为传送的只是基本数据类型,且避免的更多无谓的数据在网络间通信,成功节省带宽。而第二种很明显会简单点,虽然前期在实现过程中会麻烦一点,但是在后期的程序中只Student类的用法就和java本身具有的类用法差不多了。但是对于我这种懒人来说,还是选择第二种理所当然一点(~﹃~)~zZ

二、在Student类中所使用的类也必须全部实现序列化,比如说在Student类中有个school属性,而这个属性又是School 类的一个对象,那么School对象也要实现序列化,以此类推,如果School类中又有类,那么那个类也要序列化……

三、在java中有些类是不能进行序列化的,比如说java.net.Socket,java.io.InputStream……,这也就是说你的自定义类中不能存在这些类的对象,否则必定会报错!

四、传输对象肯定不止这两种方法,就我目前在网上查找的就包括一个什么“类锁定”的方法(没有详细说明,也不知道是否真的能解决),还有一个java中已经包括的RMI(Remote Method Invocation )系列类的使用,是可以用来加载远程类。RMI貌似是为了实现分布式应用和云计算产生的,搞这个好像有点小题大作了,不过应该也是能够解决的。更多的方法欢迎补充~~~

猜你喜欢

转载自zhuyifeng.iteye.com/blog/1453603