muduo网络库剖析——网络地址InetAddress类
- 前情
- 从muduo到my_muduo
- 概要
- socketaddr_in
- 介绍
- 成员用法
- 网络地址转换函数
- 框架与细节
- 成员
- 函数
- 使用方法
- 源码
- 结尾
前情
从muduo到my_muduo
作为一个宏大的、功能健全的muduo库,考虑的肯定是众多情况是否可以高效满足;而作为学习者,我们需要抽取其中的精华进行简要实现,这要求我们足够了解muduo库。
做项目 = 模仿 + 修改,不要担心自己学了也不会写怎么办,重要的是积累,学到了这些方法,如果下次在遇到通用需求的时候你能够回想起之前的解决方法就够了。送上一段话!
概要
socketaddr_in
介绍
sockaddr是一个用于存储套接字地址的结构体,它可以表示不同类型的地址,包括IPv4、IPv6等。它通常用于bind、connect、recvfrom和sendto等函数的参数,指明地址信息。然而,sockaddr结构体存在一些弊端,其中最主要的是sa_data字段将目标地址和端口信息混在一起,这导致在使用某些网络函数时需要额外的处理才能正确地获取地址和端口信息。
为了解决sockaddr的缺陷,出现了sockaddr_in结构体。与sockaddr相比,sockaddr_in结构体将port和addr分开储存在两个变量中,分别对应sin_port和sin_addr字段。这种结构使得地址和端口信息更加清晰,易于处理。同时,sockaddr_in结构体还提供了方便的函数来操作地址和端口信息,例如inet_pton和inet_ntop等。
总的来说,sockaddr_in相对于sockaddr具有更加清晰的结构和易于操作的字段,因此在网络编程中通常更受欢迎。虽然sockaddr仍然可以使用,但在处理IPv4地址时建议使用sockaddr_in结构体。
成员用法
sin_family
sin_family是sockaddr_in结构体中的一个字段,用于指定地址家族。在IPv4中,sin_family的值为AF_INET,表示使用IPv4地址。在IPv6中,sin_family的值为AF_INET6,表示使用IPv6地址。因此,sin_family字段用于指明地址类型,以便正确地处理和传输网络通信数据。sin_addr
sin_addr是一个用于存储IP地址的字段,它是sockaddr_in结构体的一部分。sin_addr字段通常用于表示IPv4地址,使用无符号长整型(unsigned long)类型来存储32位的IP地址。通过使用inet_addr函数可以将字符串形式的IP地址转换为unsigned long类型的IP地址,并将其赋值给sin_addr字段。在处理网络通信时,sin_addr字段用于指明目标主机或服务器的IP地址,以便正确地建立连接和传输数据。需要注意的是,sin_addr字段的值应该以网络字节序存储,因为网络通信中通常使用网络字节序进行数据传输。sin_port
sin_port是一个用于存储端口号的字段,它是sockaddr_in结构体的一部分。sin_port字段的数据类型是16位的无符号整数,用于表示端口号。端口号的取值范围通常为065535,其中01023为系统保留的端口号,一般由系统分配给特定的服务程序。用户可以使用1024~65535之间的端口号。在处理网络通信时,sin_port字段用于指明目标主机或服务器的端口号,以便正确地建立连接和传输数据。需要注意的是,sin_port字段的值应该以网络字节序存储,因为网络通信中通常使用网络字节序进行数据传输。如果需要在主机字节序和网络字节序之间进行转换,可以使用htons函数将主机字节序转换为网络字节序,使用ntohs函数将网络字节序转换为主机字节序。sin_zero
sin_zero是一个通常不使用的字段,它是sockaddr_in结构体的一部分。sin_zero字段的存在只是为了与通用套接字地址结构struct sockaddr在内存中对齐,通常会被置为0。因此,在实际的网络编程中,很少使用sin_zero字段。
网络地址转换函数
-
int inet_aton(const char *cp, struct in_addr *inp);
inet_aton()将Internet主机地址cp从IPv4数字和点符号转换为二进制形式(以网络字节顺序),并将其存储在inp指向的结构中。Inet_aton()如果地址有效则返回非零,如果无效则返回零。
如果提供的字符串被成功解释,Inet_aton()返回1,如果字符串无效(错误时没有设置errno)则返回0。
-
in_addr_t inet_addr(const char *cp);
inet_addr()函数的作用是:将Internet主机地址cp从IPv4的数字点法转换为网络字节顺序的二进制数据。如果输入无效,则返回INADDR_NONE(通常为-1)。使用这个函数是有问题的,因为-1是一个有效的地址(255.255.255.255)。避免使用它,而应使用inet_aton()、inet_pton(3)或getaddrinfo(3),它们提供了一种更清晰的方式来指示错误返回。 -
in_addr_t inet_network(const char *cp);
inet_network()函数的作用是将cp (IPv4数字和点表法中的字符串)转换为适合用作Internet网络地址的主机字节顺序的数字。如果成功,则返回转换后的地址。如果输入无效,则返回-1。 -
char *inet_ntoa(struct in_addr in);
inet_ntoa()函数的作用是:将Internet主机地址(以网络字节顺序给出)转换为IPv4点分十进制格式的字符串。字符串在静态分配的缓冲区中返回,后续调用将覆盖该缓冲区。 -
inet_pton(int af, const char *src, void *dst)
该函数将字符串src转换为af地址族中的网络地址结构,然后将该网络地址结构复制到dst。参数af必须是AF_INET或AF_INET6。DST是按网络字节顺序写的。
Inet_pton()成功时返回1(网络地址已成功转换)。0如果SRC不包含表示指定地址族中有效网络地址的字符串,则返回。如果af不包含有效的地址族,则返回-1并将errno设置为EAFNOSUPPORT。
与inet_aton(3)和inet_addr(3)不同,inet_pton()支持IPv6地址。另一方面,inet pton()只接受点分十进制表示法的IPv4地址,而inet_aton(3)和inet_addr(3)允许更通用的数字和点表示法(十六进制和八进制数字格式,以及不需要显式写入所有四个字节的格式)。对于同时处理IPv6 ad- 1地址和数字点表示法的IPv4地址的接口,请参见getaddrinfo(3). -
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
这个函数将af地址族中的网络地址结构src转换为字符串。结果字符串被复制到dst所指向的缓冲区,dst必须是一个非空指针。调用方在参数size中指定此缓冲区中可用的字节数。
Inet_ntop()扩展了inet_ntoa(3)函数以支持多个地址族,inet_ntoa(3)现在被认为是不推荐的,而支持Inet_ntop()。目前支持的地址族如下:
如果成功,inet_ntop()返回一个指向dst的非空指针。如果有错误,则返回NULL,并设置errno来指示错误。 -
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
框架与细节
成员
成员是一个sockaddr_in结构体,具体见上面介绍。
函数
主要是构造函数,构造分为sockaddr_in构造和ip地址,端口号构造。网络地址结构的设置与获取,以及网络地址的打印。
其中主要用到的函数,inet_pton与inet_ntop,将字符串src转换为af地址族中的网络地址结构与将af地址族中的网络地址结构src转换为字符串。htons与ntohs,主机端口号变成网络端口号与网络端口号变成主机端口号。
使用方法
源码
//InetAddress.h
#pragma once#include <string>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>#include "copyable.h"
#include "Log.h"class InetAddress : copyable {
public:InetAddress(uint16_t port = 8000, const char* ip = "127.0.0.1");InetAddress(sockaddr_in addr) {addr_ = addr;}uint16_t toPort() const {return ntohs(addr_.sin_port);}std::string toIp() const;std::string toIpPort() const;sa_family_t family() const {return addr_.sin_family;}sockaddr_in* getSockAddr() { return &addr_; }void setSockAddr(sockaddr_in addr) { addr_ = addr; }void setSockAddr(uint16_t port, const char* ip);
private:struct sockaddr_in addr_;
};//InetAddress.cpp
#include "InetAddress.h"std::string InetAddress::toIpPort() const {return toIp() + " : " + std::to_string(toPort());
}std::string InetAddress::toIp() const {char buf[64] = {0};if (::inet_ntop(addr_.sin_family, &addr_.sin_addr, buf, sizeof buf) == nullptr) {LOG_ERROR("%s--%s--%d--%d : inet_ntop error\n", __FILE__, __FUNCTION__, __LINE__, errno);}return buf;
}InetAddress::InetAddress(uint16_t port, const char* ip) {bzero(&addr_, sizeof addr_);addr_.sin_family = AF_INET;if (::inet_pton(addr_.sin_family, ip, &addr_.sin_addr) != 1) {LOG_FATAL("%s--%s--%d--%d : inet_pton error\n", __FILE__, __FUNCTION__, __LINE__, errno);}addr_.sin_port = htons(port);
}void InetAddress::setSockAddr(uint16_t port, const char* ip) {bzero(&addr_, sizeof addr_);addr_.sin_family = AF_INET;if (::inet_pton(addr_.sin_family, ip, &addr_.sin_addr) != 1) {LOG_FATAL("%s--%s--%d--%d : inet_pton error\n", __FILE__, __FUNCTION__, __LINE__, errno);}addr_.sin_port = htons(port);
}
结尾
以上就是网络地址InetAddress类的相关介绍,以及我在进行项目重写的时候遇到的一些问题,和我自己的一些心得体会。发现写博客真的会记录好多你的成长,而且对于一个好的项目,写博客也是证明你确实有过深度思考,并且在之后面试或者工作时遇到同样的问题能够进行复盘的一种有效的手段。所以,希望uu们也可以像我一样,养成写博客的习惯,逐渐脱离菜鸡队列,向大佬前进!!!加油!!!
也希望我能够完成muduo网络库项目的深度学习与重写,并在功能上能够拓展。也希望在完成这个博客系列之后,能够引导想要学习muduo网络库源码的人,更好地探索这篇美丽繁华的土壤。致敬chenshuo大神!!!
鉴于博主只是一名平平无奇的大三学生,没什么项目经验,所以可能很多东西有所疏漏,如果有大神发现了,还劳烦您在评论区留言,我会努力尝试解决问题!