f-stack/app/micro_thread/mt_net.h

719 lines
17 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* Tencent is pleased to support the open source community by making MSEC available.
*
* Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the GNU General Public License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* https://opensource.org/licenses/GPL-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
/**
* @file mt_net.h
* @info 微线程封装的网络接口类
**/
#ifndef __MT_NET_H__
#define __MT_NET_H__
#include "micro_thread.h"
#include "hash_list.h"
#include "mt_api.h"
#include "mt_cache.h"
#include "mt_net_api.h"
namespace NS_MICRO_THREAD {
/**
* @brief 连接类型定义
*/
enum MT_CONN_TYPE
{
TYPE_CONN_UNKNOWN = 0,
TYPE_CONN_SHORT = 0x1, ///< 短连接, 一次交互后关闭
TYPE_CONN_POOL = 0x2, ///< 长连接,每次使用后, 可回收重复使用
TYPE_CONN_SESSION = 0x4, ///< 长连接按session id 复用, 防串包
TYPE_CONN_SENDONLY = 0x8, ///< 只发不收
};
/******************************************************************************/
/* 内部实现部分 */
/******************************************************************************/
class CSockLink;
/**
* @brief 定时回收的对象池模板实现
* @info List必须是tailq, Type 需要有reset函数, releasetime, linkentry字段
*/
template <typename List, typename Type>
class CRecyclePool
{
public:
// 构造函数, 默认60s超时
CRecyclePool() {
_expired = 60 * 1000;
_count = 0;
TAILQ_INIT(&_free_list);
};
// 析构函数, 删除池中元素
~CRecyclePool() {
Type* item = NULL;
Type* tmp = NULL;
TAILQ_FOREACH_SAFE(item, &_free_list, _link_entry, tmp)
{
TAILQ_REMOVE(&_free_list, item, _link_entry);
delete item;
}
_count = 0;
};
// 复用或新创建对象
Type* AllocItem() {
Type* item = TAILQ_FIRST(&_free_list);
if (item != NULL)
{
TAILQ_REMOVE(&_free_list, item, _link_entry);
_count--;
return item;
}
item = new Type();
if (NULL == item)
{
return NULL;
}
return item;
};
// 释放管理对象
void FreeItem(Type* obj) {
//obj->Reset();
TAILQ_INSERT_TAIL(&_free_list, obj, _link_entry);
obj->_release_time = mt_time_ms();
_count++;
};
// 回收句柄
void RecycleItem(uint64_t now) {
Type* item = NULL;
Type* tmp = NULL;
TAILQ_FOREACH_SAFE(item, &_free_list, _link_entry, tmp)
{
if ((now - item->_release_time) < _expired) {
break;
}
TAILQ_REMOVE(&_free_list, item, _link_entry);
delete item;
_count--;
}
};
// 设置自定义的超时时间
void SetExpiredTime(uint64_t expired) {
_expired = expired;
};
private:
List _free_list; ///< 空闲链表
uint64_t _expired; ///< 超时时间
uint32_t _count; ///< 元素计数
};
/**
* @brief 每次IO关联一个句柄对象
*/
class CNetHandler : public HashKey
{
public:
// 句柄状态描述
enum {
STATE_IN_SESSION = 0x1,
STATE_IN_CONNECT = 0x2,
STATE_IN_SEND = 0x4,
STATE_IN_RECV = 0x8,
STATE_IN_IDLE = 0x10,
};
/**
* @brief 节点元素的hash算法, 获取key的hash值
* @return 节点元素的hash值
*/
virtual uint32_t HashValue();
/**
* @brief 节点元素的cmp方法, 同一桶ID下, 按key比较
* @return 节点元素的hash值
*/
virtual int HashCmp(HashKey* rhs);
// 同步收发接口
int32_t SendRecv(void* data, uint32_t len, uint32_t timeout);
// 获取返回buff信息, 有效期直到helper析构
void* GetRspBuff() {
if (_rsp_buff != NULL) {
return _rsp_buff->data;
} else {
return NULL;
}
};
// 获取返回buff信息, 有效期直到helper析构
uint32_t GetRspLen() {
if (_rsp_buff != NULL) {
return _rsp_buff->data_len;
} else {
return 0;
}
};
// 设置rsp信息
void SetRespBuff(TSkBuffer* buff) {
if (_rsp_buff != NULL) {
delete_sk_buffer(_rsp_buff);
_rsp_buff = NULL;
}
_rsp_buff = buff;
};
// 设置协议的类型, 默认UDP
void SetProtoType(MT_PROTO_TYPE type) {
_proto_type = type;
};
// 设置连接类型, 默认长连接
void SetConnType(MT_CONN_TYPE type) {
_conn_type = type;
};
// 设置目的IP地址
void SetDestAddress(struct sockaddr_in* dst) {
if (dst != NULL) {
memcpy(&_dest_ipv4, dst, sizeof(*dst));
}
};
// 设置session本次session id信息, 必须非0
void SetSessionId(uint64_t sid) {
_session_id = sid;
};
// 设置session解析回调函数
void SetSessionCallback(CHECK_SESSION_CALLBACK function) {
_callback = function;
};
// 获取回调函数信息
CHECK_SESSION_CALLBACK GetSessionCallback() {
return _callback;
};
public:
// 关联连接对象
void Link(CSockLink* conn);
// 解耦连接对象
void Unlink();
// 检查必要的参数信息
int32_t CheckParams();
// 获取链接, 同时关联到等待连接的队列中
int32_t GetConnLink();
// 检查必要的参数信息
int32_t WaitConnect(uint64_t timeout);
// 检查必要的参数信息
int32_t WaitSend(uint64_t timeout);
// 检查必要的参数信息
int32_t WaitRecv(uint64_t timeout);
// 关联在等待连接队列
void SwitchToConn();
// 切换到发送队列
void SwitchToSend();
// 切换到接收队列
void SwitchToRecv();
// 切换到空闲状态
void SwitchToIdle();
// 解耦连接对象
void DetachConn();
// 注册session管理
bool RegistSession();
// 取消注册session
void UnRegistSession();
// 跳过发送的请求长度
uint32_t SkipSendPos(uint32_t len);
// 设置返回码
void SetErrNo(int32_t err) {
_err_no = err;
};
// 获取关联的线程信息
MicroThread* GetThread() {
return _thread;
};
// 获取待发送的指针与数据长度
void GetSendData(void*& data, uint32_t& len) {
data = _req_data;
len = _req_len;
};
// 复用接口
void Reset();
// 构造与析构
CNetHandler();
~CNetHandler();
// 队列快捷访问的宏定义
TAILQ_ENTRY(CNetHandler) _link_entry;
uint64_t _release_time;
protected:
MicroThread* _thread; ///< 关联线程指针对象
MT_PROTO_TYPE _proto_type; ///< 协议类型
MT_CONN_TYPE _conn_type; ///< 连接类型
struct sockaddr_in _dest_ipv4; ///< ipv4目的地址
uint64_t _session_id; ///< 会话ID
CHECK_SESSION_CALLBACK _callback; ///< 会话提取回调函数
uint32_t _state_flags; ///< 内部状态字段
int32_t _err_no; ///< 返回码信息
void* _conn_ptr; ///< socket 链路指针
uint32_t _send_pos; ///< 已发送的pos位置
uint32_t _req_len; ///< 请求包长度
void* _req_data; ///< 请求包指针
TSkBuffer* _rsp_buff; ///< 应答buff信息
};
typedef TAILQ_HEAD(__NetHandlerList, CNetHandler) TNetItemList; ///< 高效的双链管理
typedef CRecyclePool<TNetItemList, CNetHandler> TNetItemPool; ///< 定时回收的对象池
/**
* @brief 长连接链路对象
*/
class CSockLink : public KqueuerObj
{
public:
// 句柄状态描述
enum {
LINK_CONNECTING = 0x1,
LINK_CONNECTED = 0x2,
};
// 状态队列定义
enum {
LINK_IDLE_LIST = 1,
LINK_CONN_LIST = 2,
LINK_SEND_LIST = 3,
LINK_RECV_LIST = 4,
};
// 检查或创建socket句柄
int32_t CreateSock();
// 关闭链路的句柄
void Close();
// 发起连接过程
bool Connect();
bool Connected() {
return (_state & LINK_CONNECTED);
}
// 异常终止的处理函数
void Destroy();
// 获取管理链表
TNetItemList* GetItemList(int32_t type);
// 管理句柄信息
void AppendToList(int32_t type, CNetHandler* item);
// 管理句柄信息
void RemoveFromList(int32_t type, CNetHandler* item);
// 获取目标ip信息
struct sockaddr_in* GetDestAddr(struct sockaddr_in* addr);
// 发起连接过程
int32_t SendData(void* data, uint32_t len);
// udp发送数据
int32_t SendCacheUdp(void* data, uint32_t len);
// tcp发送数据
int32_t SendCacheTcp(void* data, uint32_t len);
// 尝试接收更多的数据到临时buff
void ExtendRecvRsp();
// 数据分发处理过程
int32_t RecvDispath();
// 或者回调函数, 优先从排队等待中获取, 备份从父节点获取
CHECK_SESSION_CALLBACK GetSessionCallback();
// TCP接收数据流处理与分发
int32_t DispathTcp();
// UDP接收数据流处理与分发
int32_t DispathUdp();
// 查询本地sessionid关联的session信息
CNetHandler* FindSession(uint64_t sid);
/**
* @brief 可读事件通知接口, 考虑通知处理可能会破坏环境, 可用返回值区分
* @return 0 该fd可继续处理其它事件; !=0 该fd需跳出回调处理
*/
virtual int InputNotify();
/**
* @brief 可写事件通知接口, 考虑通知处理可能会破坏环境, 可用返回值区分
* @return 0 该fd可继续处理其它事件; !=0 该fd需跳出回调处理
*/
virtual int OutputNotify();
/**
* @brief 异常通知接口
* @return 忽略返回值, 跳过其它事件处理
*/
virtual int HangupNotify();
// 构造与析构函数
CSockLink();
~CSockLink();
// 清理置初始化逻辑
void Reset();
// 通知唤醒线程
void NotifyThread(CNetHandler* item, int32_t result);
// 通知唤醒线程
void NotifyAll(int32_t result);
// 设置协议类型, 决定buff池的指针
void SetProtoType(MT_PROTO_TYPE type);
// 设置上级指针信息
void SetParentsPtr(void* ptr) {
_parents = ptr;
};
// 获取上级节点指针
void* GetParentsPtr() {
return _parents;
};
// 获取上次的访问时间
uint64_t GetLastAccess() {
return _last_access;
};
public:
// 队列快捷访问的宏定义
TAILQ_ENTRY(CSockLink) _link_entry;
uint64_t _release_time;
private:
TNetItemList _wait_connect;
TNetItemList _wait_send;
TNetItemList _wait_recv;
TNetItemList _idle_list;
MT_PROTO_TYPE _proto_type;
int32_t _errno;
uint32_t _state;
uint64_t _last_access;
TRWCache _recv_cache;
TSkBuffer* _rsp_buff;
void* _parents;
};
typedef TAILQ_HEAD(__SocklinkList, CSockLink) TLinkList; ///< 高效的双链管理
typedef CRecyclePool<TLinkList, CSockLink> TLinkPool; ///< 定时回收的对象池
class CDestLinks : public CTimerNotify, public HashKey
{
public:
// 构造函数
CDestLinks();
// 析构函数
~CDestLinks();
// 重置复用的接口函数
void Reset();
// 启动定时器
void StartTimer();
// 获取一个连接link, 暂时按轮询
CSockLink* GetSockLink();
// 释放一个连接link
void FreeSockLink(CSockLink* sock);
// 获取协议类型
MT_PROTO_TYPE GetProtoType() {
return _proto_type;
};
// 获取连接类型
MT_CONN_TYPE GetConnType() {
return _conn_type;
};
// 设置关键信息
void SetKeyInfo(uint32_t ipv4, uint16_t port, MT_PROTO_TYPE proto, MT_CONN_TYPE conn) {
_addr_ipv4 = ipv4;
_net_port = port;
_proto_type = proto;
_conn_type = conn;
};
// 拷贝KEY信息
void CopyKeyInfo(CDestLinks* key) {
_addr_ipv4 = key->_addr_ipv4;
_net_port = key->_net_port;
_proto_type = key->_proto_type;
_conn_type = key->_conn_type;
};
// 获取IP port信息
void GetDestIP(uint32_t& ip, uint16_t& port) {
ip = _addr_ipv4;
port = _net_port;
};
/**
* @brief 超时通知函数, 检查空闲链路, 检查配置链路个数
*/
virtual void timer_notify();
/**
* @brief 节点元素的hash算法, 获取key的hash值
* @return 节点元素的hash值
*/
virtual uint32_t HashValue() {
return _addr_ipv4 ^ (((uint32_t)_net_port << 16) | (_proto_type << 8) | _conn_type);
};
/**
* @brief 节点元素的cmp方法, 同一桶ID下, 按key比较
* @return 节点元素的hash值
*/
virtual int HashCmp(HashKey* rhs) {
CDestLinks* data = (CDestLinks*)(rhs);
if (!data) {
return -1;
}
if (this->_addr_ipv4 != data->_addr_ipv4) {
return (this->_addr_ipv4 > data->_addr_ipv4) ? 1 : -1;
}
if (this->_net_port != data->_net_port) {
return (this->_net_port > data->_net_port) ? 1 : -1;
}
if (this->_proto_type != data->_proto_type) {
return (this->_proto_type > data->_proto_type) ? 1 : -1;
}
if (this->_conn_type != data->_conn_type) {
return (this->_conn_type > data->_conn_type) ? 1 : -1;
}
return 0;
};
// 设置session解析回调函数
void SetDefaultCallback(CHECK_SESSION_CALLBACK function) {
_dflt_callback = function;
};
// 获取回调函数信息
CHECK_SESSION_CALLBACK GetDefaultCallback() {
return _dflt_callback;
};
// 队列快捷访问的宏定义
TAILQ_ENTRY(CDestLinks) _link_entry;
uint64_t _release_time;
private:
uint32_t _timeout; ///< idle的超时时间
uint32_t _addr_ipv4; ///< ip地址
uint16_t _net_port; ///< port 网络序列
MT_PROTO_TYPE _proto_type; ///< 协议类型
MT_CONN_TYPE _conn_type; ///< 连接类型
uint32_t _max_links; ///< 最大连接数
uint32_t _curr_link; ///< 当前连接数
TLinkList _sock_list; ///< 连接链表
CHECK_SESSION_CALLBACK _dflt_callback; ///< 默认的check函数
};
typedef TAILQ_HEAD(__DestlinkList, CDestLinks) TDestList; ///< 高效的双链管理
typedef CRecyclePool<TDestList, CDestLinks> TDestPool; ///< 定时回收的对象池
/**
* @brief 连接管理工厂模型
*/
class CNetMgr
{
public:
/**
* @brief 消息buff的全局管理句柄接口
* @return 全局句柄指针
*/
static CNetMgr* Instance (void);
/**
* @brief 消息清理接口
*/
static void Destroy(void);
// 查询是否已经存在同一个sid的对象
CNetHandler* FindNetItem(CNetHandler* key);
// 注册一个item, 先查询后插入, 保证无冲突
void InsertNetItem(CNetHandler* item);
// 移除一个item对象
void RemoveNetItem(CNetHandler* item);
// 查询或创建一个目标ip的links节点
CDestLinks* FindCreateDest(CDestLinks* key);
// 删除掉已有的目标链路信息
void DeleteDestLink(CDestLinks* dst);
// 查询是否已经存在同一个sid的对象
CDestLinks* FindDestLink(CDestLinks* key);
// 注册一个item, 先查询后插入, 保证无冲突
void InsertDestLink(CDestLinks* item);
// 移除一个item对象
void RemoveDestLink(CDestLinks* item);
/**
* @brief 消息buff的析构函数
*/
~CNetMgr();
/**
* @brief 回收资源信息
*/
void RecycleObjs(uint64_t now);
// 分配一个网络管理句柄
CNetHandler* AllocNetItem() {
return _net_item_pool.AllocItem();
};
// 释放一个网络管理句柄
void FreeNetItem(CNetHandler* item) {
return _net_item_pool.FreeItem(item);
};
// 分配一个SOCK连接链路
CSockLink* AllocSockLink() {
return _sock_link_pool.AllocItem();
};
// 释放一个SOCK连接链路
void FreeSockLink(CSockLink* item) {
return _sock_link_pool.FreeItem(item);
};
// 分配一个SOCK连接链路
CDestLinks* AllocDestLink() {
return _dest_ip_pool.AllocItem();
};
// 释放一个SOCK连接链路
void FreeDestLink(CDestLinks* item) {
return _dest_ip_pool.FreeItem(item);
};
// 获取udp的buff池信息
TSkBuffMng* GetSkBuffMng(MT_PROTO_TYPE type) {
if (type == NET_PROTO_TCP) {
return &_tcp_pool;
} else {
return &_udp_pool;
}
};
private:
/**
* @brief 消息buff的构造函数
*/
CNetMgr();
static CNetMgr * _instance; ///< 单例类句柄
HashList* _ip_hash; ///< 目的地址hash
HashList* _session_hash; ///< session id的hash
TSkBuffMng _udp_pool; ///< udp pool, 64K
TSkBuffMng _tcp_pool; ///< tcp pool, 4K
TDestPool _dest_ip_pool; ///< 目的ip对象池
TLinkPool _sock_link_pool; ///< socket pool
TNetItemPool _net_item_pool; ///< net handle pool
};
}
#endif