JAVA Socket传输数据和对象

来源:互联网 发布:算法小时代 编辑:程序博客网 时间:2024/04/29 18:46

1   引子

               Socket通信中,我们要在客户端和服务端中传输数据,传输的数据有各种类型,int,short,long以及String,甚至是自定义的各种Object,我们如何能够保证在服务端和客户端之间的数据能够使得两端都能理解,这就是所谓的“应用协议”;

 

在通信的“管道”中,流的都是二进制数据,所以,双方如何约定好“解析规则”,则就必须要求通信双方都预先制定好协议;

 

2   序列化和反序列化

Java中,有一个借口Serializable接口,这个接口不需要实现实现任何方法,只是一个标识,Java 序列化技术可以使你将一个对象的状态写入一个Byte 流里,并且可以从其它地方把该Byte 流里的数据读出来。重新构造一个相同的对象。这种机制允许你将对象通过网络进行传播,并可以随时把对象持久化到数据库、文件等系统里。

 

 

代码如下:

Person

import java.io.Serializable;

import java.util.Date;

 

public class Person implements Serializable {

         private String name;

         private int tall;

         private transient Date birthday;

         private Address address;

 

         public String getName() {

                   return name;

         }

 

         public void setName(String name) {

                   this.name = name;

         }

 

         public int getTall() {

                   return tall;

         }

 

         public void setTall(int tall) {

                   this.tall = tall;

         }

 

         public Date getBirthday() {

                   return birthday;

         }

 

         public void setBirthday(Date birthday) {

                   this.birthday = birthday;

         }

 

         public Address getAddress() {

                   return address;

         }

 

         public void setAddress(Address address) {

                   this.address = address;

         }

 

}

 

Address

package stream.demo;

 

import java.io.Serializable;

 

public class Address implements Serializable{

         private String city;

         private String street;

 

         public Address() {

         }

 

         public Address(String city, String street) {

                   this.city = city;

                   this.street = street;

         }

 

         public String getCity() {

                   return city;

         }

 

         public void setCity(String city) {

                   this.city = city;

         }

 

         public String getStreet() {

                   return street;

         }

 

         public void setStreet(String street) {

                   this.street = street;

         }

 

}

 

我们将这个对象串行化至文件系统,然后将之还原,这个过程其实类似于一个“压扁”和“充气”的过程,请注意,我们的Person类中包含一个嵌入对象,并且birthday变化,将之设置为transient限定符,这表示我们放弃了birthday的串行化;

package stream.demo;

 

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.OutputStream;

import java.util.Date;

 

public class Persistence {

         public static void main(String[] args) {

                   Persistence.savePerson();

                   Persistence.getPerson();

         }

 

         public static void getPerson() {

                   try {

                            InputStream in = new FileInputStream("c://person.dat");

                            ObjectInputStream dataInput = new ObjectInputStream(in);

                            Person p = (Person) dataInput.readObject();

                            System.out.println(p.getName());

                            System.out.println(p.getTall());

                            System.out.println(p.getBirthday());

                            System.out.println(p.getAddress().getCity());

                            System.out.println(p.getAddress().getStreet());

                   } catch (Exception e) {

                            // TODO Auto-generated catch block

                            e.printStackTrace();

                   }

         }

 

         public static void savePerson() {

                   Person p = new Person();

                   p.setName("corey");

                   p.setTall(171);

                   p.setBirthday(new Date());

                   p.setAddress(new Address("yiyang", "ziyang"));

                   OutputStream out = new ByteArrayOutputStream();

                   try {

                            OutputStream fileOut = new FileOutputStream(new File(

                                               "c://person.dat"));

 

                            ObjectOutputStream dataOut = new ObjectOutputStream(fileOut);

                            dataOut.writeObject(p);

 

                            dataOut.close();

                            fileOut.close();

                   } catch (IOException e) {

                            // TODO Auto-generated catch block

                            e.printStackTrace();

                   }

         }

}

 

Console:

corey

171

null

yiyang

ziyang

 

                   可见,我们的目的实现了,并且birthday没有被串行化;如果我们不讲Address实现Serializable,会发生异常:

java.io.WriteAbortedException: writing aborted

 

3   IO

我们都知道JAVAIO是一个极其庞大的类家族,也正是运用了装饰模式的经典场景;

 

可见,我们一部分管道是直接连接了数据源,或者直接输出到数据源,他们能够直接读数据或者写数据,我们上一节中使用的FileoutputStreamFileInputStream就是这样的管道,他们的数据源就是指存在文件系统中的文件;下一部分我们要求使用的ByteArrayInputStreamByteArrayOutputStream也是这样的管道,不过他的数据源是存在内存中的数组;他能够通过write把数据,字符串等等写进这块数组区域,并且能够得到一个完整的字节数组;

而另外的一部分则不能直接与数据源发生读写交互,他主要用于装饰上一部分的管道,给这个管道赋予更加便捷的功能,诸如BufferedReader等等的,主要是提供了缓存,就好比给上一部分管道中间提供了一个“蓄水池”,这个“蓄水池”等到“水流”到了一定的程度的时候,才会一次性的流进或者流出等等,诸如ObjectInputStreamObjectOutputStream,他们能够直接读写一个Object,但是这个Object被写到了哪里,是内存中的字节数组还是文件文件的文件中,而必须委托给其他管道实现;

 

4   在网络中传输对象

在网络中的两台机器中传输对象,前提首先是基于同一个平台,这是序列化的基础,所以这里主要有两种做法:

 

l  采用JAVA的序列化机制,将对象“压扁”成二进制字节,将二进制字节在网络中传输;

l  自定义协议,将对象用字符串描述出来,将字符串用二进制表示,在网络中传输,在另外一边用相反的策略解析这个字符串,重新构造业务对象,这个方法能够在异构平台中进行传输而不变形,但是需要额外的编写“压扁”和“充气”的代码;

 

我们这里用第一种方法:

package stream.demo;

 

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.OutputStream;

import java.util.Date;

 

public class Persistence {

         public static void main(String[] args) {

                   byte[] bs = Persistence.toBytes();

        //在网络中进行传输

                   Persistence.getBytes(bs);

         }

 

         public static byte[] toBytes() {

                   Person p = new Person();

                   p.setName("corey");

                   p.setTall(171);

                   p.setBirthday(new Date());

                   p.setAddress(new Address("yiyang", "ziyang"));

                   ByteArrayOutputStream out = new ByteArrayOutputStream();

                   try {

                            ObjectOutputStream oout = new ObjectOutputStream(out);

                            oout.writeObject(p);

                   } catch (IOException e) {

                            // TODO Auto-generated catch block

                            e.printStackTrace();

                   }

                   return out.toByteArray();

         }

 

         public static void getBytes(byte[] bs) {

                   try {

                            ByteArrayInputStream byteIn = new ByteArrayInputStream(bs);

                            ObjectInputStream in = new ObjectInputStream(byteIn);

                            Person p = (Person) in.readObject();

                            System.out.println(p.getName());

                            System.out.println(p.getTall());

                            System.out.println(p.getBirthday());

                            System.out.println(p.getAddress().getCity());

                            System.out.print(p.getAddress().getStreet());

                   } catch (Exception e) {

                            // TODO Auto-generated catch block

                            e.printStackTrace();

                   }

         }

}

 

其中服务端代码片段为:

in = this.getRequestSocket().getInputStream();

                            out = this.getRequestSocket().getOutputStream();

                            byte[] bs = Persistence.toBytes();

                            System.out.println("发送数字长度:"+bs.length);

                            out.write(bs);

                            this.getRequestSocket().close();

 

客户端代码片段为:

InputStream in = request.getInputStream();

                            byte[] bin = new byte[200];

                            int length = 0;

                            while ((length = in.read(bin)) != -1) {

                                     System.out.println("length:" + length);

                                     Persistence.getBytes(bin);

                            }

 

其中,服务端代码和客服端代码都引用了Person jar包;