网络编程(字节序)

文章详细介绍了字节序的概念,包括小端和大端存储方式,以及如何通过共用体检测本地字节序。还讨论了本地字节序与网络字节序的区别,提供了字节序转换函数如htons和ntohl,并涉及结构体对齐、类型长度和IP地址在网络通信中的转换方法,包括inet_aton、inet_pton和inet_ntoa等函数的使用。
摘要由CSDN通过智能技术生成

一、字节序的概念

字节序是指不同类型CPU主机,内存存储 多字节整数 序列的方式。

float, char, 字符串均没有字节序的说法

short, int , long , long long 有字节序的说法

小端字节序:低字节存储在低地址,高字节存储在高地址

大端字节序:低字节存储在高地址,高字节存储在低地址。

ps :

1. 取地址,获取的都是变量的首地址,即获取的是变量的最低的地址。

2. 内存中的数据读取,都是从低地址往高地址读取,读取完毕后经过大小端转换得到结果。

用共用体(联合体)的方式,判断本机的字节序?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
union
{
    unsigned int a;
    char b;
}s;
int main(int argc, const char *argv[])
{
    /*
       unsigned int a = 0x87654321;
       int* pa = &a;
       char b = 0;
       b = a;
       printf("%#x\n",*pa);
       printf("%#x\n",b);
       */
    /*
       unsigned int a =0x87654321;
       char *pa = (char*)&a;
       printf("%#x\n",*pa);
       if(0x21 == *pa)
       printf("这是一个小端字节序\n");
       else if(0x87 == *pa)
       printf("这是一个大端字节序\n");
       */
    s.a=0x87654321;
    printf("%#x\n",s.b);
    if(0x21 == s.b)
        printf("这是一个小端字节序\n");
    else if(0x87 == s.b)
        printf("这是一个大端字节序\n");

    return 0;
}
#include <stdio.h>

union t
{
    int a;
    char b;
};

int main(int argc, const char *argv[])
{
    unsigned int a = 0x87654321;
    char *pa = (char*)&a;

    if(0x21 == *pa)
        printf("这是一个小端\n");
    else if(0x87 == *pa)
        printf("这是一个大端\n");


    union t text;
    text.a = 1;                        
    if(1 == text.b)
        printf("这是一个小端\n");
    else if(0 == text.b)
        printf("这是一个大端\n");

    return 0;
}

 二、本地字节序与网络字节序

本地字节数:主机字节序(Host Byte Order) HBO

网络字节序(Network Byte Order) NBO,网络字节序规定使用大端字节序

在跨主机传输过程中,需要使用统一的字节序,即网络字节序,避免兼容性问题。

三、字节序转换函数

1)htons htonl 主机字节序-->网络字节序

头文件:
#include <arpa/inet.h>
​uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
参数:指定要转换成网络字节序的整型:分别是32bit和16bit;
返回值:成功,返回转换后网络字节序的整型
#include <stdio.h>
#include <arpa/inet.h>
​
int main(int argc, const char *argv[])
{
    unsigned int a = 0x87654321;
    printf("%#x\n", a);             //0x87654321
    printf("%#x\n", htonl(a));      //0x21436587
​
    printf("%#x\n", htons(a));      //0x2143                 
    
    return 0;
}   

2)ntohs ntohl 网络字节序---->主机字节序

头文件:
#include <arpa/inet.h>
原型:
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
参数:
uint32_t hostlong:32位网络字节序整型;
uint16_t hostshort:16位网络字节序整型;
返回值:成功,返回转换成主机字节序的整型;

四、结构体对齐

编译器会对结构体进行对齐,加速CPU取值周期,由于数据对齐也是与操作系统相关,不同的主机如果使用不同的对齐方式,会导致数据无法解析。

所以网络传输结构体的时候需要取消结构体对齐;

#include <stdio.h>
#pragma pack(1)         //设置默认对齐系数 :()中的参数只能填2^n (n=0,1,2,3,4,5......)  

typedef struct
{
    char a;     //1
    int b;      //4
    int d;      //4
}_A;
​
#pragma pack()      //重置默认对其系数,重新置为8
​
typedef struct
{
    char a;     //1
    int b;      //4
    int d;      //4
} __attribute__((packed))  B;       //取消结构体对齐
​
​
typedef struct
{   
    char a;     //1
                //3
    int b;      //4
    int d;      //4
}_C;
​
int main(int argc, const char *argv[])
{   
    printf("%ld\n", sizeof(_A));    //9
    printf("%ld\n", sizeof(_B));    //9
    printf("%ld\n", sizeof(_C));    //12
​
    
    return 0;
}

五、类型长度 

int long int不同操作系统这两个数据类型所占的字节数可能是不一样的

解决方式:可以通过通用类型:uint8_t uint16_t uint32_t

因为涉及到跨平台,不同平台会有不同的字长

#include <stdint.h>
​
typedef struct
{
    uint8_t a;      //1
    uint32_t b;      //4
    uint16_t d;      //2
}_A;

六、IP转换

由于IP地址本质上是一个4个字节的无符号整数,所以在跨主机传输中也有字节序的概念。

所以需要将IP地址转换成网络字节序。

"192.168.8.189" ---->本机字节序的整型 0xC0A808BD---->网络字节序0xBD08A8C0

"192.168.31.42"----> 0xC0A81F2A ---->0x2A1FA8C0

6.1点分十进制--->网络字节序

1)inet_aton
头文件:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
原型:
int inet_aton(const char *cp, struct in_addr *inp);
参数:
char *cp:源IP地址的点分十进制字符串,例如 “192.168.1.10”;
struct in_addr *inp:存储转换成网络字节序的IP;
           typedef uint32_t in_addr_t;
           struct in_addr {
               in_addr_t s_addr;
           };
返回值:
成功,返回非0;
失败,返回0;
​只能转换IPv4
例子:
 #define IP  "192.168.1.10"      //0xC0A8010A
 int main(int argc, const char *argv[])
 {
     struct in_addr inp;
 
     if(inet_aton(IP, &inp) == 0)
     {
         printf("转换失败\n");
         return -1;
     }
 
     printf("%#X\n", inp.s_addr);    //0X0A01A8C0
                                                           
     return 0;
 }            
2)inet_pton(既可以转IPv4也能处理IPv6)
头文件:
#include <arpa/inet.h>
原型:
  int inet_pton(int af, const char *src, void *dst);
参数:
int af:协议族
            AF_INET         IPV4
            AF_INET6        IPV6
char *src:指定要转换成网络字节序的点分十进制字符串;
     void* dst
           typedef uint32_t in_addr_t;
           struct in_addr {
               in_addr_t s_addr;
           };
​
            af == AF_INETa;
            struct in6_addr
            {
            }
返回值:
成功,返回1;
失败,返回0或者-1,更新errno;
2)inet_addr 最常用
头文件:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
原型:
uint32_t inet_addr(const char *cp);
参数:
char *cp:源IP地址的点分十进制字符串,例如 “192.168.1.10”;
返回值:
成功,返回转换后的网络字节序IP地址;
typedef uint32_t in_addr_t;
​失败,返回INADDR_NONE (usually -1);

6.2网络字节序--->点分十进制

1)inet_ntoa 常用
头文件:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
原型:
char *inet_ntoa(struct in_addr in);
参数:
struct in_addr in:指定要转换成点分十进制字符串的IP地址;
           typedef uint32_t in_addr_t;
           struct in_addr {
               in_addr_t s_addr;
           };
返回值:
成功,返回点分十进制字符串的首地址;
2)inet_ntop
头文件:
#include <arpa/inet.h>
原型:
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
参数:
 int af:协议族
            AF_INET         IPV4
            AF_INET6        IPV6
 void* src:存储要转换成点分十进制字符串的IP首地址;
           typedef uint32_t in_addr_t;
           struct in_addr {
               in_addr_t s_addr;
           };
​
            af == AF_INETa;
            struct in6_addr
            {
            }
 char *dst:存储转换后的结果,点分十进制的首地址;
 socklen_t size:缓冲区大小,其实就是指定多大的空间用于转换IP;
返回值:
成功,返回字符串的首地址,就是dst;
失败,返回NULL,更新errno;   
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值