JAVA Socket传输数据和对象

2024-04-27 10:48

本文主要是介绍JAVA Socket传输数据和对象,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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包;

这篇关于JAVA Socket传输数据和对象的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/940304

相关文章

Java中Map的五种遍历方式实现与对比

《Java中Map的五种遍历方式实现与对比》其实Map遍历藏着多种玩法,有的优雅简洁,有的性能拉满,今天咱们盘一盘这些进阶偏基础的遍历方式,告别重复又臃肿的代码,感兴趣的小伙伴可以了解下... 目录一、先搞懂:Map遍历的核心目标二、几种遍历方式的对比1. 传统EntrySet遍历(最通用)2. Lambd

Spring Boot 中 RestTemplate 的核心用法指南

《SpringBoot中RestTemplate的核心用法指南》本文详细介绍了RestTemplate的使用,包括基础用法、进阶配置技巧、实战案例以及最佳实践建议,通过一个腾讯地图路线规划的案... 目录一、环境准备二、基础用法全解析1. GET 请求的三种姿势2. POST 请求深度实践三、进阶配置技巧1

springboot+redis实现订单过期(超时取消)功能的方法详解

《springboot+redis实现订单过期(超时取消)功能的方法详解》在SpringBoot中使用Redis实现订单过期(超时取消)功能,有多种成熟方案,本文为大家整理了几个详细方法,文中的示例代... 目录一、Redis键过期回调方案(推荐)1. 配置Redis监听器2. 监听键过期事件3. Redi

Spring Boot 处理带文件表单的方式汇总

《SpringBoot处理带文件表单的方式汇总》本文详细介绍了六种处理文件上传的方式,包括@RequestParam、@RequestPart、@ModelAttribute、@ModelAttr... 目录方式 1:@RequestParam接收文件后端代码前端代码特点方式 2:@RequestPart接

SpringBoot整合Zuul全过程

《SpringBoot整合Zuul全过程》Zuul网关是微服务架构中的重要组件,具备统一入口、鉴权校验、动态路由等功能,它通过配置文件进行灵活的路由和过滤器设置,支持Hystrix进行容错处理,还提供... 目录Zuul网关的作用Zuul网关的应用1、网关访问方式2、网关依赖注入3、网关启动器4、网关全局变

SpringBoot全局异常拦截与自定义错误页面实现过程解读

《SpringBoot全局异常拦截与自定义错误页面实现过程解读》本文介绍了SpringBoot中全局异常拦截与自定义错误页面的实现方法,包括异常的分类、SpringBoot默认异常处理机制、全局异常拦... 目录一、引言二、Spring Boot异常处理基础2.1 异常的分类2.2 Spring Boot默

基于SpringBoot实现分布式锁的三种方法

《基于SpringBoot实现分布式锁的三种方法》这篇文章主要为大家详细介绍了基于SpringBoot实现分布式锁的三种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、基于Redis原生命令实现分布式锁1. 基础版Redis分布式锁2. 可重入锁实现二、使用Redisso

SpringBoot的全局异常拦截实践过程

《SpringBoot的全局异常拦截实践过程》SpringBoot中使用@ControllerAdvice和@ExceptionHandler实现全局异常拦截,@RestControllerAdvic... 目录@RestControllerAdvice@ResponseStatus(...)@Except

Springboot配置文件相关语法及读取方式详解

《Springboot配置文件相关语法及读取方式详解》本文主要介绍了SpringBoot中的两种配置文件形式,即.properties文件和.yml/.yaml文件,详细讲解了这两种文件的语法和读取方... 目录配置文件的形式语法1、key-value形式2、数组形式读取方式1、通过@value注解2、通过

Java 接口定义变量的示例代码

《Java接口定义变量的示例代码》文章介绍了Java接口中的变量和方法,接口中的变量必须是publicstaticfinal的,用于定义常量,而方法默认是publicabstract的,必须由实现类... 在 Java 中,接口是一种抽象类型,用于定义类必须实现的方法。接口可以包含常量和方法,但不能包含实例