跨平台的对象序列化-基于protocolBuffer的网络序列化对象的收发——Java

使用谷歌的开源跨平台序列化技术:Protocol Buffer

1.过程:编辑描述文件Student.proto 

syntax = "proto2";

package xuexi;

option java_package = "com.bn";
option java_outer_classname = "StudentProtos";

message Student{
   required string sno = 1;
   required string sname = 2;
   optional int32 sage = 3;
   optional string sclass = 4;
}

2.用compile.bat(自己编写的)工具进行转换,转换获得跨平台序列化的辅助类studentProtos.java
 

3.编译生成的辅助类。(注:过程要使用.jar包)

4.(MainFileWrite.java)创建可跨平台序列化的对象并序列化对象生成字节数组存入文件中,生成序列化文件stu.pb

package com.bn;

import java.io.*;

public class MainFileWrite
{
	public static void main(String args[]) throws Exception
	{
		//创建学生对象构建器
		StudentProtos.Student.Builder builder=StudentProtos.Student.newBuilder();//Student为辅助类中的一个内部类
		//设置构建器各项属性
		builder.setSno("10001");
		builder.setSname("王强");
		builder.setSage(21);
		builder.setSclass("97001");
		//创建学生对象
		StudentProtos.Student stuSend=builder.build();//Student为辅助类中的一个内部类
		
		//获取学生对象字节数据
		byte[] data=stuSend.toByteArray();//把对象序列化,并返回字节数组
		
		//将数据写入文件
		FileOutputStream fout=new FileOutputStream("stu.pb");
		fout.write(data);
		fout.flush();//刷新
		fout.close();
	}
}

5.(MainFileRead.java)从stu.pb文件中读出序列化对象的字节数据,并将字节数据解析为对象。

package com.bn;

import java.io.*;

public class MainFileRead
{
	public static void main(String args[]) throws Exception
	{
		//从文件中读出序列化对象的字节数据
		File f=new File("stu.pb");
		int size=(int)f.length();		
		byte[] data=new byte[size];
		FileInputStream fin=new FileInputStream(f);
		fin.read(data);
		fin.close();
		
		//将字节数据解析为对象
		StudentProtos.Student stuAccept=StudentProtos.Student.parseFrom(data);
		//打印对象各个属性信息
		System.out.println(stuAccept.getSno());
		System.out.println(stuAccept.getSname());
		System.out.println(stuAccept.getSage());
		System.out.println(stuAccept.getSclass());
	}
}

跨平台Socket核心要点

1、Socket通讯的核心是收发一堆字节,原来学Java时的那一套,诸如readUTF等都是在这个上面的封装,好处是方便,缺点是只能语言内部使用。

2、因此跨语言时,我们心中传输的只能是字节序列,因为只有字节跨语言、跨平台解析时不会有问题。

3、收发字节序列的要点就是收发代码应该是有保障的,例如:希望读到4个字节,那么就只能正好读到四个字节,多一个少一个都不行。

4、能够两边正常有保障地收发任意长度字节序列后,就可以在这之上再搭建传输其他类型数据的实现。
 

模拟跨语言跨平台网络数据收发案例:

使用资源:生成的跨平台序列化的辅助类studentProtos.java
工具类:ConvertUtilCommonSocket.java、IOUtilCommonSocket.java

package com.bn.net;

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;

public class ConvertUtilCommonSocket 
{
	//填充字节数组的指定区间
	public static void fillData(byte[] dataAll,byte[] dataTemp,int from,int length)
	{
		for(int i=0;i<length;i++)
		{
			dataAll[i+from]=dataTemp[i];
		}
	}

	//将字符串转换为字节数组,按照UTF-8格式
	public static byte[] fromStringToBytes(String s)
	{
		byte[] ba=s.getBytes(Charset.forName("UTF-8"));
		return ba;
	}
	
	//将浮点数转换为字节数组
	public static byte[] fromFloatToBytes(float f)
	{
		int ti=Float.floatToIntBits(f);
		return fromIntToBytes(ti);
	}
	
	//将整数变为四字节数组,索引大的字节为高位
	public static byte[] fromIntToBytes(int k)
	{
		byte[] buff = new byte[4];
		buff[0]=(byte)(k&0x000000FF);
		buff[1]=(byte)((k&0x0000FF00)>>>8);
		buff[2]=(byte)((k&0x00FF0000)>>>16);
		buff[3]=(byte)((k&0xFF000000)>>>24);
		
		return buff;
	}
	
	//将整数变为四字节数组,索引大的字节为高位
	public static byte[] fromIntToBytesNI(int k)
	{
		byte[] buff = new byte[4];
		buff[3]=(byte)(k&0x000000FF);
		buff[2]=(byte)((k&0x0000FF00)>>>8);
		buff[1]=(byte)((k&0x00FF0000)>>>16);
		buff[0]=(byte)((k&0xFF000000)>>>24);
		
		return buff;
	}
	
	//将字节数组转化为字符串
	public static String fromBytesToString(byte[] bufId)
	{
		String s=null;		
		try 
		{
			s=new String(bufId,"UTF-8");
		} 
		catch (UnsupportedEncodingException e) 
		{
			e.printStackTrace();
		}
		return s;
	}
	
	//将字节数组转四字节整数
	public static int fromBytesToInt(byte[] buff)
	{
		return (buff[3] << 24) 
			+ ((buff[2] << 24) >>> 8) 
			+ ((buff[1] << 24) >>> 16) 
			+ ((buff[0] << 24) >>> 24);
	}
	
	//将字节数组转四字节整数
	public static int fromBytesToIntNI(byte[] buff)
	{
		return (buff[0] << 24) 
			+ ((buff[1] << 24) >>> 8) 
			+ ((buff[2] << 24) >>> 16) 
			+ ((buff[3] << 24) >>> 24);
	}
	
	public static float fromBytesToFloat(byte[] buf)
	{
		int k= fromBytesToInt(buf);		
		return Float.intBitsToFloat(k);
	}
}
------------------------------------------
package com.bn.net;
import java.io.*;

public class IOUtilCommonSocket
{
	//消息的前四个字节为长度字节数
	public static void writeBytes(byte[] data,OutputStream out) throws Exception
	{
		//写出配套Netty协议的数据字节数
		out.write(ConvertUtilCommonSocket.fromIntToBytesNI(data.length));
		//写出数据字节
		out.write(data);
		out.flush();
	}


	public static byte[] readBytes(InputStream din,int count) throws Exception
	{
		byte[] buf=new byte[count];
		int len=count;
		int curr=din.read(buf);
		count=count-curr;
		while(count!=0)
		{
			curr=din.read(buf,len-count,count);
			count=count-curr;
		}
		return buf;
	}

	public static int readIntNI(InputStream din) throws Exception
	{
		byte[] buf=readBytes(din,4);
		return ConvertUtilCommonSocket.fromBytesToIntNI(buf);
	}
}

服务端和客户端:

服务端:将客户端传来的数据解析成对象,整合成字符串,然后转换成字节序列传回去

package com.bn.net;

import java.io.*;
import com.bn.StudentProtos;
import java.net.*;

public class Server 
{
	public static void main(String args[]) throws Exception
	{
		ServerSocket ss=new ServerSocket(9999);
		System.out.println("Listening on 9999....");
		boolean flag=true;
		while(flag)
		{
			Socket sc=ss.accept();
			InputStream in=sc.getInputStream();
			OutputStream out=sc.getOutputStream();
			
			//服务器先接收数据长度
			int length=IOUtilCommonSocket.readIntNI(in);			
			//接收对象数据
			byte[] data=IOUtilCommonSocket.readBytes(in, length);
			//将字节数据解析为对象
			StudentProtos.Student stuAccept=StudentProtos.Student.parseFrom(data);
			//组装返回用字符串
			StringBuilder sb=new StringBuilder();
			sb.append(stuAccept.getSno());
			sb.append("|");
			sb.append(stuAccept.getSname());
			sb.append("|");
			sb.append(stuAccept.getSage());
			sb.append("|");
			sb.append(stuAccept.getSclass());
			//将返回字符串转换为字节序列
			data=ConvertUtilCommonSocket.fromStringToBytes(sb.toString());
			//发送返回字符串			
			IOUtilCommonSocket.writeBytes(data, out);
			//打印信息
			System.out.println("------------"+sc.getInetAddress()+"------------");
			System.out.println(sb.toString());
			//关闭相关
			in.close();
			out.close();
			sc.close();
		}
		ss.close();
	}
}

客户端: 通过辅助类创建对象,获得其字节序列;建立Socket连接,并传输数据;接收返回的字节序列数据

package com.bn.net;

import java.io.*;
import com.bn.StudentProtos;
import java.net.*;

public class Client 
{
	public static void main(String args[]) throws Exception
	{
		//创建学生对象构建器
		StudentProtos.Student.Builder builder=StudentProtos.Student.newBuilder();
		//设置构建器各项属性
		builder.setSno("10001");
		builder.setSname("王强");
		builder.setSage(21);
		builder.setSclass("97001");
		//创建学生对象
		StudentProtos.Student stuSend=builder.build();
		
		//获取学生对象字节数据
		byte[] data=stuSend.toByteArray();
		
		//创建Socket连接
		Socket sc=new Socket("192.168.8.100",9999);
		InputStream in=sc.getInputStream();
		OutputStream out=sc.getOutputStream();
		//写出对象数据
		IOUtilCommonSocket.writeBytes(data, out);
		//接收返回消息字节数
		int count=IOUtilCommonSocket.readIntNI(in);
		//接收返回消息字节序列
		data=IOUtilCommonSocket.readBytes(in, count);
		//将返回序列转化为字符串
		String msg=ConvertUtilCommonSocket.fromBytesToString(data);
		//打印信息
		System.out.println(msg);
		//关闭相关
		in.close();
		out.close();
		sc.close();
		
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值