linux socket 字节序 大小端检测
socket(通过文件描述符操作)介绍:一系列的API完成协议的封装和分用
所谓socket(套接字),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议根进行交互的接口。
socket可以看成是两个网络应用程序进行通信时,各自通信连接中的端点,这是一个逻辑上的概念。它是网络环境中进程间通信的 API ,也是可以被命名和寻址的通信端点,使用中的每一个套接字都有其类型和一个与之相连进程。通信时其中一个网络应用程序将要传输的一段信息写入它所在的主机socket中,该socket通过与网络接口卡(NIC)相连的传输介质将这段信息送到另外一台主机的 socket 中,使对方能够接收到这段信息。socket 是由 IP 地址和端口结合的,提供向应用层进程传送数据包的机制。
socket本身有 “插座” 的意思,在 Linux 环境下,用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。既然是文件,那么理所当然的,我们可以使用文件描述符引用套接字。与管道类似的,Linux 系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接字多应用于网络进程间数据的传递。
socket在源机器和目的机器都有各自有 读写缓冲区,内部发送方的封装数据和 接收方的分用数据 实现缓冲区的数据通信。
套接字通信分两部分:
- 服务器端:被动接受连接,一般不会主动发起连接
- 客户端:主动向服务器发起连接
socket是一套通信的接口,Linux 和 Windows 都有,但是有一些细微的差别。
字节序:
现代CPU的累加器一次都能装载(至少)4字节(这里为32位机),即一个整数。那么这4字节在内存中排列的顺序将影响它被累加器装载成的整数的值,这就是字节序的问题。
在各种计算机体系结构中,对于字节、字等的存储机制有所不同,因而引发了计算机通信领域中一个很重要的问题,即通信双方交流的信息单元(比特、字节、字、双字等等)应该以什么样的顺序进行传送。如果不达成一致的规则,通信双方将无法进行正确的编码/译码从而导致通信失败。
字节序:顾名思义 字节的顺序,就是大于一个字节类型的数据在内存中存放顺序(一个字节的数据当然就无需谈顺序的问题了)
字节序分为大端字节序(Big-Endian)和小端字节序(Little-Endian)。大端字节序是指一个整数的最高位字节(23 ~ 31 bit)存储在内存的低地址处,低位字节(0 ~ 7 bit)存储在内存的高地址处;小端字节序则是指整数的高位字节存储在内存的高地址处,而低位字节则存储在内存的低地址处。
字节序举例:
⚪小端字节序(大多计算机使用小端字节序):
0x 01020304(4个字节)
内存的方向 -------->
内存的低位 --------> 内存的高位
04 03 02 01
0x 11 22 33 44 12 34 56 78
⚪大端字节序:
0x 12 34 56 78 11 22 33 44
通过代码测试本机字节序:
1 /* 2 字节序:字节在内存中存储的顺序 3 小端字节序:数据的高位字节存储在内存的高位地址,低位字节存储在内存的低位地址 4 大端字节序:数据的低位字节存储在内存的高位地址,高位字节存储在内存的低位地址 5 */ 6 7 //通过代码检测当前主机的字节序 8 #include <stdio.h> 9 int main() 10 { 11 union 12 { 13 short value; //2字节 14 char bytes[sizeof(short)];// char[2] 15 16 }test;//定义一个联合体 起名为 test 17 test.value = 0x0102; 18 if((test.bytes[0] == 1) && (test.bytes[1] == 2)) 19 { 20 printf("大端字节序\n"); 21 } 22 else if((test.bytes[0] == 2) && (test.bytes[1] == 1)) 23 { 24 printf("小端字节序\n"); 25 } 26 else 27 { 28 printf("未知\n"); 29 } 30 return 0; 31 }
字节序转换函数:
当格式化的数据在两台使用不同字节序的主机之间直接传递时,接收端必然错误的解释。 解决问题的方法是:发送端总是把要发送的数据转换成大端字节序数据后再发送,而接收端知道对方传送过来的数据总是采用大端字节序,所以接收端可以根据自身采用的字节序决定是否对接收到的数据进行转换(小端机转换,大端机不转换)。
网络字节顺序是 TCP/IP 中规定好的一种数据表示格式,它与具体的 CPU 类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释,网络字节顺序采用大端排序方式。
BSD Socket 提供了封装好的转换接口,方便程序员使用。包括从主机字节序到网络字节序的转换函数: htons、htonl;从网络字节序到主机字节序的转换函数:ntohs、ntohl。
1 /* 2 h - host 主机,主机字节序 3 to - 转换成什么 4 n - network 网络字节序 5 s - short unsigned short 6 l - long unsigned int 7 8 网络通信时,需要将主机字节序转换成网络字节序(大端),另外一端获取到数据以后根据情况将网络字节序转换成主机字节序 9 #include <arpa/inet.h> 10 //转换端口 11 uint16_t htons(uint16_t hostshort); //主机字节序 - 网络字节序 12 uint16_t ntohs(uint16_t netshort); //网络字节序 - 主机字节序 13 14 //转IP 15 uint32_t htonl(uint32_t hostlong); //主机字节序 - 网络字节序 16 uint32_t ntohl(uint32_t netlong); //网络字节序 - 主机字节序 17 */ 18 #include <stdio.h> 19 #include <arpa/inet.h> 20 int main() 21 { 22 //htons 转换端口 23 unsigned short a = 0x0102;//2 bytes 地址没有负数 使用 unsigned 24 printf("a:%x\n",a);//a:102 25 unsigned short b = htons(a); 26 printf("b:%x\n",b);//b:201 %x:十六进制打印 27 printf("========================\n"); 28 29 //htonl 转换IP 30 char buf[4] = {192,168,1,100}; 31 int num = *(int *)buf;//buf是一个地址 强转为int * 类型 后 取值 32 int sum = htonl(num); 33 unsigned char *p = (char * )∑ 34 printf("%d %d %d %d\n",*p,*(p+1),*(p+2),*(p+3));// 100 1 168 192 35 printf("========================\n"); 36 37 //ntohl 38 unsigned char buf1[4] = {1, 1, 168, 192};//本机存储是小端 39 int num1 = *(int *)buf1; 40 int sum1 = ntohl(num1); 41 unsigned char * p1 = (unsigned char *)&sum1; 42 printf("%d %d %d %d\n",*p1,*(p1+1),*(p1+2),*(p1+3));//192 168 1 1 43 printf("========================\n"); 44 //ntohs 45 unsigned short c = 0x0201; 46 printf("c:%x\n",c);//c:201 47 unsigned short d = ntohs(c); 48 printf("d:%x\n",d);//d:102 49 return 0; 50 }
字节序检测代码:
#include <iostream> using namespace std; int main() { int a = 0x1234; //由于int和char的长度不同,借助int型转换成char型,只会留下低地址的部分 char c = (char)(a); if (c == 0x12) cout << "big endian" << endl; else if (c == 0x34) cout << "little endian" << endl; }
联合体检测代码:
1 #include <iostream> 2 using namespace std; 3 //union联合体的重叠式存储,endian联合体占用内存的空间为每个成员字节长度的最大值 4 union endian 5 { 6 int a; 7 char ch; 8 }; 9 int main() 10 { 11 endian value; 12 value.a = 0x1234;//0x十六进制--> 十进制 4660 13 cout << value.a << endl;//4660 14 cout << value.ch << endl;//4-----000100 15 //a和ch共用4字节的内存空间 16 if (value.ch == 0x12)//18 16 2-----010010 17 cout << "big endian" << endl; 18 else if (value.ch == 0x34)//52 32 16 4-----110100 19 cout << "little endian" << endl; 20 system("pause"); 21 return 0; 22 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)