向sockaddr_in注入地址时,需要将ip地址的字符串形式端口号的主机字节序形式转化为网络字节序的形式;而相反地,网络字节序也能转化回字符串形式和主机字节序形式。(字符串形式本质是ASCII码存储的形式,在主机也是以主机字节序存储,也要转化为网络字节序)

值得注意的是,除了向sockaddr_in结构体填充数据外,其他情况无需考虑字节序问题。

1 端口号的网络字节序转换

假设主机字节序中,存储是数据是0x12345678

  • 大端模式(网络字节序):高位字节存放在内存的低地址处

数据

12

34

56

78

地址

0x00

0x01

0x02

0x03

大端适合网络传输(流方式或者缓冲区)一次读一个字节,然后解析字节,读下一个字节。

  • 小端模式:高位字节存放在内存的高地址处

数据

78

56

34

12

地址

0x00

0x01

0x02

0x03

小端适合逻辑电路

然后介绍4种转化字节的方法。htons中h表示主机host,n表示网络network,s表示short,所以是h、to、n、s、l的排列组合,htons解释为把short型数据从主机字节序列转化为网络字节序列。

unsigned short htons(unsigned short);
unsigned short ntohs(unsigned short)
unsigned long htonl(unsigned long)
unsigned long ntohl(unsigned long)
  • 转化实例
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
	unsigned short host_port = 0x1234;
	unsigned short net_port;
	unsigned long host_port_l = 0x12345678;
	unsigned long net_port_l;
	
	net_port = htons(host_port);
	net_port_l = htonl(host_port_l);
	
	printf("Host ordered port short: %#x \n", host_port);
	printf("Nerwork ordered port short: %#x \n", net_port);
	printf("Host ordered port long: %#xl \n", host_port_l);
	printf("Nerwork ordered port long: %#xl \n", net_port_l);
	return 0;
}

下图小端cpu的转化结果,若是在大端CPU上,则不会变化

lua 转网络字节序_网络字节序

2 ip地址的网络字节序转换

用到的函数分别inet_addr()、inet_aton()、inet_ntoa()、inet_pton()、inet_ntop(),转化的方向如下图。

lua 转网络字节序_#include_02

2.1 inet_addr()

#include <arpa/inet.h>
in_addr_t inet_addr(const char *ip);
  • 功能:是将一个点分十进制ipv4的IP地址转换32位大端网络字节序整数
  • 参数:点分十进制的ip地址字符串ip
  • 返回值:成功时返回32位大端整数,失败返回INADDR_NONE

2.2 inet_aton()和inet_ntoa()

inet_addr()直接返回结果,而inet_aton()将转换结果直接写入传入的参数中,方便向sockaddr_in注入IP地址。
记法:ASCII to network / network to ASCII

inet_aton()

#include <arpa/inet.h>
int inet_aton(const char *ip, struct in_addr *addr);
  • 功能:是将一个点分十进制的ipv4的IP地址转换32位大端网络字节序整数,结果直接填入in_addr中,使用频率比inet_addr()多。
  • 参数:
  1. string: 点分十进制的ip地址字符串ip
  2. addr: 属于sockaddr_in结构体的结构体in_addr地址
  • 返回值:成功时返回1,失败返回0

inet_ntoa()

#include <arpa/inet.h>
char *inet_ntoa(struct in_addr *addr );
  • 功能:是将一个32位大端网络字节序整数转换为点分十进制的ipv4的IP地址。
  • 参数:
  1. addr: 属于sockaddr_in结构体的结构体in_addr地址
  • 返回值:存放转化结果的首地址,char*指针,要提前分配空间。失败时返回-1。

2.3 inet_pton()inet_ntop()

前面的只适用于ipv4地址,这两个适用于ipv4和ipv6,因此使用时要指定地址族
记法:presentation to numeric / numeric to presentation

inet_pton()

#include <arpa/inet.h>
int inet_pton(int af, const char *ip, void *dst);
  • 功能:将ipv4或者ipv6的字符串ip地址转换为大端网络字节序整数
  • 参数:
  1. af: 地址族,取值为AF_INET和AF_INET6,分别ipv4和ipv6
  2. ip:要转化的ip字符串首地址
  3. dst:接收转化后大端网络字节序整数结果的地址,ipv4可以用sin_addr
  • 返回值:如果函数出错将返回一个负值,并将errno设置为EAFNOSUPPORT,如果参数af指定的地址族和ip格式不对,函数将返回0。

inet_ntop()

#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
  • 功能:将为大端网络字节序整数转换为ipv4或者ipv6的字符串ip地址
  • 参数:
  1. af: 地址族,取值为AF_INET和AF_INET6,分别ipv4和ipv6
  2. src:大端网络字节序整数首地址地址,ipv4可以用sin_addr
  3. dst:接收转化后的ip地址字符串首地址(缓冲区)
  4. cnt:dst缓冲区大小
  • 返回值:
    如果缓存区太小无法存储地址的值,则返回一个空指针,并将errno置为ENOSPC。若无错误发生,Inet_ntop()函数返回一个指向缓冲区的指针。

2.4例程

代码

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <string.h>  
#include <netinet/in.h>  
#include <sys/socket.h>  
#include <sys/types.h>  
#include <arpa/inet.h>
int main()  
{  
    char ip1[] = "192.168.0.74";  
    char fakeip[] = "192.256.0.74";
    struct sockaddr_in addr_inet;
/*
    inet_addr()
*/
    printf("ip %s converting\n",ip1);
    unsigned long net_ordered_addr = inet_addr(ip1);
    if(net_ordered_addr == INADDR_NONE)
    {
        printf("inet_addr() error\n\n");
    }else{
        printf("inet_addr() success: network ordered integer addr: %#x \n\n",
            net_ordered_addr);
    }
    // fake ip
    printf("fake ip %s converting\n", fakeip);
    net_ordered_addr = inet_addr(fakeip);
    if(inet_aton(fakeip,&addr_inet.sin_addr)==0)
    {
        printf("inet_addr() error\n\n");
    }else{
        printf("inet_addr() success: network ordered integer addr: %#x \n\n",
            net_ordered_addr);
    }
/*
    inet_aton() and inet_ntoa()
*/
    printf("ip %s converting\n",ip1);
    if(inet_aton(ip1,&addr_inet.sin_addr)==0)
    {
        printf("inet_aton() error\n\n");
    }else{
        printf("inet_aton() success: network ordered integer addr: %#x \n\n",
            addr_inet.sin_addr.s_addr);
    }
    printf("network odered addr %#x converting\n",addr_inet.sin_addr.s_addr);
    char *ip_new;
    ip_new = inet_ntoa(addr_inet.sin_addr);
    if(ip_new[0]== -1){
        printf("inet_ntoa() error\n\n");
    }else{
        printf("inet_ntoa() success: ip: %s \n\n", ip_new);
    }
/*
    inet_pton() and inet_ntop()
*/
    char ip2[] = "255.255.255.255";  
    printf("ip %s converting\n", ip2);
    if(!inet_pton(AF_INET, ip2, (void *) &addr_inet.sin_addr))
    {
        printf("inet_pton() error\n\n");
    }else{
        printf("inet_pton() success: network ordered integer addr: %#x \n\n",
            addr_inet.sin_addr);
    }
    
    char ip2_new[16];  //ipv4的字符串长度为16字节
    printf("network odered addr %#x converting\n", addr_inet.sin_addr.s_addr);
    if(!inet_ntop(AF_INET, (void *) &addr_inet.sin_addr, ip2_new, sizeof(ip2_new)))
    {
        printf("inet_pton() error\n\n");
    }else{
        printf("inet_pton() success: ip: %s \n\n", ip2_new);
    }
   
    return 0;
}

运行结果

lua 转网络字节序_字符串_03