socket编程相关的结构体和字节序转换、IP、PORT转换函数


注意:结构体之间不能直接进行强制转换, 必须先转换成指针类型才可以进行结构体间的类型转换, 这里需要明确的定义就是什么才叫强制转换.
强制转换是将内存中一段代码以另一种不同类型的方式进行解读, 因此转换的空间必须与源空间一一对应.
而结构体则是由不固定的多种类型变量组合而成, 因此强制转换时并不确定原格式与目标格式确定的对应关系, 例如一个结构体为3个变量, 而另一个则为2个, 那么就无法确定如何分配.
因此最简单的让计算机可以理解的方式就是先将结构体转换成指针, 然后将指针转换成目标格式, 再读取转换后的指针, 这样计算机就会按照指针的格式读取特定目标代码段了.


 1、sockaddr 、sockaddr_in 、addrinfo比较

struct sockaddr
{
    unsigned  short  sa_family;     /* address family, AF_xxx */
    char  sa_data[14];                 /* 14 bytes of protocol address */
};

sa_family是地址家族,一般都是“AF_xxx”的形式。好像通常大多用的是都是AF_INET。
sa_data是14字节协议地址。
此数据结构用做bind、connect、recvfrom、sendto等函数的参数,指明地址信息。

但一般编程中并不直接针对此数据结构操作,而是使用另一个与sockaddr等价的数据结构sockaddr_in,在netinet/in.h中定义:

struct  sockaddr_in
{
    short  int  sin_family;                      /* Address family */
    unsigned  short  int  sin_port;       /* Port number */
    struct  in_addr  sin_addr;              /* Internet address */
    unsigned  char  sin_zero[8];         /* Same size as struct sockaddr */
};
struct  in_addr
{
    unsigned  long  s_addr;
};

typedef struct in_addr
{
    union
    {
        struct
        {
            unsigned char s_b1,
                     s_b2,
                     s_b3,
                     s_b4;
        } S_un_b;
        struct
        {
            unsigned short s_w1,
                     s_w2;
        } S_un_w;
        unsigned long S_addr;
    } S_un;
} IN_ADDR;
sin_family 指代协议族,在socket编程中只能是AF_INET
sin_port存储端口号(使用 网络字节顺序
sin_addr 存储IP地址,使用in_addr这个数据结构
sin_zero 是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节
s_addr 按照 网络字节顺序 存储IP地址。

 

addrinfo结构是getaddrinfo函数用来保存主机地址信息的结构体。在头文件Ws2tcpip.h中定义。

typedef struct addrinfo
{
    int ai_flags;
    int ai_family;
    int ai_socktype;
    int ai_protocol;
    size_t ai_addrlen;
    char* ai_canonname;
    struct sockaddr* ai_addr;
    struct addrinfo* ai_next;

} ADDRINFOA,  *PADDRINFOA;

ai_flags:表示getaddrinfo要使用的选项,可赋的值在Winsock2.h中有定义,且可以是几个值的组合。

ai_family:地址族。一般为AF_INET

ai_socktype:套接字类型。SOCK_STREAM,SOCK_DGRAM,SOCK_RAW,SOCK_RDM,SOCK_SEQPACKET

ai_protocol:协议类型。可取的值取决于ai_address和ai_socktype的值。IPPROTO_TCP,IPPROTO_UDP,IPPROTO_RM。

如果ai_family置为AF_IRDA,则ai_protocol必须为0。

ai_addrlen:ai_addr指向的缓冲区的字节数。

ai_canonname:主机的规范化名称(canonical name)。

ai_addr:指向sockaddr结构的指针。

 

2、IP地址的网络字节顺序和机器字节顺序

210.25.132.181属于IP地址的ASCII表示法,也就是字符串形式。英语叫做IPv4 numbers-and-dots notation。

如果把210.25.132.181转换为整数形式,是3524887733,这个就是整数形式的IP地址。英语叫做binary data。(其实binary是二进制的意思)

Internet地址用“.”间隔的地址可有下列几种表达方式: a.b.c.d,a.b.c,a.b,a   当四个部分都有定值时,每个都解释成一个字节数据,从左到右组成Internet四字节地址。 Internet 地址以网络字节顺序返回(字节从左到右排列。请注意,当一个Internet地址在Intel机器上表示成一个32位整型数时,则上述的字节为“d.c.b.a”。这是因为Intel处理器的字节是从右向左排列的。 上述中的“a.b.c.d”就为网络字节顺序,而“d.c.b.a”则为机器字节顺序。

 

3、ntohs, ntohl, htons,htonl的比较和详解

ip地址是32位的,端口号是16位的 。

ntohs =net to host short int 16

htons=host to net short int 16

ntohs =net to host long int 32

htonl=host to net long int  32

 u_short PASCAL FAR ntohs( u_short netshort);

      netshort:一个以网络字节顺序表达的16位数。本函数将一个16位数由网络字节顺序转换为16位主机字节顺序。

 u_short PASCAL FAR htons( u_short hostshort);

       hostshort:一个主机字节顺序表达的16位数。 本函数将一个16位数从主机字节顺序转换成16位网络字节顺序。

 u_long PASCAL FAR htonl( u_long hostlong);  

       hostlong:一个主机字节顺序表达的32位数。本函数将一个32位数从主机字节顺序转换成32位网络字节顺序。

 u_long PASCAL FAR ntohl( u_long netlong)

       netlong:一个以网络字节顺序表达的32位数。 本函数将一个32位数由网络字节顺序转换为32位的主机字节顺序。

 

4、 inet_network()、inet_addr()、inet_aton()、inet_ntoa()

int inet_aton(const char *string, struct in_addr *addr);

in_addr_t inet_addr(const char *cp);

in_addr_t inet_network(const char *cp);

char FAR* PASCAL FAR inet_ntoa( struct in_addr in);

函数inet_addr():将IP地址从 点数格式转换成无符号长整型。使用方法如下:

ina.sin_addr.s_addr = inet_addr("132.241.5.10");
注意,inet_addr()返回的地址已经是网络字节格式,所以你无需再调用 函数htonl()。

函数inet_network():inet_network和inet_addr函数都是用于将字符串形式转换为整数形式用的,两者区别很小,inet_addr返回的整数形式是网络字节序,而inet_network返回的整数形式是主机字节序。

函数int inet_aton(const char *string, struct in_addr *addr):是一个改进的方法来将一个字符串IP地址转换为一个32位的网络序列IP地址。输入参数string包含ASCII表示的IP地址。  输出参数addr是将要用新的IP地址更新的结构。   返回值: 如果这个函数成功,函数的返回值非零。如果输入地址不正确则会返回零。使用这个函数并没有错误码存放在errno中,所以他的值会被忽略。 inet_aton函数和上面这俩小子的区别就是在于他认为255.255.255.255是有效的,他不会冤枉这个看似特殊的IP地址。inet_aton函数返回的是网络字节序的IP地址。如:inet_aton("127.0.0.1",&adr_inet.sin_addr))

函数inet_ntoa()将网络地址转换成“.”点隔的字符串格式。本函数将一个用in参数所表示的Internet地址结构转换成以“.” 间隔的诸如“a.b.c.d”的字符串形式。请注意inet_ntoa()返回的字符串存放在WINDOWS套接口实现所分配的内存中。应用程序不应假设该内存是如何分配的。在同一个线程的下一个WINDOWS套接口调用前,数据将保证是有效。返回值:   若无错误发生,inet_ntoa()返回一个字符指针。否则的话,返回NULL。其中的数据应在下一个WINDOWS套接口调用前复制出来。函数 inet_ntoa()("ntoa"的含义是"network to ascii"),就像这样: printf("%s",inet_ntoa(ina.sin_addr)); 它将输出IP地址。

5、inet_pton() 和inet_ntop()

     Linux下这2个IP地址转换函数,可以在将IP地址在“点分十进制”和“整数”之间转换   而且,inet_pton和inet_ntop这2个函数能够处理ipv4和ipv6。算是比较新的函数了。

inet_pton函数原型如下[将“点分十进制” -> “整数”]   #include <sys/types.h>   #include <sys/socket.h>   #include <arpa/inet.h>   int inet_pton(int af, const char *src, void *dst);   这个函数转换字符串到网络地址,第一个参数af是地址族,转换后存在dst中 。

inet_pton 是inet_addr的扩展,支持的多地址族有下列:   af = AF_INET   src为指向字符型的地址,即ASCII的地址的首地址(ddd.ddd.ddd.ddd格式的),函数将该地址   转换为in_addr的结构体,并复制在*dst中   af =AF_INET6   src为指向IPV6的地址,,函数将该地址   转换为in6_addr的结构体,并复制在*dst中   如果函数出错将返回一个负值,并将errno设置为EAFNOSUPPORT,如果参数af指定的地址族和src格式不对,函数将返回0。

    inet_ntop函数原型如下[将“点分十进制” -> “整数”]   #include <sys/types.h>   #include <sys/socket.h>   #include <arpa/inet.h>   const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);   这个函数转换网络二进制结构到ASCII类型的地址,参数的作用和上面相同,只是多了一个参数socklen_t cnt,他是所指向缓存区dst的大小,避免溢出,如果缓存区太小无法存储地址的值,则返回一个空指针,并将errno置为ENOSPC。

6、gethostbyname和gethostbyaddr

这两个函数仅仅支持IPv4,getaddrinfo函数能够处理名字到地址以及服务到端口这两种转换,返回的是一个sockaddr结构的链表而不是一个地址清单。这些sockaddr结构随后可由套接口函数直接使用。如此以来,getaddrinfo函数把协议相关性安全隐藏在这个库函数内部。应用程序只要处理由getaddrinfo函数填写的套接口地址结构。该函数在 POSIX规范中定义了。

#include<netdb.h>
int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result );
返回0:  成功
返回非0:  出错

hostname:一个主机名或者地址串(IPv4的点分十进制串或者IPv6的16进制串)
service:一个服务名或者10进制端口号数串。
hints:可以是一个空指针,也可以是一个指向某个addrinfo结构的指针,调用者在这个结构中填入关于期望返回的信息类型的暗示。举例来说:如果指定的服务既支持TCP也支持UDP,那么调用者可以把hints结构中的ai_socktype成员设置成SOCK_DGRAM使得返回的仅仅是适用于数据报套接口的信息。

本函数通过result指针参数返回一个指向addrinfo结构链表的指针,而addrinfo结构定义在头文件netdb.h中:
struct addrinfo{
    int       ai_flags; 
    int       ai_family;
    int       ai_socktype;
    int       ai_protocol;
    socklen_t ai_addrlen;
    char    *ai_canonname;
    struct sockaddr *ai_addr;
    struct addrinfo *ai_next;
};

如果本函数返回成功,那么由result参数指向的变量已被填入一个指针,它指向的是由其中的ai_next成员串联起来的addrinfo结构链表。可以导致返回多个addrinfo结构的情形有以下2个:
    1.    如果与hostname参数关联的地址有多个,那么适用于所请求地址簇的每个地址都返回一个对应的结构。
    2.    如果service参数指定的服务支持多个套接口类型,那么每个套接口类型都可能返回一个对应的结构,具体取决于hints结构的ai_socktype成员。

我们必须先分配一个hints结构,把它清零后填写需要的字段,再调用getaddrinfo然后遍历一个链表逐个尝试每个返回地址。

getaddrinfo解决了把主机名和服务名转换成套接口地址结构的问题。

其中,如果getaddrinfo出错,那么返回一个非0的错误值。


转载于:https://www.cnblogs.com/young525/p/5873722.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值