首先描述一下场景,在用java做服务端的情况下,正确的获取包头(即报文长度), 我当时听了一位比较有经验的同事的疑问,说银行用C的情况下有可能获取的这个长度(int或short或long)有可能高低位不同(比如包头应该是0402,实际上我们收到的是2040),需要对这个做一个特殊处理,鉴于该同事在银行工作时间很长,所以带着一些问题看了下这方面的资料,发现并肯定,不会产生这种问题。
主要是C在不同CPU下存储机制的方式不同==》
大端模式与小端模式
一、概念及详解
在各种体系的计算机中通常采用的字节存储机制主要有两种: big-endian和little-endian,即大端模式和小端模式。
两个关键词,MSB和LSB:
MSB:Most Significant Bit ------- 最高有效位
LSB:Least Significant Bit ------- 最低有效位
大端模式(big-edian)
big-endian:MSB存放在最低端的地址上。
举例,双字节数0x1234以big-endian的方式存在起始地址0x00002000中:
| data |<-- address
| 0x12 |<-- 0x00002000
| 0x34 |<-- 0x00002001
在Big-Endian中,对于bit序列中的序号编排方式如下(以双字节数0x8B8A为例):
bit | 0 1 2 3 4 5 6 7 | 8 9 10 11 12 13 14 15
------MSB----------------------------------LSB
val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |
+--------------------------------------------+
= 0x8 B 8 A
小端模式(little-endian)
little-endian:LSB存放在最低端的地址上。
举例,双字节数0x1234以little-endian的方式存在起始地址0x00002000中:
| data |<-- address
| 0x34 |<-- 0x00002000
| 0x12 |<-- 0x00002001
在Little-Endian中,对于bit序列中的序号编排和Big-Endian刚好相反,其方式如下(以双字节数0x8B8A为例):
bit | 15 14 13 12 11 10 9 8 | 7 6 5 4 3 2 1 0
------MSB-----------------------------------LSB
val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |
+---------------------------------------------+
= 0x8 B 8 A
二、数组在大端小端情况下的存储:
以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value:
Big-Endian: 低地址存放高位,如下:
高地址
---------------
buf[3] (0x78) -- 低位
buf[2] (0x56)
buf[1] (0x34)
buf[0] (0x12) -- 高位
---------------
低地址
Little-Endian: 低地址存放低位,如下:
高地址
---------------
buf[3] (0x12) -- 高位
buf[2] (0x34)
buf[1] (0x56)
buf[0] (0x78) -- 低位
--------------
低地址
三、大端小端转换方法:
Big-Endian转换成Little-Endian如下:
#define BigtoLittle16(A) ((((uint16)(A) & 0xff00) >>
| \
(((uint16)(A) & 0x00ff) <<
)
#define BigtoLittle32(A) ((((uint32)(A) & 0xff000000) >> 24) | \
(((uint32)(A) & 0x00ff0000) >>
| \
(((uint32)(A) & 0x0000ff00) <<
| \
(((uint32)(A) & 0x000000ff) << 24))
四、大端小端检测方法:
如何检查处理器是big-endian还是little-endian?
联合体union的存放顺序是所有成员都从低地址开始存放,利用该特性就可以轻松地获得了CPU对内存采用Little-endian还是Big-endian模式读写。
int checkCPUendian()
{
union
{
unsigned int a;
unsigned char b;
}c;
c.a = 1;
return (c.b == 1);
}
/*return 1 : little-endian, return 0:big-endian*/
网络字节顺序
1、字节内的比特位不受这种顺序的影响
比如一个字节 1000 0000 (或表示为十六进制 80H)不管是什么顺序其内存中的表示法都是这样。
2、大于1个字节的数据类型才有字节顺序问题
比如 Byte A,这个变量只有一个字节的长度,所以根据上一条没有字节顺序问题。所以字节顺序是“字节之间的相对顺序”的意思。
3、大于1个字节的数据类型的字节顺序有两种
比如 short B,这是一个两字节的数据类型,这时就有字节之间的相对顺序问题了。
网络字节顺序是“所见即所得”的顺序。而Intel类型的CPU的字节顺序与此相反。
比如上面的 short B=0102H(十六进制,每两位表示一个字节的宽度)。所见到的是“0102”,按一般数学常识,数轴从左到右的方向增加,即内存地址从左到右增加的话,在内存中这个 short B的字节顺序是:
01 02
这就是网络字节顺序。所见到的顺序和在内存中的顺序是一致的!
而相反的字节顺序就不同了,其在内存中的顺序为:02 01
假设通过抓包得到网络数据的两个字节流为:01 02
如果这表示两个 Byte类型的变量,那么自然不需要考虑字节顺序的问题。
如果这表示一个 short 变量,那么就需要考虑字节顺序问题。根据网络字节顺序“所见即所得”的规则,这个变量的值就是:0102
假设本地主机是Intel类型的,那么要表示这个变量,有点麻烦:
定义变量 short X,
字节流地址为:pt,按顺序读取内存是为
x=*((short*)pt);
那么X的内存顺序当然是 01 02
按非“所见即所得”的规则,这个内存顺序和看到的一样显然是不对的,所以要把这两个字节的位置调换。
调换的方法可以自己定义,但用已经有的API还是更为方便。
网络字节顺序与主机字节顺序NBO与HBO
网络字节顺序NBO(Network Byte Order):按从高到低的顺序存储,在网络上使用统一的网络字节顺序,可以避免兼容性问题。
主机字节顺序(HBO,Host Byte Order):不同的机器HBO不相同,与CPU设计有关计算机数据存储有两种字节优先顺序:高位字节优先和低位字节优先。
Internet上数据以高位字节优先顺序在网络上传输,所以对于在内部是以低位字节优先方式存储数据的机器,在Internet上传输数据时就需要进行转换。
相关网络函数
htonl()
简述:
将主机的无符号长整形数转换成网络字节顺序。
#include <winsock.h>
u_long PASCAL FAR htonl( u_long hostlong);
hostlong:主机字节顺序表达的32位数。
注释:
本函数将一个32位数从主机字节顺序转换成网络字节顺序。
返回值:
htonl()返回一个网络字节顺序的值。
inet_ntoa()
简述:
将网络地址转换成“.”点隔的字符串格式。
#include <winsock.h>
char FAR* PASCAL FAR inet_ntoa( struct in_addr in);
in:一个表示Internet主机地址的结构。
注释:
本函数将一个用in参数所表示的Internet地址结构转换成以“.” 间隔的诸如“a.b.c.d”的字符串形式。请注意inet_ntoa()返回的字符串存放在WINDOWS套接口实现所分配的内存中。应用程序不应假设该内存是如何分配的。在同一个线程的下一个WINDOWS套接口调用前,数据将保证是有效。
返回值:
若无错误发生,inet_ntoa()返回一个字符指针。否则的话,返回NULL。其中的数据应在下一个WINDOWS套接口调用前复制出来。
网络中传输的数据有的和本地字节存储顺序一致,而有的则截然不同,为了数据的一致性,就要把本地的数据转换成网络上使用的格式,然后发送出去,接收的时候也是一样的,经过转换然后才去使用这些数据,基本的库函数中提供了这样的可以进行字节转换的函数,如和htons() htonl() ntohs() ntohl(),这里n表示network,h表示host,htons() htonl()用于本地字节向网络字节转换的场合,s表示short,即对2字节操作,l表示long即对4字节操作。同样ntohs()ntohl()用于网络字节向本地格式转换的场合。
inet_ntoa() 将网络字节序地址转换成“.”点隔的字符串格式
inet_addr() 将“.”点隔字符串地址转换成网络字节序地址
htonl() 将主机字节序转换为网络字节序 long
ntohl() 将网络字节序转换为主机字节序 long
htons() 将主机字节序转换为网络字节序 short
ntohs() 将网络字节序转换为主机字节序 short
intel cpu的主机数据是小端存储模式
网络传输是大端存储模式
小端存储模式 0x1234H 34H存在低地址,12H存在高地址
大端存储模式 0x1234H 34H存在高地址,12H存在低地址
因为给java的数据都是byte流直接过来的, 一般我们采取将字节流的byte转化为string,然后通过这个string直接获取其数字类型,而不是通过byte直接转换为数字类型,所以不会出现这种高低位相反的问题
分享到:
相关推荐
在本场景中,我们关注的是C#作为服务器端,Android作为客户端的Socket通信实现。这种通信方式常用于移动应用与后台服务之间的实时数据交互,如即时消息、位置更新、文件传输等。 一、C# Socket服务器端实现 1. 引用...
本教程将深入讲解C#中的Socket通信,包括客户端和服务器端的实现,以及如何通过源码学习相关知识。 首先,我们要理解Socket的基本概念。Socket,也称为套接字,是网络通信中的一个接口,它允许应用程序通过网络发送...
pc端通过socket通信,实现和安卓智能终端的数据交互
博客:https://blog.csdn.net/u014261855/article/details/109583060 unity c#实现简单前后端socket通信框架 .net dll工程,封装三个工具: 1.服务器 2.客户端 3.通用byte流客户端
Socket通信通常基于TCP或UDP协议,TCP保证数据传输的可靠性,而UDP则更注重速度但不保证数据顺序。 2. **TCP Socket通信** 在C#中,我们使用`TcpClient`和`TcpListener`类来实现TCP Socket通信。`TcpClient`用于...
- **套接字(Socket)**: 是通信的两端,可以是服务器端也可以是客户端。每个套接字都有一个唯一的标识符,称为套接字描述符。 - **TCP(Transmission Control Protocol)与UDP(User Datagram Protocol)**: 是两...
"Android+PC Socket通信"指的是通过TCP Socket协议实现Android设备(手机客户端)与个人计算机之间的数据传输。Socket通信允许应用程序之间建立端到端的连接,进行双向数据交换,非常适合用于实时通讯、文件传输等...
在Android应用开发中,手机客户端与服务器之间的通信是至关重要的,而Socket通信提供了一种可靠的、基于连接的数据传输方式。本文将深入探讨Android客户端如何利用Socket进行与服务器的交互。 一、Socket基础知识 ...
在Socket通信中,TCP协议确保了数据的顺序传输和错误检查,保证了文件传输的完整性和可靠性。 3. **Socket通信步骤**: - **创建Socket**:服务器端首先创建一个ServerSocket,监听特定的端口号,等待客户端的连接...
在MATLAB环境中实现Socket通信是将强大的MATLAB计算能力与网络通信功能相结合的重要技术。Socket是一种网络编程接口,它允许程序之间通过网络进行双向通信。MATLAB提供了丰富的工具和函数来支持Socket通信,使得用户...
Socket 通信异常是指在网络通信过程中出现的各种错误和异常,包括网络连接断开、服务器端或客户端程序异常、网络链路异常等。这些异常会导致 Socket 连接断开,影响系统的稳定性和可靠性。下面是解决 Socket 通信...
### 客户端与服务器端的Socket通信:深入解析与实践 #### 引言 在当今高度互联的世界中,客户端与服务器端的通信是互联网应用的核心。本文将深入探讨Socket通信的基本原理及其在客户端与服务器端的应用,尤其关注...
在Android开发中,Socket通信是实现设备间网络通信的一种常见方式...以上就是关于Android开发中Socket Client端和Socket Server端数据发送和接收的核心知识点。理解并掌握这些内容,将有助于构建稳定的跨设备通信系统。
SOCKET通信调试工具是一款专为开发者设计的实用辅助软件,旨在简化SOCKET通信的调试过程。在编程领域,SOCKET是网络通信的基础,它允许不同设备间的程序通过互联网进行数据交换。这款工具允许用户在本地环境中模拟...
在这个场景中,我们将讨论如何实现PC(个人计算机)与开发板之间的通信,特别关注使用Socket进行TCP/IP连接的情况,同时,PC端还具备图形用户界面(GUI)。让我们深入探讨这个主题。 首先,Socket是应用层与传输层...
Socket通信是计算机网络编程中的重要组成部分,主要用于实现不同设备间进程间的通信。在这个主题中,我们将深入探讨Socket的工作原理、服务器与客户端的交互过程以及如何使用Socket进行编程。 首先,Socket可以理解...
在这个"**c# Socket通信源码**"中,我们主要会看到客户端和服务器端的实现,这有助于理解如何在C#环境下使用Socket进行网络通信。 首先,让我们深入了解一下Socket的基本概念。Socket是网络上的进程间通信(IPC)的...
- 发送端:构建数据包时,先写入长度头,再写入实际数据,然后使用`Socket.Send`发送。 - 接收端:首先接收长度头,转换为整数,然后循环接收数据直到达到指定长度,完成一个数据包的接收。 5. **其他解决方案** ...
Java实现的简单Socket通信是网络编程中的基础技术,主要用于设备间的双向数据传输。在这个场景中,我们有两个窗口,一个作为服务器端,另一个作为客户端,它们通过TCP协议进行通信。TCP是一种面向连接的、可靠的传输...