1、Linux环境下使用以下4个函数进行字节序之间的转换,其函数原型如下
#include
uint32_t htonl(uint32_t hostint32);
uint16_t htons(uint16_t hostint16);
uint32_t ntohl(uint32_t netint32);
uint16_t ntohs(uint16_t netint16);
htonl():函数的参数是一个32位的本地主机数据,该数据采用的是主机字节序。htons()函数将其转换为网络字节序,并且返回。
htons():函数和htonl()函数功能类似,不过该函数对16位整数进行转换。
ntohl():将网络字节序的数据转换为主机字节序
ntohs():将网络字节序的数据转换为主机字节序。
例:
#include
#include
int main(void)
{
short a = htons(0x0102); //主机字节转换位网络字节序
short *p = &a;
if(* ((char *)p) == 0x01) //测试最低位的数据是多少
printf("big-endian\n");
else if (* (char *)p) == 0x02)
printf("little-endian\n");
else
printf("unknown\n0"); //未知存储方法
return 0;
}
$gcc order.c -o order
$./order
big-endian
2、地址格式
Linux中使用in_addr结构表示一个IP地址,该结构的定义如下
#include
struct in_addr {
in_addr_t s_addr; //in_addr_t被定义为无符号整型
}
当使用唯一的IP地址定位到通信的目标主机后,还需要确定到底是主机中的哪个进程需要通信。使用端口号可以解决该问题,每个进程都唯一对应一个16位的端口号。因此在网络环境中,一个IP地址加上一个端口号,可以唯一的确定一台主机上是一个进程。
#include
//linux中的网络通信地址结构
struct socketaddr_in {
sa_family_t sin_family; //16位的地址族
in_port_t sin_port; //16位的端口号
struct in_addr sin_addr; //32位的IP地址
unsigned char sin_zero[8]; //填充区,8个字节填0
}
在网络通信地址族统一般使用AF_NET
sin_zero[8]:表示的是填充区,其作用是保证socketaddr_in结构正好16个字节,这样做的目的是为了使socketaddr_in结构可以和socketaddr地址结构随意转换。secketaddr结构定义如下:
#include
struct socketaddr {
unsigned short sa_family ; //16位的地址族
char sa_data[14]; //14字节的填充区
}
3、地址形式转换
a、Linux提供了IP地址的格式转换函数
#include
const char *inet_ntop(int domain,const void *restrict src, char *restrict dst, socklen_t cnt);
int inet_pton(int domain, const char *restrict src, void *restrict dst);
domain:地址族可以是AF_INET或者AF_INET6
src:来源地址
dst:转换后的地址
cnt:指向缓存区dst的大小,避免溢出,如果缓存区太小无法存储地址的值,则返回一个空指针,并将errno置为ENOSPC
例:
#include
#include
#include
int main(void)
{
char addr_p[16] ; //IP地址的点分十进制字符串表示形式
struct in_addr addr_n; //IP地址的二进制表示形式
if(inet_pton(AF_INET,"192.168.11.6",&addr_n) == -1) { //地址由字符串转换为二进制数
perror("fail to convert");
eixt(1);
}
printf("address :%x\n",addr_n.s_addr); //打印地址的十六进制形式这样便于阅读
//地址由二进制数转换为点分十进制字符串
if(inet_ntop(AF_INET, &addr_n, &addr_p, sizeof(addr_p)) == -1) {
perror("fail to convert");
exit(1);
}
printf("address :%s\n",addr_p); //打印地址的点分十进制形式
return 0;
}
$gcc addr.c -o addr
$./addr
60ba8c0
192.168.11.6
b、
#include
#include
#include
int inet_aton(const char *cp, struct in_addr *inp);
功能:是将一个字符串IP地址转换为一个32位的网络序列IP地址。如果这个函数成功,函数的返回值非零,如果输入地址不正确则会返回零。使用这个函数并没有错误码存放在errno中,所以它的值会被忽略
#define _BSD_SOURC E
#include
#include
#include
int
main(int argc, char *argv[])
{
struct in_addr addr;
if (argc != 2) {
fprintf(stderr, "%s \n", argv[0]);
exit(EXIT_FAILURE);
}
if (inet_aton(argv[1], &addr) == 0) {
fprintf(stderr, "Invalid address\n");
exit(EXIT_FAILURE);
}
printf("%s\n", inet_ntoa(addr));
exit(EXIT_SUCCESS);
}
char *inet_ntoa(struct in_addr in);
功能:将一个十进制网络字节序转换为点分十进制IP格式的字符串。
#include
in_addr_t inet_addr(const char *cp);
功能:是将一个点分十进制的IP转换成一个长整数型数(u_long类型)
返回:若字符串有效则将字符串转换为32位二进制网络字节序的IPV4地址,否则为INADDR_NONE
in_addr_t inet_network(const char *cp);
inet_addr和inet_network函数都是用于将字符串形式转换为整数形式用的,两者区别很小,inet_addr返回的整数形式是网络字节序,而inet_network返回的整数形式是主机字节序。
4、获得主机信息
Linux环境下使用gethostent()函数读取和主机有关的信息,该函数的原型如下
#include
struct hostent *gethostent(void);
该函数从系统的/etc/hosts文件中读取主机相关信息,并将其内容存储在系统中的一个静态缓冲区中,返回该静态缓冲区的首地址;如果失败则返回NULL。该结构定义在netdb.h文件中,其原型如下:
#include
struct hostent {
char * h_name; //正式主机名,每个主机只有一个
char **h_aliases; //主机别名列表,可以有很多个,以二维数组形式存储
int h_addrtype; //IP地址类型,可以选择IPv4或者IPv6
int h_length; //IP地址长度,IPv4对应4字节的地址长度
char **h_addr_list //IP地址列表,h_addr_list[0]为主机的IP地址
}
例:
#include
#include
#include
#include
#define NEF_ADDR 16 //16个字节,用于存放点
int main(void)
{
struct hostent *host; //用于存放主机信息
char addr_p[NET_ADDR]; //用于存储点分十进制IP地址的字符串
int i;
if((host = gethostent()) == NULL) { //获得主机信息
perror("fail to get host's information\n");
exit(1);
}
printf("%s\n",host->h_name); //打印主机名
for(i =0 ; host->h_aliases[i] != NULL; i++) //打印主机别名
printf("%s\n",host->h_aliases[i]);
if(host->h_addrtype == AF_INET) // 打印地址类型
printf("af_inet\n");
else
printf("unix_inet\n");
printf("%d\n",host->length); //打印地址长度
for(i =0 ;host->h_addr_list[i] != NULL; i++) //打印主机IP地址
printf("%s\n",inet_ntop(host->h_addrtype,host->h_addr_list[i],addr_p,NET_ADDR));
return 0;
}
$gcc gethostinfo.c -o gethostinfo
$./gethostinfo
af_inet
4
127.0.0.1
gethostent()函数将获得主机信息存储在一个静态的缓冲区中,所以调用该函数时上次返回的指针所指向区域的内容就失效了
struct hostent *host1, *host2;
host1 = gethostent();
host2 = gethostent();
当第二次调用gethostent()函数时,host1指针所指向的缓冲区的内容会被冲掉,因此host1和host2指针所指向的缓冲区的内容实际上是一致的。如果调用gethostent()函数时/etc/hosts文件尚未打开,那么该函数会打开该文件。使用endhostent()函数可以关闭该文件,其函数原型如下
#include
void endhosttent(void);
5、地址映射
Linux环境下提供一个函数,根据用户指定的服务器域名和服务名称得到服务器的IP地址和端口号。并将其填写到一个sockaddr_in地址结构中
该函数内部访问了DNS服务器,从而可以得到需要访问主机的IP地址和端口号。其函数原型如下
#include
#include
int getaddrinfo(const char * restrict host,const char *restrict service, const struct addrinfo *restrict hint, struct addrinfo **restrict res);
getaddrinfo()函数的前两个参数分别表示需要访问的主机名和服务的名称。注意这两个名称都应该已经在DNS服务器中注册。第3个参数表示一个过滤地址模板,通常情况下不过滤任何IP地址,因此将该参数设置为NULL第4个参数表示一个地址信息结构的列表。该列表列出了所有可用的符号条件的地址结构,用户可以从中任选一个作为通信的地址,如果成功得到地址信息列表,
getaddrinfo函数返回0,失败则返回-1.