嚴肅網絡程序需應用層心跳,但「踢掉空閒連接」是權宜之計。若連接 8 秒無數據則斷開,兩種暴力做法:
lastReceiveTime,連接數大時開銷高。利用固定大小循環隊列(8 格桶),每秒指針前移一格:
圖示狀態演進:tail 指針如時鐘指針旋轉,連接在桶間隨時間遷移或被刷新回尾部。
// 數據結構
struct Entry : copyable {
Entry(const WeakTcpConnectionPtr& w) : weakConn_(w) {}
~Entry() { if (auto c = weakConn_.lock()) c->shutdown(); }
WeakTcpConnectionPtr weakConn_;
};
using EntryPtr = shared_ptr<Entry>;
using Bucket = unordered_set<EntryPtr>;
using WeakConnectionList = circular_buffer<Bucket>;
// 構造函數
EchoServer(..., int idleSeconds)
: connectionBuckets_(idleSeconds) {
loop->runEvery(1.0, boost::bind(&EchoServer::onTimer, this));
connectionBuckets_.resize(idleSeconds);
}
// 定時器 Callback:極簡一行
void onTimer() { connectionBuckets_.push_back(Bucket()); }
// 連接建立:創建 Entry 放入尾桶,弱引用存入 TcpConnection::context
void onConnection(const TcpConnectionPtr& conn) {
if (conn->connected()) {
EntryPtr entry(new Entry(conn));
connectionBuckets_.back().insert(entry);
conn->setContext(WeakEntryPtr(entry));
}
}
// 收到消息:從 context 取弱引用升級為強引用,重新插入尾桶
void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp) {
// ... echo 邏輯 ...
WeakEntryPtr weakEntry(boost::any_cast<WeakEntryPtr>(conn->getContext()));
if (auto entry = weakEntry.lock())
connectionBuckets_.back().insert(entry);
}
利用 shared_ptr 引用計數自動管理 Entry 生命週期:桶裡無引用 → Entry 析構 → ~Entry() 呼叫 shutdown()。weak_ptr 避免循環引用,circular_buffer 自動彈出舊桶觸發析構。
在 TcpConnection 記錄「上次插入時的 tail 位置」,若未變則跳過重複插入,減少 Hash Set 操作。
展示 shared_ptr/weak_ptr 結合值語義、RAII、容器特性解決工程問題的優雅範例。