C++的socket地址详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chengqiuming/article/details/89342594

一 点睛

socket地址分为通用socket地址和专用socket地址。

通用socket地址出现在一些socket api函数中,比如bind函数、connect函数等。

专用socket地址是为了方面使用而提出来的。

两者可以相互转换。

二 通用socket地址

通用socket地址是一个结构体,名字是sockaddr,定义在/usr/include/bits/socket.h中。

#include <bits/sockaddr.h>

/* Structure describing a generic socket address.  */
struct sockaddr
  {
    __SOCKADDR_COMMON (sa_);    /* Common data: address family and length.  */
    char sa_data[14];        /* Address data.  */
  };

宏展开后的形式是

struct sockaddr
  {
    sa_family_t sin_family;   //sa_family_t是unsigned short int
    char sa_data[14];       
  };

sin_family用来存放地址族(或协议族)类型,常用取值如下:

  • PF_UNIX:UNIX本地域协议族

  • PF_INET:IPv4协议族

  • PF_INET6:IPv6协议族

  • AF_UNIX:UNIX本地域地址族

  • AF_INET:IPv4地址族

  • AF_INET6:IPv6地址族

sa_data用来存放地址数据。

由于sa_data只有14个字节,随着时代的发展,一些新的协议提出来了,比如IPv6,它的地址长度不够14个字节,不同协议族的具体地址长度如下表:

协议族

地址含义和长度

PF_UNIX

文件全路径名,最大长度可达108字节。

PF_INET

32位IPv4地址和16位端口号,共6个字节。

PF_INET6

128位IPv6地址、16位端口号、32位流标识和32位范围ID,共26字节。

sa_data太小了,容纳不下了,怎么办?LInux定义了新的存储结构:

struct sockaddr_storage
  {
    sa_family_t sa_family;
    unsigned long int __ss_align;    
    char __ss_padding[128-sizeof(__ss_align)];
  };

三 专用socket地址

通用地址结构把IP地址和端口号信息都放到一个char数组中,使得使用起来不方便。为此,Linux为不同的协议族定义了不同的socket结构体。

1 IPv4的socket地址定义,定义于/usr/include/netinet/in.h

struct sockaddr_in
  {
    sa_family_t sin_family;   //地址族,取AF_INET
    in_port_t sin_port;       //端口号,用网络字节序表示
    struct in_addr sin_addr;    //IPv4地址结构,用网络字节序表示
    unsigned char sin_zero[sizeof (struct sockaddr) -
                           __SOCKADDR_COMMON_SIZE -
                           sizeof (in_port_t) -
                           sizeof (struct in_addr)];
  };

in_addr定义如下:

struct in_addr
  {
    in_addr_t s_addr;  //存放ipv4地址,用网络字节序表示,in_addr_t就是一个无符号的32位整型
  };

2 IPv6的socket地址定义,定义于/usr/include/netinet/in.h

struct sockaddr_in6
  {
    sa_family_t sin6_family;   //地址族,取AF_INET6
    in_port_t sin6_port;        /* 端口号,用网络字节序表示 */
    uint32_t sin6_flowinfo;     /* IPv6 流信息,设置为0n */
    struct in6_addr sin6_addr;  /* IPv6 地址 */
    uint32_t sin6_scope_id;     /* IPv6 id范围 */
  };

in6_addr定义如下:

struct in6_addr
  {
    union
      {
        uint8_t __u6_addr8[16];
#if defined __USE_MISC || defined __USE_GNU
        uint16_t __u6_addr16[8];
        uint32_t __u6_addr32[4];
#endif
      } __in6_u;
#define s6_addr                 __in6_u.__u6_addr8
#if defined __USE_MISC || defined __USE_GNU
# define s6_addr16              __in6_u.__u6_addr16
# define s6_addr32              __in6_u.__u6_addr32
#endif
  };

3 UNIX本地域协议族定义,定义于/usr/include/linux/un.h

#define UNIX_PATH_MAX   108

struct sockaddr_un {
        __kernel_sa_family_t sun_family; /* 地址族,取AF_UNIX */
        char sun_path[UNIX_PATH_MAX];   /* 文件全路径名*/
};

这些专用的socket地址结构显然比通用的socket地址更清楚,把各个信息用不同的字段来表示。但要注意的是,socket api函数使用的是通用的地址结构,因此我们具体使用的时候,最终要把专用地址结构转换为通用地址结构,不过可以强制转换。

四 IP地址的转换

1 点睛

函数inet_addr将点分十进制IP地址转换为二进制地址

函数inet_aton将点分十进制IP地址转换为二进制地址

函数inet_ntoa将二进制地址转换为点分十进制IP地址

2 代码

#include <stdio.h>
#include <arpa/inet.h>

int main(int argc, const char * argv[])
{

    struct in_addr ia;
    inet_aton("172.16.2.6", &ia);
    printf("ia.s_addr=0x%x\n",ia.s_addr);
    printf("real_ip=%s\n",inet_ntoa(ia));
    return 0;
}

3 运行

[root@localhost test]# g++ -o test test.cpp[root@localhost test]# ./testia.s_addr=0x60210acreal_ip=172.16.2.6

 

没有更多推荐了,返回首页