原文链接: 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 类更方便,速度也更快。

public static int bytes2Integer(byte[] byteVal) {  

  int result = 0;    

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

int tmpVal = (byteVal[i] << (8 * (3 - i)));    

switch (i) {         

case 0:             tmpVal = tmpVal & 0xFF000000;               break;         

case 1:              tmpVal = tmpVal & 0x00FF0000;            break;        

case 2:          tmpVal = tmpVal & 0x0000FF00;            break;     

case 3:            tmpVal = tmpVal & 0x000000FF;           break;     

}      

result = result | tmpVal;    

}     

return result;   

}

}  

java socket 字节操作的更多相关文章

  1. java socket 基础操作

    服务端: public class Server { public static void main(String[] args) throws Exception { //1.创建一个服务器端Soc ...

  2. Java的IO操作中有面向字节(Byte)和面向字符(Character)两种方式

    解析:Java的IO操作中有面向字节(Byte)和面向字符(Character)两种方式.面向字节的操作为以8位为单位对二进制的数据进行操作,对数据不进行转换,这些类都是InputStream和Out ...

  3. JAVA通信系列一:Java Socket技术总结

    本文是学习java Socket整理的资料,供参考. 1       Socket通信原理 1.1     ISO七层模型 1.2     TCP/IP五层模型 应用层相当于OSI中的会话层,表示层, ...

  4. JAVA Socket 编程学习笔记(一)

    1. Socket 通信简介及模型 Java Socket 可实现客户端--服务器间的双向实时通信.java.net包中定义的两个类socket和ServerSocket,分别用来实现双向连接的cli ...

  5. JAVA Socket超时浅析

    JAVA Socket超时浅析 套接字或插座(socket)是一种软件形式的抽象,用于表达两台机器间一个连接的"终端".针对一个特定的连接,每台机器上都有一个"套接字&q ...

  6. java socket client

    用tornado做了个socket server.无奈联调的人员对接不上. 于是撸出了以下demo import java.io.*; import java.net.*; public class ...

  7. Java Socket网络编程的经典例子(转)

    事实上网络编程简单的理解就是两台计算机相互通讯数据而已,对于程序员而言,去掌握一种编程接口并使用一种编程模型相对就会显得简单的多了,Java SDK提供一些相对简单的Api来完成这些工作.Socket ...

  8. java Socket用法详解(转)

    在客户/服务器通信模式中, 客户端需要主动创建与服务器连接的 Socket(套接字), 服务器端收到了客户端的连接请求, 也会创建与客户连接的 Socket. Socket可看做是通信连接两端的收发器 ...

  9. [ 转载]JAVA Socket超时浅析

    JAVA Socket超时浅析 转载自 http://blog.csdn.net/sureyonder/article/details/5633647 套接字或插座(socket)是一种软件形 式的抽 ...

随机推荐

  1. 免费开源工作流Smartflow-Sharp v2.0

    @font-face { font-family: 宋体 } @font-face { font-family: "Cambria Math" } @font-face { fon ...

  2. Power Designer建模之餐饮在线点评系统——业务处理模型

    餐饮在线点评系统除查看会员促销活动.查看站内消息等简单业务流程外,相对复杂的业务流程包括管理员注册餐厅,发布餐厅信息,餐厅信息主要包括特色菜.促销活动.团购活动和优惠券信息. 餐厅信息发布后,用户可以 ...

  3. LPCTSTR的含义

    LPCTSTR: LP代表指针.C代表不可改变.T代表根据是否定义UNICODE宏而分别define为char或wchar_t.STR代表字符串. 例如: LPCTSTR lp="BMP F ...

  4. Arduino PID Library

    Arduino PID Library by Brett Beauregard,contact: br3ttb@gmail.com What Is PID?   PID是什么 From  Wikipe ...

  5. 【学习笔记/题解】分层图/[JLOI2011]飞行路线

    题目戳我 \(\text{Solution:}\) 关于分层图: 一般用于处理:给你\(k\)次机会对边权进行修改的最短路问题. 算法流程: 建立出\(k\)层图,对应进行\(k\)次操作后的局面. ...

  6. 题解 CF149D

    题目链接 首先,这是一道区间dp题: 首先我们假设 \(l\) ~ \(r\) 是一段合法的区间: 考虑状态,对于一个区间 \(l\) ~ \(r\) 的方案数,我们需要知道方案数,以及 \(l ,r ...

  7. Lane-Detection 近期车道线检测论文阅读总结

    近期阅读的几篇关于车道线检测的论文总结. 1. 车道线检测任务需求分析 1.1 问题分析 针对车道线检测任务,需要明确的问题包括: (1)如何对车道线建模,即用什么方式来表示车道线. 从应用的角度来说 ...

  8. ASP。NET Core Blazor CRUD使用实体框架和Web API

    下载source code - 1.7 MB 介绍 *请查看我的Youtube视频链接来学习ASP.NET Core Blazor CRUD使用实体框架和Web API. 在本文中,我们将了解如何为A ...

  9. SpringCache整合Redis

    之前一篇文章 SpringBoot整合Redis 已经介绍了在SpringBoot中使用redisTemplate手动 操作redis数据库的方法了.其实这个时候我们就已经可以拿redis来做项目了, ...

  10. vue去掉地址栏#号

    mode:'history' 将这代码放入router.js里面