Socket 编程-地址转换

2 篇文章 0 订阅
1 篇文章 0 订阅

一、三种地址结构体

在Socket编程中,有三种常见的结构类型,它们用来存放socket地址信息。这三种结构类型分别为struct in_addr、struct sockaddr、struct sockaddr_in

1. struct in_addr

struct in_addr 用来存储IP地址,对于IPv4来说,IP地址为32位无符号整数。其定义在头文件 <netinet/in.h>中,详细信息如下:

/* Internet address.  */
typedef uint32_t in_addr_t;
struct in_addr
{
    in_addr_t s_addr;
};

2. struct sockaddr

struct sockaddr结构用来保存套接字地址信息,其定义在头文件 <bits/socket.h> 中,详细信息如下:

struct sockaddr 
{
        unsigned short int sa_family; /* 地址家族, AF_xxx */
        char sa_data[14]; /*14字节协议地址*/
};
  • sa_family是地址族类型,常见为 AF_INET
  • sa_data 包含套接字中的IP地址信息和端口信息。

3. struct sockaddr_in

struct sockaddr结构中 sa_data 字段包含较多信息,不利于方便编程和对其进行赋值,因此建立了struct sockaddr_in结构,该结构与 struct sockaddr 结构大小相等,能更好处理struct sockaddr结构中的数据。
其定义在头文件 <netinet/in.h>中,详细信息如下:

struct sockaddr_in
  {
    unsigned short int sin_family;
    uint16_t sin_port;                 /* Port number.  */
    struct in_addr sin_addr;            /* Internet address.  */

    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[8];    /* sizeof (struct sockaddr) -
                           __SOCKADDR_COMMON_SIZE -
                           sizeof (in_port_t) -
                           sizeof (struct in_addr) = 16 - 2 - 2 - 4 = 8*/
  };

sin_zero 应该使用函数bzero()memset() 来全部置零。它被加入到这个结构,以使struct sockaddr_in的长度和 struct sockaddr 一样
通过sin_zero补 0 ,sockaddr_in结构体 和 sockaddr 结构体等效,一个指向 sockaddr_in 结构体的指针也可以被指向结构体 sockaddr 并且代替它。

4. 总结

  • 结构体 struct in_addr 用于存储IP地址;
  • 结构体 struct sockaddr 用于存储地址信息(包含地址族、端口和IP地址(struct in_addr)),但是IP信息和端口信息都在同一个变量(sa_data)中表示;
  • 结构体 struct sockaddr_instruct sockaddr 等效,只是将每个信息都用一个变量表示,使用时更清晰方便。

二、IP地址转换

IP地址转换函数是指完成点分十进制IP地址与二进制IP地址之间的相互转换。
IP地址转换主要有inet_aton、inet_addr和inet_ntoa这三个函数完成。

1. 将点分十进制IP地址转换为二进制地址

  • in_addr_t inet_addr (const char *cp)
  • int inet_aton (const char *cp, struct in_addr *inp)

1. “ntoa"的含义是"network to ascii”,“aton"的含义是"ascii to network”
2. inet_addr 返回二进制地址,返回值是 in_addr_t 类型(struct in_addr结构体中的变量)
3. inet_aton 将生成的二进制地址写入第二个参数,参数类型为 指向struct in_addr的指针

2. 将二进制地址转换为点分十进制IP地址

  • char *inet_ntoa(struct in_addr in)

1. inet_ntoa() 将结构体 in_addr 作为一个参数。
2. 同样需要注意的是它返回的是一个指向静态内存的指针。

3. 代码示例:

#include <stdio.h>
#include <arpa/inet.h>
int main()
{
        char *a1, *a2;
        struct sockaddr_in ina;
        struct sockaddr_in ina2;
  
        ina.sin_addr.s_addr = inet_addr("198.92.129.1");
	    inet_aton("132.241.5.10", &ina2.sin_addr);
        
        a1 = inet_ntoa(ina.sin_addr);     /* 这是198.92.129.1 */
        printf("address 1: %s\n", a1);
        a2 = inet_ntoa(ina2.sin_addr);    /* 这是132.241.5.10 */

        printf("address 1: %s\n", a1);
        printf("address 2: %s\n", a2);
        return 0;
}

注意,以上代码,输出的是:

address 1: 198.92.129.1
address 1: 132.241.5.10
address 2: 132.241.5.10

而不是

address 1: 198.92.129.1
address 1: 198.92.129.1
address 2: 132.241.5.10

前面说到,inet_ntoa() 返回的是一个指向静态内存的指针
所以每次调用 inet_ntoa(),它就将修改此指针指向的内存的内容,然后返回此指针,因此会覆盖上次调用时所得的IP地址
a1指针 与 inet_ntoa()返回的指针 指向同一块内存。
当第二次调用inet_ntoa()时,inet_ntoa() 控制的指针指向的内容发生变化,即a1指针指向的内容发生变化,因此,最终a1,a2指向同一个位置,表示相同的信息。
因此如果需要保存返回的IP地址,需要使用strcpy或其它拷贝函数

静态内存的逻辑大致如下:

#include <stdio.h>
#include <stdlib.h>

static char local_buf[8];
static char* buffer = local_buf;
char* test(char* str)
{
        sprintf(buffer, "%s", str);
        return buffer;
}
int main()
{
        char* p1 = "p1";
        char* p1_test = test(p1);
        printf("p1_test:%s\n", p1_test);

        char* p2 = "p2";
        char* p2_test = test(p2);
        printf("p1_test:%s\n", p1_test);
        printf("p2_test:%s\n", p2_test);
        return 0;
}

输出为

p1_test:p1
p1_test:p2
p2_test:p2

而不是

p1_test:p1
p1_test:p1
p2_test:p2

附录. inet_ntoa 源码

位于 glibc-2.4/inet/Inet_ntoa.c 文件:

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <bits/libc-lock.h>

/* The interface of this function is completely stupid, it requires a
   static buffer.  We relax this a bit in that we allow at least one
   buffer for each thread.  */

/* This is the key for the thread specific memory.  */
static __libc_key_t key;

/* If nonzero the key allocation failed and we should better use a
   static buffer than fail.  */
static char local_buf[18];
static char *static_buf;

/* Destructor for the thread-specific data.  */
static void init (void);
static void free_key_mem (void *mem);


char *
inet_ntoa (struct in_addr in)
{
  __libc_once_define (static, once);
  char *buffer;
  unsigned char *bytes;

  /* If we have not yet initialized the buffer do it now.  */
  __libc_once (once, init);

  if (static_buf != NULL)
    buffer = static_buf;
  else
    {
      /* We don't use the static buffer and so we have a key.  Use it
	 to get the thread-specific buffer.  */
      buffer = __libc_getspecific (key);
      if (buffer == NULL)
	{
	  /* No buffer allocated so far.  */
	  buffer = malloc (18);
	  if (buffer == NULL)
	    /* No more memory available.  We use the static buffer.  */
	    buffer = local_buf;
	  else
	    __libc_setspecific (key, buffer);
	}
    }

  bytes = (unsigned char *) &in;
  __snprintf (buffer, 18, "%d.%d.%d.%d",
	      bytes[0], bytes[1], bytes[2], bytes[3]);

  return buffer;
}


/* Initialize buffer.  */
static void
init (void)
{
  if (__libc_key_create (&key, free_key_mem))
    /* Creating the key failed.  This means something really went
       wrong.  In any case use a static buffer which is better than
       nothing.  */
    static_buf = local_buf;
}


/* Free the thread specific data, this is done if a thread terminates.  */
static void
free_key_mem (void *mem)
{
  free (mem);
  __libc_setspecific (key, NULL);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值