套接字抽象封装,包含TCP服务器、TCP客户端(TCP session)、UDP套接字、与EventPoller关联处理描述符事件,数据发送以及接收处理等。
概述
以上,是套接字封装模块的类图以及每个类的大致功能,上层使用最多的是Socket和SocketHelper两个类。
功能分析
SockNum
- 它有两个成员函数:
- _fd :
- 构造函数中初始化
- 析构函数中关闭
- _type :fd的类型,构造函数中指定,指定后不可更改
- _fd :
就是简单的包装了一下_fd而已
class SockNum {
public:
using Ptr = std::shared_ptr<SockNum>;
typedef enum {
Sock_Invalid = -1,
Sock_TCP = 0,
Sock_UDP = 1
} SockType;
SockNum(int fd, SockType type) {
_fd = fd;
_type = type;
}
~SockNum() {
::shutdown(_fd, SHUT_RDWR);
close(_fd);
}
int rawFd() const {
return _fd;
}
SockType type() {
return _type;
}
void setConnected() {
}
private:
int _fd;
SockType _type;
};
SockFD
这玩意啥也没有干呀,就是在析构的时候不再监听fd,并关闭fd而已。
注意,这里的fd不是在这个类中设置被EventPoller监听的,仅仅在析构是不监听fd了
//socket 文件描述符的包装
//在析构时自动溢出监听并close套接字
//防止描述符溢出
class SockFD : public noncopyable {
public:
using Ptr = std::shared_ptr<SockFD>;
/**
* 创建一个fd对象
* @param num 文件描述符,int数字
* @param poller 事件监听器
*/
SockFD(int num, SockNum::SockType type, const EventPoller::Ptr &poller) {
_num = std::make_shared<SockNum>(num, type);
_poller = poller;
}
/**
* 复制一个fd对象
* @param that 源对象
* @param poller 事件监听器
*/
SockFD(const SockFD &that, const EventPoller::Ptr &poller) {
_num = that._num;
_poller = poller;
if (_poller == that._poller) {
throw std::invalid_argument("copy a SockFD with same poller!");
}
}
~SockFD() {
auto num = _num;
_poller->delEvent(_num->rawFd(), [num](bool) {});
}
void setConnected() {
_num->setConnected();
}
int rawFd() const {
return _num->rawFd();
}
SockNum::SockType type() {
return _num->type();
}
private:
SockNum::Ptr _num;
EventPoller::Ptr _poller;
};
socket
Socket封装了针对文件描述符的各种操作,包括TCP与UDP套接口基础概念封装、数据的发送与接收控制、事件回调处理等。
Socket可用于TCP Server监听(listen socket)、TCP Server会话(accept后的socket)、TCP Client、UDP Server/Client类型。
构造&&析构
socket构造函数
啥也没有干,仅仅重置了一下成员函数
- 声明:
Socket(const EventPoller::Ptr &poller = nullptr, bool enable_mutex = true);
- 实现:
Socket::Socket(const EventPoller::Ptr &poller, bool enable_mutex) :
_mtx_sock_fd(enable_mutex), _mtx_event(enable_mutex),
_mtx_send_buf_waiting(enable_mutex), _mtx_send_buf_sending(enable_mutex){
_poller = poller;
if (!_poller) {
_poller = EventPollerPool::Instance().getPoller();
}
setOnRead(nullptr);
setOnErr(nullptr);
setOnAccept(nullptr);
setOnFlush(nullptr);
setOnBeforeAccept(nullptr);
}
析构函数&&closeSock
啥也没有干,就是清理了一下资源
#define LOCK_GUARD(mtx) lock_guard<decltype(mtx)> lck(mtx)
Socket::~Socket() {
closeSock();
}
// closeSock
void Socket::closeSock() {
_con_timer = nullptr;
_async_con_cb = nullptr;
LOCK_GUARD(_mtx_sock_fd);
_sock_fd = nullptr;
}
createSocket
- 声明
/**
* 构造socket对象,尚未有实质操作
* @param poller 绑定的poller线程
* @param enable_mutex 是否启用互斥锁(接口是否线程安全)
*/
static Ptr createSocket(const EventPoller::Ptr &poller = nullptr, bool enable_mutex = true);
- 实现
Socket::Ptr Socket::createSocket(const EventPoller::Ptr &poller, bool enable_mutex){
return std::make_shared<Socket>(poller, enable_mutex);
}
啥也没有干,就是调用了一些socket的构造函数
仅用于TCP Server监听
listen
仅用于TCP Client
connect:创建tcp客户端并异步连接服务器
(1)因为我们是要创建一个新的连接,所以第一步应该是先清理一下之前的设置(将之前打开的资源关闭)
//重置当前socket
closeSock();
从上面我们也可以推测出,这个类是可以复用的
(2)第二步,我们要知道这个类它是继承了std::enable_shared_from_this<Socket>
的
//异步IO Socket对象,包括tcp客户端、服务器和udp套接字
class Socket : public std::enable_shared_from_this<Socket> {
}
为什么要继承std::enable_shared_from_this<Socket>
呢?因为这用于异步操作,因为在异步调用中,异步函数执行的时间点我们是无法确定的,然而异步函数可能会使用到异步调用之前就存在的变量。为了保证该变量在异步函数执期间一直有效,我们可以传递一个指向自身的share_ptr给异步函数,这样在异步函数执行期间share_ptr所管理的对象就不会析构,所使用的变量也会一直有效了(保活)。怎么包含呢?从
weak_ptr<Socket> weak_self = shared_from_this();
SockFD、SocketNum、Socket关系
描述符封装,SockNum作为SockFD的成员变量,SockFD作为Socket的成员变量。