(MuduoManual.pdf P108)
这个比较简单,在连接里面判断最大数量断开连接。
void EchoServer::onConnection(const TcpConnectionPtr& conn)
{
LOG_INFO << "EchoServer - " << conn->peerAddress().toIpPort() << " -> "
<< conn->localAddress().toIpPort() << " is "
<< (conn->connected() ? "UP" : "DOWN");
if (conn->connected())
{
++numConnected_;
if (numConnected_ > kMaxConnections_)
{
conn->shutdown();
}
}
else
{
--numConnected_;
}
LOG_INFO << "numConnected = " << numConnected_;
}
(MuduoManual.pdf P122)
第一种方法:
注册一个没过一秒钟,重复执行的定时器。
EventLoop::runEvery();
foreach(connection in connectionList)
{
if(now - connection.lastReceiveTime) > 8s )
close(connection);
}
如果连接数特别多成千上万,而空闲的只有几个,一秒遍历一次比较耗时,性能不太高。
第二种方法:
每个连接设置一个 one-shot timer,超时定为 8s,在超时的时候就断开本连接。 当然,每次收到数据要去更新 timer。这种做法需要很多个 one-shot timer,会 频繁地更新 timers。如果连接数目比较大,可能对 reactor 的 timer queue 造成 压力。
第三种方法:
环形缓冲区,有两个重要的数据成员,head,tail 。注册一个每过一秒钟,重复执行的定时器。每过一秒钟,tail向前移动一格。每个连接过来都加入到环形缓冲区队尾,新消息过来也添加到队尾,环形缓冲区是一个智能指针,定时器每隔1秒清空队尾。
set 当我们把元素添加到set中,内部会自动排序。
#ifndef MUDUO_EXAMPLES_IDLECONNECTION_ECHO_H
#define MUDUO_EXAMPLES_IDLECONNECTION_ECHO_H
#include "muduo/net/TcpServer.h"
//#include
#include
#include
// RFC 862
class EchoServer
{
public:
EchoServer(muduo::net::EventLoop* loop,
const muduo::net::InetAddress& listenAddr,
int idleSeconds);
void start();
private:
void onConnection(const muduo::net::TcpConnectionPtr& conn);
void onMessage(const muduo::net::TcpConnectionPtr& conn,
muduo::net::Buffer* buf,
muduo::Timestamp time);
void onTimer();
void dumpConnectionBuckets() const;
typedef std::weak_ptr WeakTcpConnectionPtr;
struct Entry : public muduo::copyable
{
explicit Entry(const WeakTcpConnectionPtr& weakConn)
: weakConn_(weakConn)
{
}
~Entry()
{
muduo::net::TcpConnectionPtr conn = weakConn_.lock();
if (conn)
{
conn->shutdown();
}
}
WeakTcpConnectionPtr weakConn_;
};
typedef std::shared_ptr EntryPtr;
typedef std::weak_ptr WeakEntryPtr;
typedef std::unordered_set Bucket;
typedef boost::circular_buffer WeakConnectionList;
muduo::net::TcpServer server_;
WeakConnectionList connectionBuckets_;
};
#endif // MUDUO_EXAMPLES_IDLECONNECTION_ECHO_H
#include "examples/idleconnection/echo.h"
#include "muduo/base/Logging.h"
#include "muduo/net/EventLoop.h"
#include
#include
using namespace muduo;
using namespace muduo::net;
EchoServer::EchoServer(EventLoop* loop,
const InetAddress& listenAddr,
int idleSeconds)
: server_(loop, listenAddr, "EchoServer"),
connectionBuckets_(idleSeconds)
{
server_.setConnectionCallback(
std::bind(&EchoServer::onConnection, this, _1));
server_.setMessageCallback(
std::bind(&EchoServer::onMessage, this, _1, _2, _3));
loop->runEvery(1.0, std::bind(&EchoServer::onTimer, this));
connectionBuckets_.resize(idleSeconds);
dumpConnectionBuckets();
}
void EchoServer::start()
{
server_.start();
}
void EchoServer::onConnection(const TcpConnectionPtr& conn)
{
LOG_INFO << "EchoServer - " << conn->peerAddress().toIpPort() << " -> "
<< conn->localAddress().toIpPort() << " is "
<< (conn->connected() ? "UP" : "DOWN");
if (conn->connected())
{
EntryPtr entry(new Entry(conn));
connectionBuckets_.back().insert(entry);
dumpConnectionBuckets();
WeakEntryPtr weakEntry(entry);
conn->setContext(weakEntry);
}
else
{
assert(!conn->getContext().empty());
WeakEntryPtr weakEntry(boost::any_cast(conn->getContext()));
LOG_DEBUG << "Entry use_count = " << weakEntry.use_count();
}
}
void EchoServer::onMessage(const TcpConnectionPtr& conn,
Buffer* buf,
Timestamp time)
{
string msg(buf->retrieveAllAsString());
LOG_INFO << conn->name() << " echo " << msg.size()
<< " bytes at " << time.toString();
conn->send(msg);
assert(!conn->getContext().empty());
WeakEntryPtr weakEntry(boost::any_cast(conn->getContext()));
EntryPtr entry(weakEntry.lock());
if (entry)
{
connectionBuckets_.back().insert(entry);
dumpConnectionBuckets();
}
}
void EchoServer::onTimer()
{
connectionBuckets_.push_back(Bucket());
dumpConnectionBuckets();
}
void EchoServer::dumpConnectionBuckets() const
{
LOG_INFO << "size = " << connectionBuckets_.size();
int idx = 0;
for (WeakConnectionList::const_iterator bucketI = connectionBuckets_.begin();
bucketI != connectionBuckets_.end();
++bucketI, ++idx)
{
const Bucket& bucket = *bucketI;
printf("[%d] len = %zd : ", idx, bucket.size());
for (const auto& it : bucket)
{
bool connectionDead = it->weakConn_.expired();
printf("%p(%ld)%s, ", get_pointer(it), it.use_count(),
connectionDead ? " DEAD" : "");
}
puts("");
}
}