Socket编程:必须要了解的网络字节序和转换函数

目录

主机字节序和网络字节序

转换函数

htonl 、htons 、ntohl 和 ntohs

inet_addr 、inet_aton 和 inet_ntoa

inet_pton 和 inet_ntop


主机字节序和网络字节序

现代 CPU 的累加器一次能装载至少 4 字节,即一个整数。那么这 4个 字节在内存中的排列的顺序将影响它被累加器装载成的整数的值,这就是字节序问题。字节序分为大端字节序 (即大端模式,Big endian) 和 小端字节序 (即小端模式,Little endian)。

大端字节序:整数的高位字节存储在内存的低地址处,低位字节存储在内存的高地址处。

小端字节序:整数的高位字节存储在内存的高地址处,而低位字节存储在内存的低地址处。

以下两种方法可以用来验证你的 PC 是大端模式还是小端模式:

#include <iostream>
#include <string>
using namespace std;

int main()
{
	int as = 0x12345678;
	char *p =(char *) &as;
	if(*p == 0x12)
		cout << "Big-endian" <<endl;
	else
		cout << "Little-endian" <<endl;
		
		
	union {
		short  s;
		char   c[sizeof(short)];
	} un;
	un.s = 0x0102;
	if (sizeof(short) == 2) {
		if (un.c[0] == 1 && un.c[1] == 2)
			printf("Big-endian\n");
		else if (un.c[0] == 2 && un.c[1] == 1)
			printf("Little-endian\n");
		else
			printf("unknown\n");
	} else
		printf("sizeof(short) = %d\n", sizeof(short));

	return 0;
}

一个 32 位值的、4 字节以下顺序传输:首先是0 - 7位然后是8 - 15位接着是16 - 23位最后是24 - 31位。这就是所谓的大端字节序它是 TCP/IP 头部中所有二进制整数在网络中传输时所需的字节顺序它也被称为网络字节序。计算机的 CPU 使用其他格式存储二进制整数例如大多数 PC 使用小端字节序。

当格式化的数据在两台使用不同字节序的主机之间直接传递时,接收端按自己模式对数据进行提取或解释时必然会导致错误。解决问题的方法是:发送端总是把要发送的数据转化成大端模式数据后再发送,而接收段知道对方传送过来的数据总是采用大端模式,所以接收段可以根据自身采用的模式决定是否对接收到的数据进行转换。

注意:网络字节序是 TCP/IP 中规定好的一种数据表示格式,它与具体的 CPU 类型、操作系统等无关,它保证数据在不同主机之间传输时能够被正确解释。

 

 

转换函数

htonl 、htons 、ntohl 和 ntohs

Linux 提供了如下4个函数来完成主机字节序和网络字节序之间的转换:

头文件#include <netinet/in.h>
unsigned long int htonl ( unsigned long int hostlong );
htonl 把unsigned long int类型从主机序转换到网络序

unsigned short int htons ( unsigned short int hostlong );
htons把unsigned short int类型从主机序转换到网络序

unsigned long int ntohl ( unsigned long int netlong );
ntohl 把unsigned long int类型从网络序转换到主机序 

unsigned short int ntohs ( unsigned short int netshort ); 
ntohs 把unsigned short int类型从网络序转换到主机序

在这 4 个函数中,长整型函数通常用来转换 IP 地址,短整型函数用来转换端口号。当然函数的使用不限于此,任何格式化的数据通过网络传输时,都应该使用这些函数来转换字节序。

 

inet_addr 、inet_aton 和 inet_ntoa

下面 3 个函数可用于将点分十进制字符串表示的 IPv4 地址和用网络字节序整数表示的 IPv4 地址之间进行转换,这里做一个简单的介绍:

#include<arpa/inet.h>
in_addr_t inet_addr( const char *strptr );
int inet_aton( const char * cp, struct in_addr* inp );
char * inet_ntoa( struct in_addr in );

inet_addr 函数将用点分十进制字符串表示的 IPv4 地址转化为用网络字节序整数表示的 IPv4 地址。它失败时返回 INADDR_NONE。

inet_aton 函数完成和 inet_addr 同样的功能,但是将转化结果存储于参数 inp 指向的地址结构中。它成功返回 1 ,失败返回 0。

inet_ntoa 函数将用网络字节序整数表示的 IPv4 地址转化为用点分十进制字符串表示的 IPv4 地址。但需要注意的是,该函数内部用一个静态变量存储转化结果,函数的返回值指向该静态内存,因此 inet_ntoa 是不可重入的。

 

inet_pton 和 inet_ntop

inet_pton 和 inet_ntop也能完成和前面 3 个函数同样的功能,并且它们同时适用于 IPv4 地址和 IPv6 地址

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);

inet_pton 函数将用字符串表示的 IP 地址 src 转换成用网络字节序整数表示的 IP 地址,即[点分十进制] -> [二进制整数]。并把转换结果存储于 dst 指向的内存中。

参数1:af 指定地址族,可以是 AF_INET 或 AF_INET6

参数2:src 是来源地址。

参数3:dst 接收转换后的数据。

若成功,则返回1;如果函数出错将返回一个负值,并将 errno 设置为 EAFNOSUPPORT;如果参数 af 指定的地址族和 src 格式不对,函数将返回 0。

 

#include <sys/socket.h>
#include <netinet/in.h>
#include<arpa/inet.h>
const char *inet_ntop(int af, const void *src, char *dst, size_t len);

这 inet_ntop 函数将[二进制整数] -> [点分十进制]。

参数1:af 可以是 AF_INET 或 AF_INET6

参数2:src 是一个指向网络字节序的二进制值的指针

参数3:dst 是一个指向转换后的点分十进制串的指针

参数4:len 是目标的大小,以免函数溢出其调用者的缓冲区。

若成功则为指向结果的指针若出错返回 NULL

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main ()
{
	char IPdotdec[20]; //存放点分十进制IP地址
	struct in_addr s;  //IPv4地址结构体
	
	//输入IP地址
	printf("Please input IP address: ");
	scanf("%s", IPdotdec);
	
	//转换
	inet_pton(AF_INET, IPdotdec, (void *)&s);
	printf("inet_pton: 0x%x\n", s.s_addr);     //注意得到的字节序
	
	//反转换
	inet_ntop(AF_INET, (void *)&s, IPdotdec, 16);
	printf("inet_ntop: %s\n", IPdotdec);
}

输出:
Please input IP address: 192.169.1.1
inet_pton: 0x101a9c0
inet_ntop: 192.169.1.1

 

 

参考:

百度百科:https://baike.baidu.com/item/inet_pton

https://blog.csdn.net/u010889616/article/details/47157637?utm_source=blogxgwz6

  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tyler_Zx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值