网络编程之格式转换

 

1、Linux环境下使用以下4个函数进行字节序之间的转换,其函数原型如下

#include<arpa/inet.h>

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<stdio.h>
#include<arpa/inet.h>
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<netinet/in.h>
struct in_addr {
      in_addr_t s_addr; //in_addr_t被定义为无符号整型
}
	

      当使用唯一的IP地址定位到通信的目标主机后,还需要确定到底是主机中的哪个进程需要通信。使用端口号可以解决该问题,每个进程都唯一对应一个16位的端口号。因此在网络环境中,一个IP地址加上一个端口号,可以唯一的确定一台主机上是一个进程。

#include<netinet/in.h>
//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<netinet/in.h>
struct socketaddr {
     unsigned short sa_family ;  //16位的地址族
     char sa_data[14];      //14字节的填充区
}


3、地址形式转换
a、Linux提供了IP地址的格式转换函数

#include<netinet/in.h>

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<stdio.h>
#include<stdlib.h>
#include<netinet/in.h>
	
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 <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

 int inet_aton(const char *cp, struct in_addr *inp);

功能:是将一个字符串IP地址转换为一个32位的网络序列IP地址。如果这个函数成功,函数的返回值非零,如果输入地址不正确则会返回零。使用这个函数并没有错误码存放在errno中,所以它的值会被忽略

#define _BSD_SOURC E
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char *argv[])
{
   struct in_addr addr;

   if (argc != 2) {
	   fprintf(stderr, "%s <dotted-address>\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 <arpa/inet.h>
 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<netdb.h>

struct hostent *gethostent(void);

    该函数从系统的/etc/hosts文件中读取主机相关信息,并将其内容存储在系统中的一个静态缓冲区中,返回该静态缓冲区的首地址;如果失败则返回NULL。该结构定义在netdb.h文件中,其原型如下:

 

#include<netdb.h>
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<stdio.h>
#include<stdlib.h>
#include<netdb.h>
#include<arpa/inet.h>
#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<netdb.h>
void endhosttent(void);

 

5、地址映射
Linux环境下提供一个函数,根据用户指定的服务器域名和服务名称得到服务器的IP地址和端口号。并将其填写到一个sockaddr_in地址结构中
该函数内部访问了DNS服务器,从而可以得到需要访问主机的IP地址和端口号。其函数原型如下

 

#include<sys/socket.h>
#include<netdb.h>
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.     

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值