原文链接: http://blog.csdn.net/hslinux/article/details/6214594

java与C++之间进行SOCKET通讯要点简要解析

hslinux

0、篇外语

  此乃本人学习过程中自娱自乐之作,为了遗忘后有个地方再温习。如入您法眼,转载请尊重原作者,请说明出处。

1、big-endian与little-endian

Endian定义:在计算机系统体系结构中用来描述在多字节数中各个字节的存储顺序。

big-endian也称高位在前、大端在前。是计算机体系结构中一种描述多字节存储顺序的术语,在这种机制中最重要字节(MSB)存放在最低端的地址 上。采用这种机制的处理器有Mortolora PowerPC微处理器系列和绝大多数的 RISC处理器。

big-endian 最直观的字节序:

内存地址从左到右与值由低到高的顺序相对应。

little-endian也称低位在前、小端在前。计算机体系结构中一种描述多字节存储顺序的术语,在这种机 制中最不重要字节(LSB)存放在最低端的地 址上。采用这种机制的处理器有Intel x86系列微处理器和一些网络通信设备。该术语除了描述多字节存储顺序外还常常用来描述一个字节中各个比特的排放次序,这里仅讨论多字节存储循序。

little-endian是最符合人的思维的字节序,低与低,高与高一一对应:

地址低位存储值的低位

地址高位存储值的高位

下面举一个例子具体说明big-endian与little-endian:

int  nValue = 0x01020304;

上面的整型nValue有4个字节,其中01为最高位的字节,04为最低位的字节。那么在内存(或文件)中,该值的存储循序为:

内存(或文件)地址:0x12000001  0x12000002  0x12000003  0x12000004

Big-endian         :     01         02           03         04

Little-endian        :     04         03           02         01

如果用一个byte数组来保存的话,也就是如下:

Big-endian模式下: byte  byValue[] = {0x01, 0x02, 0x03, 0x04};

Little-endian模式下:byte  byValue[] = {0x04, 0x03, 0x02, 0x01};

Big-endian或是little-endian的判断:

bool IsLittleEndian()

{

int  i = 1;

char *p = (char*)&i;

if (*p = 1)

return true;   // 小端

else

   return false;  // 大端

}

2、网络字节序与主机字节序

在各种计算机体系结构中,对于字节、字等的存储机制有所不同,因而引发了计算机通信领域中一个很重要的问题,即通信双方交流的信息单元(比特、字节、字、双字等等)应该以什么样的顺序进行传送。如果不达成一致的规则, 通信双方 将无法进行正确的编/译码从而导致通信失败。

通常所说的网络字节序(Network Byte Order)就是遵循big-endian规则。实际通信过程中,通信双方需要把数据按照big-endian编码再通过网络传输。

通常所说的主机字节序(Host Byte Order),与CPU的字节序一致。x86系列主机的字节序都是little-endian桂册。所有little-endian规则主机直接通过网络通讯的时候,需要进行字节序转化。

为了进行转换 bsd socket提供了转换的函数 有下面四个

htons 把unsigned short类型从主机字节序转换到网络字节序

htonl 把unsigned long类型从主机字节序转换到网络字节序

ntohs 把unsigned short类型从网络字节序转换到主机字节序

ntohl 把unsigned long类型从网络字节序转换到主机字节序

在使用little endian的系统中这些函数会把字节序进行转换

在使用big endian类型的系统中这些函数会定义成空宏

3、java字节序

由于Java运行需要自己的虚拟机来支持,所以Java程序所支持的字节序与Java虚拟机一致。Java虚拟机遵循的是big-endian规则。所以可以把Java字节序看作是遵循big-endian规则的主机字节序。

4、Java程序与C++之间的SOCKET通讯

4.1 字节序问题

一直以来都在进行着C++上面的网络开发,发现在C++上面进行通讯的时候,基本上都没有考虑到网络字节序的问题,特别是网络应用中的用户数据。大家都知道网络通讯传输的都是字节流的数据,于是都是定义一个char类型的缓冲区,然后不管int,WORD, DWORD还是其他自定义类型的结构对象也好,都直接memcpy()拷贝到缓冲区,直接发送出去了,根本都没有考虑所传输数据的网络字节序问题。如果非要说一点关注了网络字节序问题的话,那就是有一个地方,大家都回去用到的,也就是网络通讯端口,把端口号赋值给sockaddr_in.sin_port之时大家都会使用了htons(),也就是把端口号从主机字节序转化为网络字节序。

因为这些程序的服务器端也好,客户端也好,都是在x86系列系统下运行,并且都是C++编译出来的,所以不会因为字节序而出现问题。

现在所做项目,涉及到Java与C++之间的SOCKET通讯,这样的情况下,就需要大家都按规则来办事了,C++方面传输的多字节类型数据还请从主机字节序转化成网络字节序再进行传输。

当然,数据是由程序员来组合的,也是由程序员来解析的,所以如果不按标准行事也是可以的。但是就需要在解析数据的时候注意好了。

建议还是按标准行事,把数据转化成网络字节序。

PS:

Java与Windows平台下其他开发语言之间进行数据交与,也需要遵循该原则;

Java下读写Windows平台下其他开发语言保存的数据,或是Windows平台下其他语言读写Java保存的数据,也需要注意字节序问题。

4.2 字节对齐问题

#include <iostream>

using namespace std;

typedef struct tag_S1

{

char s_szValue[8];

char s_cValue;

} S1;

typedef struct tag_S2

{

int  s_nValue1;

char s_szValue[8];

char s_cValue;

int  s_nValue2;

} S2;

typedef struct tag_S3

{

int  s_nValue;

char s_cValue;

} S3;

#pragma pack(push, 1)

typedef struct tag_S4

{

int  s_nValue;

char s_cValue;

} S4;

#pragma pack(pop)

int main(int argc, char* argv[])

{

cout << "sizeof(S1):" << sizeof(S1) << endl;

cout << "sizeof(S2):" << sizeof(S2) << endl;

cout << "sizeof(S3):" << sizeof(S3) << endl;

cout << "sizeof(S4):" << sizeof(S4) << endl;

system("pause");

return 0;

}

上面的程序在WinXP sp3 + VS2008Sp1下运行结果如下:

sizeof(S1):9

sizeof(S2):20

sizeof(S3):8

sizeof(S4):5

请按任意键继续. . .

Win32位平台下的微软C编译器(cl.exe for 80x86)的对齐策略:

1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;

备注:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐模数。

2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);

备注:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。

3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)。

备注:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。

Windows 32位系统下,VC中,默认的字节对齐方式是4字节对齐。

sizeof(S1) :因为结构中数据类型都是char,最宽基本类型大小是1,所以结构大小为9,S1没有进行填充;

sizeof(S2)、sizeof(S3):S2,S3就被填充了一定的字节;

sizeof(S4):因为设置了对齐方式为1字节对齐,所以不会被填充。

在Java与C++进行SOCKET通讯的C++端程序,建议涉及网络通讯的结构使用1字节对齐方式,不然Java端会增加数据处理的复杂度。

4.3 Java与C++之间基本数据类型的差别

需要注意以下几个数据类型的区别(32位系统下):

C++                                  Java

char---------1byte          Byte----------1byte

Char----------2byte2

long---------4bytes         long----------8bytes

注意:

Java中的Char是一个字符,而不是一个字节,与VC的WORD长度一致;

Java中的Byte是一个字节,与C++中的char含义一致,而VC中的BYTE是无符号的char;

Java中的long长度为8,而VC中的long长度为4(C++中short,long的长度跟编译器的实现相关)。

关于Java和C++的socket通信时如何传送结构体

问题是这样的:
有一个socket 程序,一端是c++写的socket 服务程序
另一端是Java写客户端程序,两者之间需要通信。
这个案例中c++接收和发送的都是结构体,而Java是直接发送的字节流或者byte 数组。

解决方法是:c++socket 在发送结构体的时候其实发送的也是字节流。因为结构体本身也是内存中的一块连续数据。问题就变成了如何把结构体手动转成字节的问题了
例如Java客户端要像服务端发送一个这样的结构体
/*c++ code:
* struct FileInfo
{
int sessionId;
int filelenth;//文件长度 ,单位字节
char filename[100];
};
* */    
下面两个方法就可以将是结构体转成字节数组。
/**
* 将int转为低字节在前,高字节在后的byte数组
*/
private 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;
}

private byte[] getFileInfo(String filename,int filesize, int sessionId)
{
byte[] b = new byte[108];        
byte[] temp;
//分别将struct的成员格式为byte数组。        
temp = toLH(sessionId);
System.arraycopy(temp, 0, b, 0, temp.length);
temp = toLH(filesize);
System.arraycopy(temp, 0, b, 4, temp.length);        
System.arraycopy(filename.getBytes(), 0, b, 8, filename.length());
return b;        
}
如果需要用Java 接收结构体的话只需要将上面过程逆过来即可。
另外字节数组直接使用 Java的NIO包中的 ByteBuffer 类更方便,速度也更快。

    1. public class Common {
    2. public static byte[] toLH(int n) {
    3. byte[] b = new byte[4];
    4. b[0] = (byte) (n & 0xff);
    5. b[1] = (byte) (n >> 8 & 0xff);
    6. b[2] = (byte) (n >> 16 & 0xff);
    7. b[3] = (byte) (n >> 24 & 0xff);
    8. return b;
    9. }
    10. public static int bytes2Integer(byte[] byteVal) {
    11. int result = 0;
    12. for (int i = 0; i < byteVal.length; i++) {
    13. int tmpVal = (byteVal[i] << (8 * (3 - i)));
    14. switch (i) {
    15. case 0:
    16. tmpVal = tmpVal & 0xFF000000;
    17. break;
    18. case 1:
    19. tmpVal = tmpVal & 0x00FF0000;
    20. break;
    21. case 2:
    22. tmpVal = tmpVal & 0x0000FF00;
    23. break;
    24. case 3:
    25. tmpVal = tmpVal & 0x000000FF;
    26. break;
    27. }
    28. result = result | tmpVal;
    29. }
    30. return result;
    31. }
    32. }

java与C++之间进行SOCKET通讯要点简要解析的更多相关文章

  1. Java与C之间的socket通信

    最近正在开发一个基于指纹的音乐检索应用,算法部分已经完成,所以尝试做一个Android App.Android与服务器通信通常采用HTTP通信方式和Socket通信方式.由于对web服务器编程了解较少 ...

  2. Socket网络通讯开发总结之:Java 与 C进行Socket通讯 + [备忘] Java和C之间的通讯

    Socket网络通讯开发总结之:Java 与 C进行Socket通讯 http://blog.sina.com.cn/s/blog_55934df80100i55l.html (2010-04-08 ...

  3. java socket通讯(二)处理多个客户端连接

    通过java socket通讯(一) 入门示例,就可以实现服务端和客户端的socket通讯,但是上一个例子只能实现一个服务端和一个客户端之间的通讯,如果有多个客户端连接服务端,则需要通过多线程技术来实 ...

  4. Java Socket通讯---网络基础

    java socket 通讯 参考慕课网:http://www.imooc.com/learn/161 一.网络基础知识 1.1 通讯示意图 1.2 TCP/IP协议 TCP/IP是世界上应用最为广泛 ...

  5. java socket通讯(一) 入门示例

    一.入门 要想学习socket通讯,首先得知道tcp/ip和udp连接,具体可参考浅谈TCP/IP 和 UDP的区别 二.示例 首先新建了一个java工程,包括两个部分,客户端SocketClient ...

  6. java和C#之间SOCKET通信的问题

    转自:http://www.cdtarena.com/javapx/201307/9170.html java和C#之间SOCKET通信的问题 一.服务器端(使用java编写) /** * 监听客户端 ...

  7. Socket通讯-Netty框架实现Java通讯

    Netty简介 Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序. 也就是说,Netty ...

  8. Socket网络通讯开发总结之:Java 与 C进行Socket通讯(转)

    先交待一下业务应用背景:服务端:移动交费系统:基于C语言的Unix系统客户端:增值服务系统:基于Java的软件系统通迅协议:采用TCP/IP协议,使用TCP以异步方式接入数据传输:基于Socket流的 ...

  9. JAVA与VB通过SOCKET通讯

    JAVA与VB通过SOCKET通讯 在做项目的过程中,本来是想使用JAVA的comm.jar工具实现串口通讯,不知道怎么回事,总是取不到电脑的串口.所以,改为现在的这种模式:java通过socket给 ...

随机推荐

  1. Discuz常见小问题-如何关闭验证码

    进入后台,在防灌水,验证设置中可以切换哪些情况下是否使用验证码 如果启用验证码,也客户修改验证码的难度,样式.最后点击提交,完成之后可以退出到前台,测试是否能够不用验证码自动登录

  2. Mac WIn7 QQ聊天记录互导 聊天记录合并

    也许等哪天老了回过头来看看.说不定还有一丝欢乐. 有几个方法可以实现 一.dropbox数据同步 二.QQ会员 三.下面方法 1.因为现在的Mac QQ还不支持聊天记录的导入导出.所以只能手动了 如果 ...

  3. PHP Warning: DOMDocument::loadHTML(): htmlParseEntityRef: expecting ';' in Entity,

    $html = file_get_contents("http://www.somesite.com/"); $dom = new DOMDocument(); $dom-> ...

  4. 磁盘I/O的性能评估方法

    磁盘I/O的性能评估方法 http://blog.synology.com/blog/?p=2086 通常,我们很容易观察到数据库服务器的内存和CPU压力.但是对I/O压力没有直观的判断方法.磁盘有两 ...

  5. MySql【Insert Select Not Exist】判断记录再添加值的方案

    INSERT INTO content ( detail, status, beginTime, endTime) SELECT @detail, , NULL, NULL FROM DUAL WHE ...

  6. 017-通过govendor管理依赖包

    1:安装 go get -u github.com/kardianos/govendor 2:配置环境变量 需要把 $GOPATH/bin/ 加到 PATH 中 D:\my_workspace\go_ ...

  7. Dicom格式文件解析器[转]

    Dicom格式文件解析器   Dicom全称是医学数字图像与通讯,这里讲的暂不涉及通讯那方面的问题 只讲*.dcm 也就是diocm格式文件的读取,读取本身是没啥难度的 无非就是字节码数据流处理.只不 ...

  8. Web服务器讲解与JavaWeb应用部署(本机,以Tomcat为例)

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6042290.html 在讨论Web系统发布之前,我们先来辨析两个概念:服务器.Web服务器. 通常,我们说的服 ...

  9. CentOS 7 安装php5.6,Nginx,Memcached环境及配置

    安装php5.6版本以后不再需要安装Zend Guard,而是用yum命令安装php-opcache及php-pecl-apcu就可以有效的提高php执行速度. 1. 配置yum源 事先确认yum源的 ...

  10. V-rep学习笔记:机器人模型创建4—定义模型

    完成之前的操作后终于来到最后一步——定义模型,即将之前创建的几何体.关节等元素按层级关系组织成为一个整体. 将最后一个连杆robot_link_dyn6拖放到相应的关节(robot_joint6)下, ...