报文 组包 解包

在实际项目中经常会碰到不同系统之间的数据交换,有些是用webservice。有些则是使用发socket消息的方式,将需要发送的消息组装成特定格式的字符串或Xml格式的文件,再通过socket编程发送到对方系统。本文主要讨论组装成定长字符串。

抽象任何一个定长消息包(MsgPackage)都是由一个或多个消息片(MsgPiece)组成。任何一个消息片都是由一个或多个消息域(MsgField)组成。消息域的属性有:域名、长度、值、如果值的长度小于定义的最大长度那值是靠左还是右对齐其余的是用什么字符填充。

工具代码

消息域类

package socket.msg;

/**

* 消息域

* @author Zhenwei.Zhang (2013-9-25)

*/

public class MsgField {

public MsgField (String name, int length, char fillChar, FillSide fillSide) {

this.name = name;

this.length = length;

this.fillChar = fillChar;

this.fillSide = fillSide;

}

/**

* 填充位置

* @author Zhenwei.Zhang (2013-9-25)

*/

public enum FillSide {

LEFT, RIGHT

}

/** 域名称 */

private String name;

/** 长度 */

private int length;

/** 填充字符 */

private char fillChar;

/** 填充位置 */

private FillSide fillSide;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getLength() {

return length;

}

public void setLength(int length) {

this.length = length;

}

public char getFillChar() {

return fillChar;

}

public void setFillChar(char fillChar) {

this.fillChar = fillChar;

}

public FillSide getFillSide() {

return fillSide;

}

public void setFillSide(FillSide fillSide) {

this.fillSide = fillSide;

}

}

报文消息片类

package socket.msg;

import java.beans.PropertyDescriptor;

import java.io.UnsupportedEncodingException;

import java.lang.reflect.Method;

import java.util.LinkedList;

import java.util.List;

import socket.msg.MsgField.FillSide;

/**

* 消息片,由多个消息域按一定的顺序组成

* @author Zhenwei.Zhang (2013-9-25)

*/

public abstract class MsgPiece {

/** 消息域列表 */

private List itemList = new LinkedList();

public MsgPiece(MsgField[] items) {

itemList = new LinkedList();

for (int i = 0; i < items.length; i++) {

itemList.add(items[i]);

}

}

/**

* 组消息

* @author Zhenwei.Zhang (2013-9-25)

* @param charsetName

* @return

* @throws Exception

*/

public byte[] pack(String charsetName) throws Exception {

StringBuffer result = new StringBuffer();

byte[] b = null;

try {

for (MsgField item : itemList) {

PropertyDescriptor pd = new PropertyDescriptor(item.getName(), this.getClass());

Method readMethod = pd.getReadMethod();

Object gotVal = readMethod.invoke(this);

String strFill = gotVal == null ? "" : gotVal.toString();

result.append(this.autoFill(strFill, item.getFillChar(), item.getFillSide(), item.getLength(), charsetName));

}

b = result.toString().getBytes(charsetName);

} catch (Exception e) {

throw new Exception("组消息出错:" + e.getMessage(), e);

}

return b;

}

/**

* 解消息

* @author Zhenwei.Zhang (2013-9-25)

* @param msg

* @param charsetName

* @throws Exception

*/

public void unPack(byte[] msg, String charsetName) throws Exception {

if (msg.length != this.getLength()) {

throw new Exception("解消息出错,消息长度不合法!");

}

int index = 0;

try {

for (MsgField item : itemList) {

String value = new String(msg, index, item.getLength(), charsetName);

value = this.getRealVal(value, item.getFillSide(), item.getFillChar());

PropertyDescriptor pd = new PropertyDescriptor(item.getName(), this.getClass());

Method setMethod = pd.getWriteMethod();

if (pd.getPropertyType().equals(byte.class) || pd.getPropertyType().equals(Byte.class)) {

setMethod.invoke(this, Byte.parseByte(value));

}else if (pd.getPropertyType().equals(short.class) || pd.getPropertyType().equals(Short.class)) {

setMethod.invoke(this, Short.parseShort(value));

}else if (pd.getPropertyType().equals(int.class) || pd.getPropertyType().equals(Integer.class)) {

setMethod.invoke(this, Integer.parseInt(value));

}else if (pd.getPropertyType().equals(long.class) || pd.getPropertyType().equals(Long.class)) {

setMethod.invoke(this, Long.parseLong(value));

}else if (pd.getPropertyType().equals(float.class) || pd.getPropertyType().equals(Float.class)) {

setMethod.invoke(this, Float.parseFloat(value));

}else if (pd.getPropertyType().equals(double.class) || pd.getPropertyType().equals(Double.class)) {

setMethod.invoke(this, Double.parseDouble(value));

}else if (pd.getPropertyType().equals(char.class) || pd.getPropertyType().equals(Character.class)) {

setMethod.invoke(this, value.toCharArray()[0]);

}else if (pd.getPropertyType().equals(boolean.class) || pd.getPropertyType().equals(Boolean.class)) {

setMethod.invoke(this, Boolean.valueOf(value));

}else {

setMethod.invoke(this, value);

}

index += item.getLength();

}

} catch (Exception e) {

throw new Exception("解消息出错:" + e.getMessage(), e);

}

}

/**

* 获得消息片长

* @author Zhenwei.Zhang (2013-9-25)

* @return

*/

public int getLength() {

int length = 0;

for (MsgField item : itemList) {

length += item.getLength();

}

return length;

}

/**

* 格式化真实值为指定长度字符串,超长自动截取

* @author Zhenwei.Zhang (2013-9-26)

* @param value

* @param fillChar 填充字符

* @param side 填充位置

* @param totalLen 域定义的长度

* @param charsetName 编码

* @return

*/

private String autoFill(String value, char fillChar, FillSide side, int totalLen, String charsetName) {

try {

if (value == null) {

value = "";

}

StringBuffer sbuffBuffer = new StringBuffer();

// 长度超过指定长度,截取

if (value.getBytes(charsetName).length > totalLen) {

byte[] data = new byte[totalLen];

System.arraycopy(value.getBytes(charsetName), 0, data, 0, totalLen);

sbuffBuffer.append(new String(data, charsetName));

return sbuffBuffer.toString();

}

if (side == socket.msg.MsgField.FillSide.RIGHT) {

sbuffBuffer.append(value);

}

for (int i = value.getBytes(charsetName).length; i < totalLen; i++) {

sbuffBuffer.append(fillChar);

}

if (side == socket.msg.MsgField.FillSide.LEFT) {

sbuffBuffer.append(value);

}

return sbuffBuffer.toString();

} catch (UnsupportedEncodingException e) {

throw new RuntimeException("系统不支持" + charsetName + "编码");

}

}

/**

* 获得消息域的真实值

* @author Zhenwei.Zhang (2013-9-26)

* @param value

* @param side 填充位置

* @param fillChar 填充字符

* @return

* @throws Exception

*/

private String getRealVal(String value, FillSide side, char fillChar) throws Exception {

char[] chars = value.toCharArray();

if (FillSide.LEFT == side) {

int index = 0;

for (int i = 0; i < chars.length; i++) {

if (fillChar == chars[i]) {

index ++;

} else {

continue;

}

}

return value.substring(index);

} else if (FillSide.RIGHT == side) {

int index = chars.length - 1;

for (int i = index; i >= 0; i--) {

if (fillChar == chars[i]) {

index --;

} else {

continue;

}

}

return value.substring(0, index + 1);

} else {

throw new Exception("无效的填充位置");

}

}

}

消息包类

package socket.msg;

import java.beans.PropertyDescriptor;

import java.lang.reflect.Method;

/**

* 消息包,由多个消息片组成

* @author Zhenwei.Zhang (2013-10-12)

*/

public abstract class MsgPackage {

/** 消息包的消息片数组 */

String[] pieces = null;

public MsgPackage(String... pieName) {

pieces = pieName;

}

/**

* 组包

* @author Zhenwei.Zhang (2013-9-26)

* @param charsetName

* @return

* @throws Exception

*/

public byte[] pack(String charsetName) throws Exception {

int index = 0;

byte[] result = new byte[this.getLength()];

for (String p : pieces) {

PropertyDescriptor pd = new PropertyDescriptor(p, this.getClass());

Method getterMethod = pd.getReadMethod();

MsgPiece piece = (MsgPiece)getterMethod.invoke(this);

if (piece == null) { //属性为空则该属性组空串

piece = (MsgPiece) pd.getPropertyType().newInstance();

}

byte[] t = piece.pack(charsetName);

System.arraycopy(t, 0, result, index, t.length);

index += t.length;

}

return result;

}

/**

* 解包

* @author Zhenwei.Zhang (2013-9-26)

* @param msg

* @param charsetName

* @throws Exception

*/

public void unPack(byte[] msg, String charsetName) throws Exception {

if (msg.length != this.getLength()) {

throw new Exception("解消息出错,消息包长度不合法!");

}

int index = 0;

for (String p : pieces) { // 创建一个新的属性对象,解包,赋值

PropertyDescriptor pd = new PropertyDescriptor(p, this.getClass());

Method writeMethod = pd.getWriteMethod();

MsgPiece piece = (MsgPiece) pd.getPropertyType().newInstance();

byte[] t = new byte[piece.getLength()];

System.arraycopy(msg, index, t, 0, t.length);

piece.unPack(t, charsetName);

writeMethod.invoke(this, piece);

index += t.length;

}

}

/**

* 获得消息包长度

* @author Zhenwei.Zhang (2013-9-26)

* @return

* @throws Exception

*/

public int getLength() throws Exception {

int length = 0;

try {

for (String p : pieces) {

PropertyDescriptor pd = new PropertyDescriptor(p, this.getClass());

Method getterMethod = pd.getReadMethod();

MsgPiece piece = (MsgPiece)getterMethod.invoke(this);

if (piece == null) {

piece = (MsgPiece) pd.getPropertyType().newInstance();

}

length += piece.getLength();

}

} catch (Exception e) {

throw new Exception("获得消息包长度错误:" + e.getMessage(), e);

}

return length;

}

}

实例

以下是使用上述封装做的简单实现例子:

定义一个消息由消息头和消息体组成,消息头由name,sex,UAge,amt等属性组成,消息体由content属性组成。

package socket.msg.test;

import socket.msg.MsgField;

import socket.msg.MsgPiece;

import socket.msg.MsgField.FillSide;

public class TestHead extends MsgPiece {

private String name;

private boolean sex;

private int UAge;

private String URL;

private double amt;

public int getUAge() {

return UAge;

}

public void setUAge(int uAge) {

UAge = uAge;

}

public double getAmt() {

return amt;

}

public void setAmt(double amt) {

this.amt = amt;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public boolean isSex() {

return sex;

}

public void setSex(boolean sex) {

this.sex = sex;

}

public String getURL() {

return URL;

}

public void setURL(String uRL) {

URL = uRL;

}

private static final MsgField[] items = new MsgField[]{

new MsgField("name", 10, ' ', FillSide.RIGHT),

new MsgField("sex", 10, '0', FillSide.LEFT),

new MsgField("UAge", 10, '0', FillSide.LEFT),

new MsgField("URL", 10, ' ', FillSide.RIGHT),

new MsgField("amt", 10, '0', FillSide.LEFT)

};

public TestHead() {

super(items);

}

}

package socket.msg.test;

import socket.msg.MsgField;

import socket.msg.MsgPiece;

import socket.msg.MsgField.FillSide;

public class TestBody extends MsgPiece {

private static final MsgField[] items = new MsgField[]{

new MsgField("content", 20, ' ',FillSide.RIGHT),

};

public TestBody() {

super(items);

}

private String content;

public String getContent() {

return content;

}

public void setContent(String content) {

this.content = content;

}

}

package socket.msg.test;

import socket.msg.MsgPackage;

public class TestPackage extends MsgPackage {

public TestPackage() {

super("t1", "t2");

}

private TestHead t1;

private TestBody t2;

public TestHead getT1() {

return t1;

}

public void setT1(TestHead t1) {

this.t1 = t1;

}

public TestBody getT2() {

return t2;

}

public void setT2(TestBody t2) {

this.t2 = t2;

}

}

测试

package socket.msg.test;

import java.io.UnsupportedEncodingException;

public class Test {

/**

* @author Zhenwei.Zhang (2013-11-12)

* @param args

* @throws Exception

* @throws UnsupportedEncodingException

*/

public static void main(String[] args) throws UnsupportedEncodingException, Exception {

TestHead head = new TestHead();

head.setName("name");

head.setAmt(12.121);

head.setSex(false);

head.setURL("http://asdsaaaaaaaaaaaaaaaaaaaaaaa");

head.setUAge(111);

TestBody body = new TestBody();

body.setContent("content");

TestPackage packagee = new TestPackage();

packagee.setT1(head);

packagee.setT2(body);

System.out.println(new String(packagee.pack("GBK"), "GBK"));

TestPackage packagee2 = new TestPackage();

String str = "name 000000true0000000111http://asd000012.121content ";

packagee2.unPack(str.getBytes("GBK"), "GBK");

System.out.println(packagee2.getT1().isSex());

}

}

java socket发送定长报文_定长消息报文的组包与解包简单封装(Java实现)相关推荐

  1. java socket发送定长报文_一个基于TCP协议的Socket通信实例

    原标题:一个基于TCP协议的Socket通信实例 1. 前言 一般接口对接多以http/https或webservice的方式,socket方式的对接比较少并且会有一些难度.正好前段时间完成了一个so ...

  2. Java Socket发送与接收HTTP消息简单实现

    在上次Java Socket现实简单的HTTP服务我 们实现了简单的HTTP服务,它可以用来模拟HTTP服务,用它可以截获HTTP请求的原始码流,让我们很清楚的了解到我们向服务发的HTTP消息的结 构 ...

  3. Java解析银联报文_中国银联8583报文(JAVA)封包/解包/位图 相关操作源代码

    一:IS08583包介绍: ISO8583包(简称8583包)是一个国际标准的包格式,最多由128个字段域组成,每个域都有统一的规定,并有定长与变长之分. 8583包前面一段为位图,用来确定包的字段域 ...

  4. java socket多人聊天室_如何运用Java socket实现多人聊天室功能

    如何运用Java socket实现多人聊天室功能 导语:如何运用Java socket实现多人聊天室功能呢?下面是小编给大家提供的代码实现,大家可以参考阅读,更多详情请关注应届毕业生考试网. 目录结构 ...

  5. nds文件解包_下載wwpacker 1.84 更新 給wad解包和封包的工具

    警告: 使用這篇文章提及的方法及工具有一定風險, 最壞情況有可能令你的Wii變全磚. 如有任何損失, 本人概不負責. 如你不清楚那些工具的用途及用法, 請不要隨便使用或修改.[ 所需工具 ] wwpa ...

  6. java socket发送定长报文_socket编程

    1.socket编程的概念 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请 ...

  7. java socket发送定长报文_java使用Socket类接收和发送数据

    网络应用分为客户端和服务端两部分,而Socket类是负责处理客户端通信的Java类.通过这个类可以连接到指定IP或域名的服务器上,并且可以和服务器互相发送和接受数据.在本文及后面的数篇文章中将详细讨论 ...

  8. java限制发送短信次数_使用java发送短信验证码码,出现流量限制怎么办?急急急...

    注册登录后需要企业认证,直接在某度上找一张清晰有红章的企业营业执照,注意要细心点,要看看有没有水印.我第一次就没注意上传了一张有水印的营业执照,从此这个账号再也没有审核通过了,后面只能换个账号.都是后 ...

  9. java socket发送16进制_JavaSocket短连接实现分别接收字符串和16进制数据

    做个笔记,在接收16进制数据的时候乱码了.原因是Socket在接收数据的时候需要根据不同的数据定义不同的接收方式,也就是约定好传输协议(具体体现在后面服务端接收16进制那里). 字符串的发送接收 字符 ...

最新文章

  1. 你不知道的javaScript笔记(5)
  2. 设计一个shell程序,在/userdata目录下建立50个目录,并对每个目录给754权限!
  3. c#如何wmf图片转换成png图片_每日一学:如何将png图片转换为jpg图片
  4. android linux内核开发环境,定制Android 4.2.2开发环境
  5. Angular应用一个创建场景的问题分析
  6. ubuntu分解压缩包
  7. UVA 116——Unidirectional TSP
  8. python - hadoop,mapreduce demo
  9. 摄影测量--点位精度评定
  10. CentOS下Redisserver安装配置
  11. lfw人脸识别数据集
  12. react使用echarts地图实现中国地图大区展示
  13. 海马玩模拟器 v0.7.2 官方版​
  14. Java基于JSP+Servlet的校友论坛管理系统
  15. 一文了解SAP Ariba是什么?
  16. matlab中yx是什么意思啊,matlab中怎样定义未知数,如x,syms是什么意思?
  17. 广播(Broadcast)的简单用法
  18. 在VMware中如何安装vista
  19. 关于Linux 网络抓包的一些笔记整理
  20. PowerPoint如何找回隐藏的iSlide设计工具

热门文章

  1. 可惜了!哈工大2名大四准毕业生被开除,好前途毁于一旦,原因令人哭笑不得......
  2. 【SAP-PS笔记】项目交货之基于销售订单VL01N交货
  3. linux查看虚拟列表,C++类中虚表的详细讲解
  4. 主动学习之Alipy包使用 (一):高级封装
  5. 获取google退款订单(最新攻略)
  6. 淘宝运营 一张流程图 精准定位目标人群,找到店铺精准画像
  7. 反垃圾邮件软件_学习学术vol.1 | 正版软件服务平台
  8. 利用.frm、.ibd恢复数据
  9. python-入门基础(安装与运行)
  10. php diy明信片代码,创意DIY之如何自制扫雷游戏明信片的做法教程