网络字节序本地字节序点分十进制转换函数总结&&两种初始化socket并bind的步骤

看完这些转换可以去看初始化socket的步骤,会印象更加深刻一些

##1.网络字节序、本地字节序和点分十进制的数据长啥样

1.点分十进制

常用来表示ip,就是192.168.120.139这样的字符串

2.本地字节序(主机字节序)和网络字节序

字节存储顺序主要分为大端序(Big-endian)和小端序(Little-endian),区别如下

  • Big-endian:高位字节存入低地址,低位字节存入高地址
  • Little-endian:低位字节存入低地址,高位字节存入高地址

例如,将12345678h写入1000h开始的内存中,以大端序和小端序模式存放结果如下:

img

现代 PC 大多采用小端字节序,因此小端字节序又称为主机字节序

而在两台不同字节序的主机之间传递数据时,发送端总是把要发送的数据转化成大端字节序数据后再发送,因此大端字节序也被称为网络字节序

3.重点:

1.凡是和socket相关的都要转为网络字节序

2.ip如果是本地字节序或点分十进制的字符串就要转为网络字节序

3.port如果是本地字节序就要转为网络字节序

4.我们也不用去管自己的机器到底是大端还是小端,把输进来的全都转成大端就行了

2.网络字节序本地字节序点分十进制转换函数

1.主机(本地)转网络字节序–常用

用来转换端口号或ip

长整型用来转换ip,短整型用来转换端口号

1
2
3
4
5
6
include <netinet/in.h>

//长整型用来转换ip
unsigned long int htonl ( unsigned long int hostlong );
//短整型用来转换端口号
unsigned short int htons ( unsigned short int hostshort );

2.网络转主机(本地)字节序

ip的一般不用,想要查看端口是多少的话就查查端口

1
2
3
4
//用于ip
unsigned long int ntohl ( unsigned long int netlong );
//用于端口号
unsigned short int ntohs ( unsigned short int netshort );

3.点分十进制转网络字节序

用于ip

1
2
3
4
5
#include <arpa/inet.h>

in_addr_t inet_addr ( const char* strptr );
int inet_aton ( const char* cp, struct in_addr* inp );
int inet_pton(int af, const char *src, void *dst);

image-20241213162110854

  1. 函数原型
    • int inet_pton(int af, const char *src, void *dst);
  2. 参数含义
    • af参数:
      • 代表地址族(Address Family)。它用于指定要转换的 IP 地址所属的协议族。
      • 常见的值是AF_INET(用于 IPv4 地址)和AF_INET6(用于 IPv6 地址)。这告诉函数按照哪种网络协议版本的规则来进行地址转换。
    • src参数:
      • 是一个常量字符串指针(const char *),它指向要进行转换的 IP 地址的字符串表示形式。
      • 对于AF_INET,这个字符串应该是点分十进制(如"192.168.1.1")的 IPv4 地址格式。对于AF_INET6,它应该是符合 IPv6 地址规范的字符串格式(如"2001:0db8:85a3:0000:0000:8a2e:0370:7334")。
    • dst参数:
      • 是一个指针,用于存储转换后的二进制 IP 地址。
      • afAF_INET时,dst通常指向一个struct in_addr类型的结构体,转换后的 32 位 IPv4 地址会存储在这个结构体的s_addr成员中。当afAF_INET6时,dst通常指向一个struct in6_addr类型的结构体,用于存储转换后的 IPv6 地址。

3.返回值

  • 如果转换成功,函数返回1
  • 如果输入的src字符串不是合法的 IP 地址格式(对于指定的af),函数返回0
  • 如果发生其他错误(如af参数指定了不支持的地址族等),函数返回-1

4.网络字节序转点分十进制

常用于想要查看与socket绑定的ip地址时使用

1
2
char* inet_ntoa( struct in_addr in );
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
  • char *inet_ntoa(struct in_addr in);

    • 参数in:是一个struct in_addr类型的结构体,其中存储了网络字节序的 IPv4 地址。这个结构体只有一个成员s_addr,用于存放 32 位的 IPv4 地址。
  • inet_ntop函数原型:

    • const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

    • 参数af:指定地址族,如AF_INET(IPv4)或AF_INET6(IPv6)。

    • 参数src:是一个指向存储网络字节序 IP 地址的指针。对于 IPv4,它指向struct in_addr类型结构体中的s_addr成员;对于 IPv6,它指向struct in6_addr类型结构体。

    • 参数dst:是一个字符数组,用于存储转换后的 IP 地址字符串。

    • 参数size:是dst字符数组的大小,用于防止缓冲区溢出。

    • 返回值:如果成功,返回dst(指向存储转换后 IP 地址字符串的字符数组的指针);如果失败,返回NULL

      例子:

1
2
3
printf("client ip:%s port:%d\n",
inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,client_ip,sizeof(client_ip)),
ntohs(client_addr.sin_port));

打印ip和端口号

3.两种初始化socket并bind的步骤

1.黑马中的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int listenfd=0,connfd=0;
//1.调用socket创建一个socket(所有这类函数都把它自身和错误处理封装为类似Socket这种类型了)
listenfd=Socket(AF_INET,SOCK_STREAM,0);

//2.命名socket,就是把一个sockaddr_in的内部成员赋值后转为sockaddr *绑定到socket上
struct sockaddr_in serv_addr,clie_addr;
socklen_t clie_addr_len;
//2.1 sockaddr_in 成员赋值
serv_addr.sin_family=AF_INET;//1.地址族 这是一个宏,是固定的
serv_addr.sin_port=htons(9527);//2.端口号 要用网络字节序来表示 htos是本地字节序转网络字节序,作用是不管本地字节序是大端还是小端全都转为大端
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);//3.ip地址 赋值 除了inet_aton和inet_pton(这两个是将点分十进制的ip转为网络字节序),还可以用上面的写法 ,即用INADDR_ANY的写法

//2.2 将sockaddr_in强转为sockaddr *,因为bind之类的函数只接受这类参数,不接受sockaddr_in
//3.bind进行绑定
Bind(listenfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
Listen(listenfd,128);
client_addr_len=sizeof(client_addr);

cfd=Accept(lfd,(struct sockaddr *)&client_addr,&client_addr_len);
//输出
printf("client ip:%s port:%d\n",
inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,
client_ip,sizeof(client_ip)),ntohs(client_addr.sin_port));

1.调用socket创建一个socket(所有这类函数都把它自身和错误处理封装为类似Socket这种类型了)

2.命名socket,就是把一个sockaddr_in的内部成员赋值后转为sockaddr *绑定到socket上

2.1 sockaddr_in 成员赋值

​ 1.地址族 这是一个宏,是固定的

​ 2.端口号 要用网络字节序来表示 htos是本地字节序转网络字节序,作用是不管本地字节序是大端还是小端全都转为大端

​ 3.ip地址 赋值 除了inet_aton和inet_pton(这两个是将点分十进制的ip转为网络字节序),还可以用上面的写法 ,即用INADDR_ANY的写法

  1. INADDR_ANY的含义
    • INADDR_ANY是一个特殊的常量,用于网络编程中表示本地机器的任意网络接口地址。它并不是一个真正的 IP 地址码,而是一个用于套接字绑定操作的占位符概念。
    • 从数值上来说,在 IPv4 环境下,INADDR_ANY通常被定义为0(十六进制表示为0x00000000)。这个值并不对应特定的字节序(如本地字节序或网络字节序)下的有效 IP 地址。
  2. 字节序和INADDR_ANY的关系
    • 当将INADDR_ANY用于网络编程中的函数(如bind函数)时,它的处理方式与字节序转换函数有关。
    • 例如,在代码中常常会看到serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);这样的语句。这里htonl函数将INADDR_ANY(其值为0)从主机字节序转换为网络字节序。
    • 因为在网络通信中,sockaddr_in结构体中的sin_addr.s_addr字段需要存储网络字节序的 IP 地址,所以即使INADDR_ANY本身的值很简单(为0),也需要进行字节序转换操作,以符合网络协议对数据格式的要求。
  3. 实际用途
    • 当服务器程序使用bind函数将套接字绑定到INADDR_ANY时,它表示服务器愿意接受来自本地机器上任何可用网络接口的连接请求。这对于服务器在多宿主主机(有多个网络接口,每个接口有不同的 IP 地址)上运行非常有用,因为服务器不需要指定一个特定的 IP 地址来接收连接,而是可以监听所有的本地 IP 地址。

2.2 将sockaddr_in强转为sockaddr *,因为bind之类的函数只接受这类参数,不接受sockaddr_in

3.bind进行绑定

2.linux高性能服务器中的,可以在命令行中输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if(argc<=2)
{
printf("usage:%s ip_address port_number backlog\n",basename(argv[0]));
return 1;
}
const char *ip=argv[1];
int port=atoi(argv[2]);
int sock=socket(PF_INET,SOCK_STREAM,0);
assert(sock>=0);//断言
struct sockaddr_in address;
//address初始化为0
bzero(&address,sizeof(address));
address.sin_family=AF_INET;
inet_pton(AF_INET,ip,&address.sin_addr);
address.sin_port=htons(port);

int ret=bind(sock,(struct sockaddr*)&address,sizeof(address));
assert(ret!=-1);
ret=listen(sock,5);
assert(ret!=-1);
struct sockaddr_in client;
socklen_t client_addrlength=sizeof(client);
int connfd=accept(sock,(struct sockaddr*)&client,&client_addrlength);

这个是通过命令行参数来进行创建socket的,其实就是通过argv数组把ip和端口号传了进来

ip传入的时候就是点分十进制,端口号传入的时候就是本地字节序

举例:

1
./sever 192.168.120.139 1234

./sever 要运行的服务器程序

192.168.120.139 点分十进制的ip

1234 本地字节序的port

步骤:

1.调用socket创建一个socket(所有这类函数都把它自身和错误处理封装为类似Socket这种类型了)

2.命名socket,就是把一个sockaddr_in的内部成员赋值后转为sockaddr *绑定到socket上

2.1 sockaddr_in 成员赋值

​ 1.地址族 这是一个宏,是固定的

​ 2.端口号 要用网络字节序来表示。htos是本地字节序转网络字节序,作用是不管本地字节序是大端还是小端全都转为大端

​ 3.ip地址 赋值 除了inet_aton和inet_pton(这两个是将点分十进制的ip转为网络字节序),还可以用上面的写法 ,即用INADDR_ANY的写法

  1. INADDR_ANY的含义
    • INADDR_ANY是一个特殊的常量,用于网络编程中表示本地机器的任意网络接口地址。它并不是一个真正的 IP 地址码,而是一个用于套接字绑定操作的占位符概念。
    • 从数值上来说,在 IPv4 环境下,INADDR_ANY通常被定义为0(十六进制表示为0x00000000)。这个值并不对应特定的字节序(如本地字节序或网络字节序)下的有效 IP 地址。
  2. 字节序和INADDR_ANY的关系
    • 当将INADDR_ANY用于网络编程中的函数(如bind函数)时,它的处理方式与字节序转换函数有关。
    • 例如,在代码中常常会看到serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);这样的语句。这里htonl函数将INADDR_ANY(其值为0)从主机字节序转换为网络字节序。
    • 因为在网络通信中,sockaddr_in结构体中的sin_addr.s_addr字段需要存储网络字节序的 IP 地址,所以即使INADDR_ANY本身的值很简单(为0),也需要进行字节序转换操作,以符合网络协议对数据格式的要求。
  3. 实际用途
    • 当服务器程序使用bind函数将套接字绑定到INADDR_ANY时,它表示服务器愿意接受来自本地机器上任何可用网络接口的连接请求。这对于服务器在多宿主主机(有多个网络接口,每个接口有不同的 IP 地址)上运行非常有用,因为服务器不需要指定一个特定的 IP 地址来接收连接,而是可以监听所有的本地 IP 地址。

2.2 将sockaddr_in强转为sockaddr *,因为bind之类的函数只接受这类参数,不接受sockaddr_in

3.bind进行绑定