java socket 字节操作
原文链接: 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 字节操作的更多相关文章
- java socket 基础操作
服务端: public class Server { public static void main(String[] args) throws Exception { //1.创建一个服务器端Soc ...
- Java的IO操作中有面向字节(Byte)和面向字符(Character)两种方式
解析:Java的IO操作中有面向字节(Byte)和面向字符(Character)两种方式.面向字节的操作为以8位为单位对二进制的数据进行操作,对数据不进行转换,这些类都是InputStream和Out ...
- JAVA通信系列一:Java Socket技术总结
本文是学习java Socket整理的资料,供参考. 1 Socket通信原理 1.1 ISO七层模型 1.2 TCP/IP五层模型 应用层相当于OSI中的会话层,表示层, ...
- JAVA Socket 编程学习笔记(一)
1. Socket 通信简介及模型 Java Socket 可实现客户端--服务器间的双向实时通信.java.net包中定义的两个类socket和ServerSocket,分别用来实现双向连接的cli ...
- JAVA Socket超时浅析
JAVA Socket超时浅析 套接字或插座(socket)是一种软件形式的抽象,用于表达两台机器间一个连接的"终端".针对一个特定的连接,每台机器上都有一个"套接字&q ...
- java socket client
用tornado做了个socket server.无奈联调的人员对接不上. 于是撸出了以下demo import java.io.*; import java.net.*; public class ...
- Java Socket网络编程的经典例子(转)
事实上网络编程简单的理解就是两台计算机相互通讯数据而已,对于程序员而言,去掌握一种编程接口并使用一种编程模型相对就会显得简单的多了,Java SDK提供一些相对简单的Api来完成这些工作.Socket ...
- java Socket用法详解(转)
在客户/服务器通信模式中, 客户端需要主动创建与服务器连接的 Socket(套接字), 服务器端收到了客户端的连接请求, 也会创建与客户连接的 Socket. Socket可看做是通信连接两端的收发器 ...
- [ 转载]JAVA Socket超时浅析
JAVA Socket超时浅析 转载自 http://blog.csdn.net/sureyonder/article/details/5633647 套接字或插座(socket)是一种软件形 式的抽 ...
随机推荐
- Python练习题 005:三个数字由大到小排序输出
[Python练习题 005]输入三个整数x,y,z,请把这三个数由小到大输出. ----------------------------------------------------------- ...
- 01 AS 首次编译执行项目过程中遇到的几个常见问题
问题01 as打开时出现The environment variable JAVA_HOME (with The value of C:\Java\jdk1.8.0_101\bin) does not ...
- Eclipse 设置Tab键为4个空格
参考:java编程规范之eclipse设置tab键为四个空格 Eclipse版本 1.点击菜单Window > Preference 2.在Preferences窗口中,展开General &g ...
- 主厨(第4部分)- ASP. netNET Core和Angular 2 CRUD SPA
下载source - 79.7 KB 介绍 在Master Chef(第1部分)和Master Chef(第2部分)中,我介绍了如何使用ASP.Net Core和Angular JS.在Master ...
- K8S-kubeadm-集群证书续签
ETCD证书 自签证书颁发机构(CA) ca.crt ca.key etcd集群中相互通信事业的客户端证书 peer.crt peer.key pod中定义Liveness探针事业的客户端证书 hea ...
- Python装饰器实现带参数和不带参数
1 def log(text=None): 2 3 if isinstance(text, str): 4 def decorator(func): 5 @functools.wraps(func) ...
- JMeter实战(一) 体系结构
此为开篇,介绍JMeter的组成结构,阅读后对JMeter形成整体认知和初步印象. 为了便于后续讲解,先明确下2个术语. 元件:如HTTP请求.事务控制器.响应断言,就是一个元件. 组件:如逻辑控制器 ...
- 收集的照片信息都是Excel超链接?批量命名很困难?来试试这个自制的下载器吧!
项目背景 作为大学的一名班委,经常要制作各种表格.统计各种信息,成为一名合格的"表哥"是一门必修课.其实Excel的文字信息和数字信息的统计和处理还并不算难题,很多信息可以通过问卷 ...
- git检出某文件的指定版本
比如当时文件所处的版本id是27e6266d86de3e6da6e1e7a8c43a8b51d6a87032 文件名是system/models/waimai/huodongdiscount.mdl. ...
- 文件上传-阿里云OSS-存储文件
JS上传文件到阿里云OSS OSS支持流式写入和读出.特别适合视频等大文件的边写边读业务场景. 注意在OSS的控制台:跨域管理中设置允许的方法 <script> var client = ...