Socket网络通讯开发总结之:Java 与 C进行Socket通讯
http://blog.sina.com.cn/s/blog_55934df80100i55l.html
(2010-04-08 17:26:29)先交待一下业务应用背景: 服务端:移动交费系统:基于C语言的Unix系统 客户端:增值服务系统:基于Java的软件系统 通迅协议:采用TCP/IP协议,使用TCP以异步方式接入 数据传输:基于Socket流的方式,传输的是网络字节序
Java Socket通讯实现方式这里不做过多的描述,网上到处可以搜索到,比较简单,这里要说的是Java 与 C 进行Socket通讯需注意的地方:
1、Java与C的各种数据类型存储的字节数是不同的:
Java与C的数据类型的比较
Type Java C
short 2-Byte 2-Byte
int 4-Byte 4-Byte
long 8-Byte 4-Byte
float 4-Byte 4-Byte
double 8-Byte 8-Byte
boolean 1-bit N/A
byte 1-Byte N/A
char 2-Byte 1-Byte
所以在通讯前,需要进行类型转换,对于C定义的unsign char为一个字节存储,对应Java这边用byte存储;对于C定义的int, long, float对应Java用int存储,具体可以参考以上的表。
所以在通讯前,需要进行类型转换,对于C定义的unsign char为一个字节存储,对应Java这边用byte存储;对于C定义的int, long, float对应Java用int存储,具体可以参考以上的表。
2、Socket通讯是按字节传输的(即8个bit位传输),而对于超过一个字节的类型如short 为两个字节,
就存在两种传输入方式,一种是高字节在前传输;一种是高字节在后传输。即Little-Endian和Big-Endian。
Little-Endian和Big-Endian是表示计算机字节顺序的两种格式,所谓的字节顺序指的是长度跨越多个字节的数据的存放形式.
假设从地址0x00000000开始的一个字中保存有数据0x1234abcd,那么在两种不同的内存顺序的机器上从字节的角度去看的话分别表示为:
1)little endian:在内存中的存放顺序是0x00000000-0xcd,0x00000001-0xab,0x00000002-0x34,0x00000003-0x12
2)big endian:在内存中的存放顺序是0x00000000-0x12,0x00000001-0x34,0x00000002-0xab,0x00000003-0xcd
需要特别说明的是,以上假设机器是每个内存单元以8位即一个字节为单位的.
简单的说,ittle endian把低字节存放在内存的低位;而big endian将低字节存放在内存的高位.
现在主流的CPU,intel系列的是采用的little endian的格式存放数据,而motorola系列的CPU采用的是big endian.
网络协议都是Big-Endian的,Java编译的都是Big-Endian的,C编译的程序是与机器相关的,具体是否要进行转换是需要沟通的。假设这里需要转换,以下提供short转的换成字节数组的方式:
public static byte[] ShorttoByteArray(short n) {
byte[] b = new byte[2];
b[1] = (byte) (n & 0xff);
b[0] = (byte) (n >> 8 & 0xff);
return b;
}
public static byte[] toLH(short n) {
byte[] b = new byte[2];
b[0] = (byte) (n & 0xff);
b[1] = (byte) (n >> 8 & 0xff);
return b;
}
其它的类型转换类似,无非是根据类型在判断用几个字节进行存储而已。
3、由于Socket通讯是按字节进行传输的,而在Java中只有byte是一个字节,故可以将其它类型都转换成byte数组来存储,
如:short用两位的字节数组存储,需转换了换以上方法进行,而int用四位的字节数组来存储,对String类型,直接用String.getBytes()来得到它的字节数组。
4、Java的byte与C语言的unsign char虽然都是一个字节存储,但具体的表示内容是不同的,
C的无符号char是取值的范围0--255,而Java中byte取值的范围是-128—127,故在实现C语言的字符串时(C是用char[]来表示字符串的),
Java这边需要进行转换来模仿C语的unsign char,
具体实现函数如下:
// 将有符号的char转换成无符号的char
public static char[] ToUnsignedChar(char[] signChar) {
for (int i = 0; i < signChar.length; i++) {
int x = ((byte) signChar[i]) >= 0 ? signChar[i] : ((byte) signChar[i]) + 256;
signChar[i] = (char) x;
}
return signChar;
}
这里的关键点是当signChar[i] < 0时,即加上256,将其转换到0--255中来。
通过以上四个方面的注意,基本上就可以实现Java与C进行Socket通讯了
[备忘] Java和C之间的通讯
http://www.smithfox.com/?e=119
Java和C之间完全可以用二进制报文通讯, 只要注意几点, 就不必担心 String, Int类的组装和解析问题.
1. 无论什么CPU, Java都会以标准的网络字节序传送数据, 所以C语言也要按网络字节序传送, 需要调用ntohx和htonx系列函数.
2. C语言没有定义int类型的大小, 考虑到现在很多的Server已经是64位, 所以需要用int16_t, int32_t, int64_t来代替shor, int和long long.
3. Java内部的String是Unicode编码, 既不是UTF-8, 也不是ISO-8859-1, 所以不能直接用String传送数据, 需要转换成byte数组, 类似于 this.someString.getBytes("ISO-8859-1");
4. Java内部的char类型也是Unicode编码, 同第三点一样, 所以需要转换成byte传输.
5. 常见数值型数据类型中, Java和 32-bit C所占字节数大多一样, 唯一需要注意的是long类型, Java是64位, 需要用int64_t相对应
6. Java已经提供了相当易用的类将常见数据类型封装为网络字节流, 或是相反, 从网络字节流中解析出数值.
例如, 写:
byte[] buf = new byte[100];
ByteBuffer bytebuf = ByteBuffer.wrap(buf);
bytebuf.putInt(0x10);
//还可以调用其它bytebuf.putXX函数
socket.getOutputStream().write(buf,0,4); //测试, 只写4个字节
例如, 读:
DataInputStream dis = new DataInputStream(socket.getInputStream()); dis.readByte(); dis.readInt(); byte[] recvbuf = new byte[10]; dis.readFully(recvbuf );
最后附一个 Java与 bit-32 C 的数据类型的比较, 以作参考
Type | Java | C |
short | 2 bytes | 2 bytes |
int | 4 bytes | 4 bytes |
long | 8 bytes | 4 bytes |
float | 4 bytes | 4 bytes |
double | 8 bytes | 8 bytes |
boolean | 1 byte | N/A |
byte | 1 byte | N/A |
char | 2 bytes | 1 byte |