前言
- 上一篇 Android端 - 通过Socket以及TCP协议和MFC端通信(Receive篇) 中写到 Android端和PC端通过Socket在局域网条件下传输信息即文件;
- 本文将实现 Java 端和 PC 端通过 Socket 传递结构体;
PC 端
-
结构体如下:
//SendMD5andLogDlg.h : 头文件中 typedef struct { int nMsgCode; double pdDataX[2]; double pdDataY[2]; int nDataCnt; }ST_MSG;
-
取消 C++ 自动的字节对齐:
//SendMD5andLogDlg.h : 头文件中 #pragma once #pragma pack(push,1) //主要添加这一句! …… #pragma(pop)//末尾一天给添加这一句
PC 端注意
- 结构体是个好东西 (可惜我不会) ;
- C++ 发送和接受的结构体 实际上是字节流 byte[];所以要注意在 Java 端将 对象转换成字节流发送;将字节流解析成对象;
- C++ 结构体定义的参数顺序至关重要,传递、解析都要按照该顺序;
- C++ 存在自 (xian) 动 (de) 对 (dan) 齐 (teng);举个栗子:我这里的结构体参数为 int double double …… double int ;众所周知,int 4 个字节,double 8个字节(
注意:Java和C++中这两个基本类型所占的字节数是一致的!如果你是使用其他类型,记得字节转换问题!
)在Java中不存在字节对齐,所以字节数就是 4+ 8 + 8 +…… + 8 + 4 ;但是C(蛋
)+(疼
) 给自动对齐了结果是: 8 + 8 + 8 +……+ 8 + 8;取消蛋疼的字节对齐已经在上面写了;下面我将用蛋疼
代替*语言
;
Android端
-
创建对应的类;并给出对应参数;构造器;
public class PcStruct { public byte[] buf; public int nMsgCode; public int nDataCnt; public double pdDataX[] = new double[2]; public double pdDataY[] = new double[2]; public int getnMsgCode() { return nMsgCode; } public void setnMsgCode(int nMsgCode) { this.nMsgCode = nMsgCode; } public int getnDataCnt() { return nDataCnt; } public void setnDataCnt(int nDataCnt) { this.nDataCnt = nDataCnt; } public double[] getPdDataX() { return pdDataX; } public void setPdDataX(double[] pdDataX) { this.pdDataX = pdDataX; } public double[] getPdDataY() { return pdDataY; } public void setPdDataY(double[] pdDataY) { this.pdDataY = pdDataY; } public static final String TAG = PcStruct.class.getClass().getSimpleName(); public PcStruct(byte[] buf) { this.buf = buf; } ……
-
提供解析字节流的构造器;
/** * 通过接收到的 byte[] 对象解析成结构体对象;由于PC还没有进行字节对齐,故得到的自己数为48个;int由4个字节 被蛋疼对齐成8个字节; * @param bytes 来自 PC 端的字节流文件; * @return 返回一个对象; */ public static PcStruct getPcStructInstance(byte[] bytes) { PcStruct pcStruct = new PcStruct(); byte[] tempBytes4 = new byte[8]; byte[] tempBytes8 = new byte[8]; System.arraycopy(bytes, 0, tempBytes4, 0, 8); pcStruct.setnMsgCode(byteArrayToInt(tempBytes4)); System.arraycopy(bytes, 40, tempBytes4, 0, 8); pcStruct.setnDataCnt(byteArrayToInt(tempBytes4)); System.arraycopy(bytes, 8, tempBytes8, 0, 8); double mPdDataX0 = byteArrayToDouble(tempBytes8); System.arraycopy(bytes, 16, tempBytes8, 0, 8); double mPdDataX1 = byteArrayToDouble(tempBytes8); double[] mPdDataX = new double[]{mPdDataX0, mPdDataX1}; pcStruct.setPdDataX(mPdDataX); System.arraycopy(bytes, 24, tempBytes8, 0, 8); double mPdDataY0 = byteArrayToDouble(tempBytes8); System.arraycopy(bytes, 32, tempBytes8, 0, 8); double mPdDataY1 = byteArrayToDouble(tempBytes8); double[] mPdDataY = new double[]{mPdDataY0, mPdDataY1}; pcStruct.setPdDataY(mPdDataY); return pcStruct; }
-
提供将对象转换成字节流的途径:如下:
/** * 这里通过构造器 + getBuf() 的方式,获取输出字节流,向PC端传输结构体; * 这里 PC 端已经设置为原始对齐方式,即,字节 无 对齐方式;这里需要注意的是!传递的顺序 应该和PC端定义结构参数的顺序一致 */ //参数字节:4+4 + 8+8+8+8 public PcStruct(int nMsgCode, int nDataCnt, double pdDataX0, double pdDataX1, double pdDataY0, double pdDataY1) { byte[] tempByte4 = new byte[4]; byte[] tempByte8 = new byte[8]; buf = new byte[40]; tempByte4 = toLH(nMsgCode); System.arraycopy(tempByte4, 0, buf, 0, 4); tempByte8 = toLH(pdDataX0); System.arraycopy(tempByte8, 0, buf, 4, 8); tempByte8 = toLH(pdDataX1); System.arraycopy(tempByte8, 0, buf, 12, 8); tempByte8 = toLH(pdDataY0); System.arraycopy(tempByte8, 0, buf, 20, 8); tempByte8 = toLH(pdDataY1); System.arraycopy(tempByte8, 0, buf, 28, 8); tempByte4 = toLH(nDataCnt); System.arraycopy(tempByte4, 0, buf, 36, 4); } /** * 返回要发送的数组 */ public byte[] getBuf() { return buf; }
-
提供以下字节转换的方法:这里只提供我用过好用的 int 和 double 类型;
/** * 将int转为低字节在前,高字节在后的byte数组 */ private static byte[] toLH(int n) { byte[] b = new byte[4]; b[0] = (byte) (n & 0xff); b[1] = (byte) (n >> 8 & 0xff); b[2] = (byte) (n >> 16 & 0xff); b[3] = (byte) (n >> 24 & 0xff); return b; } /** * double 转 byte[] 小端 低前高后 */ public static byte[] toLH(double data) { long intBits = Double.doubleToLongBits(data); byte[] bytes = getLongBytes(intBits); return bytes; } /** * 好使!字节数组到int的转换. */ public static int byteArrayToInt(byte[] b) { int s = 0; // 最低位 int s0 = b[0] & 0xff; int s1 = b[1] & 0xff; int s2 = b[2] & 0xff; int s3 = b[3] & 0xff; s3 <<= 24; s2 <<= 16; s1 <<= 8; s = s0 | s1 | s2 | s3; return s; } /** * 好使!字节数组到double的转换. */ public static double byteArrayToDouble(byte[] b) { long m; m = b[0]; m &= 0xff; m |= ((long) b[1] << 8); m &= 0xffff; m |= ((long) b[2] << 16); m &= 0xffffff; m |= ((long) b[3] << 24); m &= 0xffffffffl; m |= ((long) b[4] << 32); m &= 0xffffffffffl; m |= ((long) b[5] << 40); m &= 0xffffffffffffl; m |= ((long) b[6] << 48); m &= 0xffffffffffffffl; m |= ((long) b[7] << 56); return Double.longBitsToDouble(m); }
-
当然这是简单的数据,实际上,我们的数据上千个,这种方法显然是不靠谱的,我最近看到 JNA 模拟结构体可以轻松实现以上办法,之后应该会使用这种方法,请关注我吧!我会及时更新的!
感谢
-
我虽是学习 Java、Android,但难免存在知识盲点,比如 网络编程;以及 Java 和 C++ 的区别之类;
-
本文参考自以下文章,但应该不止以下文章,向他们表示感谢!
参考:https://blog.youkuaiyun.com/qq_40384776/article/details/103711300
https://blog.youkuaiyun.com/lianxianxun230/article/details/77428669
https://www.cnblogs.com/linzhanfly/p/9794752.html
https://blog.youkuaiyun.com/DoasIsay/article/details/103797033基本类型转 byte 数组:https://www.cnblogs.com/moonciki/p/8145834.html