本节我们重点来谈论:
时间类和我们的初始化链接地址类
文章目录
Timestamp类
我们为什么要封装一个时间类呢?
这也是一个大型项目必须的基础组建,这样我们不仅可以提高代码的可读性,并且封装细节避免了直接操作时间时可能引发的错误,提高代码的健壮性。
cpp
//时间类
class Timestamp {
public:
Timestamp();
explicit Timestamp(int64_t microSecondsSinceEpoch_);
//返回当前时间-长整形
static Timestamp now();
//将长整型转换为年月日字符串
std::string toString() const;
private:
int64_t microSecondsSinceEpoch_;
};
时间类的作用主要就是以上几个:
- 获取当前时间,返回值是一个长整型
- 长整型转换为年月日
调用方式应为:
cpp
int main () {
cout << Timestamp::now().toString() << endl
return 0;
}
成员函数实现
cpp
#include "Timestamp.h"
#include <time.h>
Timestamp::Timestamp(): microSecondsSinceEpoch_(0) {}
Timestamp::Timestamp(int64_t microSecondsSinceEpoch)
: microSecondsSinceEpoch_(microSecondsSinceEpoch)
{}
Timestamp Timestamp::now()
{
//返回对象实例,对象成员变量是当前的 int64_t
return Timestamp(time(NULL));
}
std::string Timestamp::toString() const
{
char buf[128] = { 0 };
tm *tm_time = localtime(µSecondsSinceEpoch_);
snprintf(buf, 128, "%4d/%02d/%02d %02d:%02d:%02d",
tm_time->tm_year + 1900,
tm_time->tm_mon + 1,
tm_time->tm_mday,
tm_time->tm_hour,
tm_time->tm_min,
tm_time->tm_sec);
return buf;
}
time(NULL)
返回一个 64 位整数,是自 Unix 纪元(1970 年 1 月 1 日 00:00:00 UTC)以来的时间,变量类型为int64_t
InetAddress类
该类也比较简单,就是封装 socket 地址的类型
cpp
class InetAddress {
public:
explicit InetAddress(uint16_t port, std::string ip = "127.0.0.1");
explicit InetAddress(const sockaddr_in &addr)
: addr_(addr)
{}
std::string toIP() const;
std::string toIpPort() const;
uint16_t toPort() const;
const sockaddr_in* getSockAddr() const {return &addr_;}
private:
sockaddr_in addr_;
};
toIP()
返回 InetAddress 对象中存储的 IP 地址的字符串表示toIpPort()
返回 InetAddress 对象中存储的 IP 地址和端口号的字符串表示,格式为 IP:PorttoPort()
返回 InetAddress 对象中存储的端口号。getSockAddr()
返回指向内部 sockaddr_in 结构体的指针,方便在其他网络操作中使用。- 私有成员变量 sockaddr_in addr_:存储 IP 地址和端口号的结构体,包含了网络地址信息。
该类的主要作用就是:
- 提供对 IP 地址和端口号的封装,便于在网络编程中管理和使用;
- 提供方便的方法来获取和表示网络地址和端口的信息;
- 通过提供的函数,能够轻松地转换地址和端口信息为字符串格式,便于调试和日志记录。
具体实现
cpp
InetAddress::InetAddress(uint16_t port, std::string ip) {
bzero(&addr_, sizeof addr_);
addr_.sin_family = AF_INET;
addr_.sin_port = htons(port);
addr_.sin_addr.s_addr = inet_addr(ip.c_str());
}
std::string InetAddress::toIP() const
{
//addr_
char buf[64] = {0};
::inet_ntop(AF_INET, &addr_.sin_addr, buf, sizeof buf);
return buf;
}
std::string InetAddress::toIpPort() const
{
//ip: port
char buf[64] = {0};
::inet_ntop(AF_INET, &addr_.sin_addr, buf, sizeof buf);
size_t end = strlen(buf);
uint16_t port = ntohs(addr_.sin_port);
sprintf(buf+end, ":%u", port);
return buf;
}
uint16_t InetAddress::toPort() const
{
return ntohs(addr_.sin_port);
}
我们可以看出,构造函数已经为我们封装好了sockaddr_in
的配置,我们只需要传入ip和port即可
然后这里主要解释几个函数:
cpp
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
inet_ntop(AF_INET, &addr_.sin_addr, buf, sizeof buf);
inet_ntop 是一个用于将网络地址转换为字符串表示的函数.
af
:地址族,通常为 AF_INET(表示 IPv4)或 AF_INET6(表示 IPv6)。
src
:指向存储有IP的结构体。
dst
:指向用于存储转换后的字符串的缓冲区。
size
:缓冲区的大小。
cpp
int sprintf(char *str, const char *format, ...);
sprintf(buf + end, ":%u", port);
sprintf 是一个 C 标准库函数,用于将格式化的数据写入字符串中。
这里的buf+end
其实就是接着之前写进去的ip后面继续写port端口。