socket编程之IP与二进制转换

906 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情


现将目录贴下:

  • 什么是socket
  • IP地址、struct以及地址转换
  • 从IPv4迁移到IPv6
  • System call 或 Bust
    • getaddrinfo()—准备开始!
    • socket()—拿到文件描述符!
    • bind()—我在监听哪个端口?
    • connect()—嘿,你好啊!
    • listen()—会有人联系我吗?
    • accept()—感谢呼叫3490端口
    • send() and recv()—跟我唠唠吧,宝儿!
    • sendto() and recvfrom()—用DGRAM风格跟我说话
    • close() and shutdown()—滚犊子!
    • getpeername()—你哪位?
    • gethostname()—我是谁?
  • Client-Server基础
    • 一个简单的流服务器
    • 一个简单的流客户端
    • Datagram Sockets
  • 技术进阶
    • Blocking—何谓阻塞?
    • poll()—同步的I/O多路复用
    • select()—老古董的同步I/O多路复用
    • 数据只传了一部分怎么办?
    • Serialization-如何封装数据?
    • 数据封装
    • 广播数据包-大声说「Hello,World」

不得不说,我们真的很幸运,如今已经有了很多函数可以让我们操作IP地址,而不需要我们自己用long类型数据以及<<运算符来处理它们。

假如你声明了一个数据结构struct sockaddr_in ina,你还有一个“10.12.110.57”或“2001:db8:63b3:1::3490”这样的IP地址,该怎么把IP地址存入ina呢。

IP转二进制

你可以使用inet_pton()将点分十进制形式的IPv4地址保存在ina中,但如果要保存IPv6地址的话,我们就需要struct sockaddr_in6 ina6了。

pton”的全称是“presentation to network”,也可以是“printable to network”,选一个能帮助你记忆的就行。

具体的转换如下代码所示:

struct sockaddr_in sa; // IPv4
struct sockaddr_in6 sa6; // IPv6
    
inet_pton(AF_INET, "10.12.110.57", &(sa.sin_addr)); // IPv4
inet_pton(AF_INET6, "2001:db8:63b3:1::3490", &(sa6.sin6_addr)); // IPv6

小记:原本的老方法是使用名为inet_addr()或是 inet_aton()的函数;这些函数已经过时了,而且不适用于 IPv6

上面的程序写的比较简单,而且不够健壮,因为没有进行错误检查。inet_pton()在发生错误时会返回-1,而若地址无效则返回0。所以在使用之前要检查返回值,并保证返回结果是大于 0 的。

现在我们可以将字符串格式的IP地址转换为二进制形式了。那么反过来需要怎么做?

二进制转IP

如果我们已经有了struct in_addr,怎么将点分十进制的IP打印出来?(或者我们有struct in6_addr,怎么打印字符串类型的IPv6地址呢?)

我们可以使用inet_ntop()函数,看代码:

ntop”的全称是“network to presentation”,也可以是“network to printable”,选一个能帮助你记忆的就行。

// IPv4:
char ip4[INET_ADDRSTRLEN];  // space to hold the IPv4 string
struct sockaddr_in sa;      // 假设sa中保存了ip信息
inet_ntop(AF_INET, &(sa.sin_addr), ip4, INET_ADDRSTRLEN);
printf("The IPv4 address is: %s\n", ip4);
​
​
// IPv6:
char ip6[INET6_ADDRSTRLEN]; // space to hold the IPv6 string
struct sockaddr_in6 sa6;    // 假设sa6种保存了ip信息
inet_ntop(AF_INET6, &(sa6.sin6_addr), ip6, INET6_ADDRSTRLEN);
printf("The address is: %s\n", ip6);

当我们调用inet_ntop(),你需要传递IP地址类型(IPv4 或者 IPv6),一个指向保存字符串结果的指针以及该字符串的最大长度。有两个宏(可以理解为常量)可以很方便地帮助我们表示要存储的IPv4或者IPv6地址字符串的最大长度,分别是INET_ADDRSTRLENINET6_ADDRSTRLEN

小记:原本的老方法是使用名为inet_ntoa()的函数,这个函数已经过时了,而且不适用于 IPv6

最后,本文讲到的函数只能用于数值型的IP地址上,它们不会用DNS做域名解析,所以你传入“www.chanmufeng.com”这样的域名是没有用的。

关于域名查询,我们将会使用getaddrinfo()函数,之前的文章提过一嘴,后文将会详细讲述,敬请期待。