muduo::Acceptor、TcpServer分析
来源:互联网 发布:js中的对象是什么 编辑:程序博客网 时间:2024/05/20 14:16
Acceptor
类Acceptor用来listen、accept,并调用回调函数来处理新到的连接。Acceptor中封装了Socket fd,它是用RAII手法初始化。accept后,如果有新连接到来,会调用handleRead()函数,在这个函数接收连接。在handleRead()函数中每次接收一个新连接,之后调用处理新连接的回调函数(如果有);但一次性可能有多个连接,这里可以改进的一点是:循环accept,一直到没有新连接到来,或者每次accept时,尝试一次接收N个。
在handleRead()中,accept后并没有考虑新到的连接是否可用,例如当文件描述符耗尽时。这里可以做个改进,拿到accept后的connfd后,如果大于0,则非阻塞poll(2)一下,看看是否可以读写,正常情况下会是writable,表明connfd可用;如果poll(2)返回错误,那么直接关闭connfd。
Acceptor.h
class Acceptor : boost::noncopyable{ public: typedef boost::function<void (int sockfd, const InetAddress&)> NewConnectionCallback;//accept后调用的函数对象 Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport); ~Acceptor(); void setNewConnectionCallback(const NewConnectionCallback& cb) { newConnectionCallback_ = cb; } bool listenning() const { return listenning_; } void listen(); private: void handleRead(); EventLoop* loop_; Socket acceptSocket_; Channel acceptChannel_; NewConnectionCallback newConnectionCallback_; bool listenning_;//是否正在监听状态 int idleFd_;};
Acceptor.cc
Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport) : loop_(loop), acceptSocket_(sockets::createNonblockingOrDie()),//初始化创建sockt fd acceptChannel_(loop, acceptSocket_.fd()),//初始化channel listenning_(false), idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC)){ assert(idleFd_ >= 0); acceptSocket_.setReuseAddr(true); acceptSocket_.setReusePort(reuseport); acceptSocket_.bindAddress(listenAddr); acceptChannel_.setReadCallback( boost::bind(&Acceptor::handleRead, this));//当fd可读时调用回调函数hanleRead}Acceptor::~Acceptor(){ acceptChannel_.disableAll();//将其冲poller监听集合中移除,此时为kDeleted状态 acceptChannel_.remove();//将其从EventList events_中移除,此时为kNew状态 ::close(idleFd_);}void Acceptor::listen(){ loop_->assertInLoopThread(); listenning_ = true; acceptSocket_.listen(); acceptChannel_.enableReading();}void Acceptor::handleRead(){ loop_->assertInLoopThread(); InetAddress peerAddr; //FIXME loop until no more int connfd = acceptSocket_.accept(&peerAddr);//这里时真正接收连接 if (connfd >= 0) { // string hostport = peerAddr.toIpPort(); // LOG_TRACE << "Accepts of " << hostport; if (newConnectionCallback_) { newConnectionCallback_(connfd, peerAddr);//将新连接信息传送到回调函数中 } else//没有回调函数则关闭client对应的fd { sockets::close(connfd); } } else { LOG_SYSERR << "in Acceptor::handleRead"; // Read the section named "The special problem of // accept()ing when you can't" in libev's doc. // By Marc Lehmann, author of livev. if (errno == EMFILE) { ::close(idleFd_); idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL); ::close(idleFd_); idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC); } }}
TcpServer
客户不直接使用Acceptor,它封装在TcpServer中。TcpServer使用比较简单,直接设置好新连接到达和消息到达的回调函数,之后start即可。
TcpServer中还封装了EventLoopThreadPool,因此TcpServer中的EventLoop对象为main Reactor,EventLoopThreadPool为sub Reactor。
当新建连接到达后,TcpServer创建一个新的TcpConnection对象来保存这个连接,设置这个新连接的回调函数,之后在EventLoopThreadPool中取一个EventLoop对象来作为这个新连接的reactor。
TcpServer用map保存了当前server对象中的TcpConnection,当TcpServer对象析构时,就会关闭所有连接。
TcpServer.h
class TcpServer : boost::noncopyable{ public: typedef boost::function<void(EventLoop*)> ThreadInitCallback; enum Option { kNoReusePort, kReusePort, }; //TcpServer(EventLoop* loop, const InetAddress& listenAddr); TcpServer(EventLoop* loop, const InetAddress& listenAddr, const string& nameArg, Option option = kNoReusePort); ~TcpServer(); // force out-line dtor, for scoped_ptr members. const string& hostport() const { return hostport_; } const string& name() const { return name_; } EventLoop* getLoop() const { return loop_; } /// Set the number of threads for handling input. /// /// Always accepts new connection in loop's thread. /// Must be called before @c start /// @param numThreads /// - 0 means all I/O in loop's thread, no thread will created. /// this is the default value. /// - 1 means all I/O in another thread. /// - N means a thread pool with N threads, new connections /// are assigned on a round-robin basis. void setThreadNum(int numThreads); void setThreadInitCallback(const ThreadInitCallback& cb) { threadInitCallback_ = cb; } /// valid after calling start() boost::shared_ptr<EventLoopThreadPool> threadPool() { return threadPool_; } /// Starts the server if it's not listenning. /// /// It's harmless to call it multiple times. /// Thread safe. void start(); /// Set connection callback. /// Not thread safe. void setConnectionCallback(const ConnectionCallback& cb) { connectionCallback_ = cb; } /// Set message callback. /// Not thread safe. void setMessageCallback(const MessageCallback& cb) { messageCallback_ = cb; } /// Set write complete callback. /// Not thread safe. void setWriteCompleteCallback(const WriteCompleteCallback& cb) { writeCompleteCallback_ = cb; } private: /// Not thread safe, but in loop void newConnection(int sockfd, const InetAddress& peerAddr); /// Thread safe. void removeConnection(const TcpConnectionPtr& conn);//TcpConnectionPtr为shared_ptr<TcpConnection> /// Not thread safe, but in loop void removeConnectionInLoop(const TcpConnectionPtr& conn); typedef std::map<string, TcpConnectionPtr> ConnectionMap; EventLoop* loop_; // the acceptor loop const string hostport_; const string name_; boost::scoped_ptr<Acceptor> acceptor_; // avoid revealing Acceptor boost::shared_ptr<EventLoopThreadPool> threadPool_; ConnectionCallback connectionCallback_;//新连接到达时的回调函数 MessageCallback messageCallback_;//消息到达时的回调函数 WriteCompleteCallback writeCompleteCallback_; ThreadInitCallback threadInitCallback_; AtomicInt32 started_; // always in loop thread int nextConnId_;//用来计算标记Connection的名字 ConnectionMap connections_;//Map的key为connection的name};
TcpServer.cc
TcpServer::TcpServer(EventLoop* loop, const InetAddress& listenAddr, const string& nameArg, Option option) : loop_(CHECK_NOTNULL(loop)), hostport_(listenAddr.toIpPort()), name_(nameArg), acceptor_(new Acceptor(loop, listenAddr, option == kReusePort)), threadPool_(new EventLoopThreadPool(loop, name_)), connectionCallback_(defaultConnectionCallback), messageCallback_(defaultMessageCallback), nextConnId_(1){ acceptor_->setNewConnectionCallback(//新连接到来时,调用的时TcpServer::newConnection函数 boost::bind(&TcpServer::newConnection, this, _1, _2));}TcpServer::~TcpServer(){ loop_->assertInLoopThread(); LOG_TRACE << "TcpServer::~TcpServer [" << name_ << "] destructing"; for (ConnectionMap::iterator it(connections_.begin());//在析构函数中销毁connection it != connections_.end(); ++it) { TcpConnectionPtr conn = it->second; it->second.reset(); conn->getLoop()->runInLoop( boost::bind(&TcpConnection::connectDestroyed, conn)); conn.reset(); }}void TcpServer::setThreadNum(int numThreads)//设置线程池大小{ assert(0 <= numThreads); threadPool_->setThreadNum(numThreads);}void TcpServer::start(){ if (started_.getAndSet(1) == 0) { threadPool_->start(threadInitCallback_);//启动线程池 assert(!acceptor_->listenning()); loop_->runInLoop( boost::bind(&Acceptor::listen, get_pointer(acceptor_)));//执行accept的listen }}void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr){ loop_->assertInLoopThread(); EventLoop* ioLoop = threadPool_->getNextLoop();//在线程池取一个EventLoop对象 char buf[32]; snprintf(buf, sizeof buf, ":%s#%d", hostport_.c_str(), nextConnId_);//生成这个连接的名字 ++nextConnId_; string connName = name_ + buf; LOG_INFO << "TcpServer::newConnection [" << name_ << "] - new connection [" << connName << "] from " << peerAddr.toIpPort(); InetAddress localAddr(sockets::getLocalAddr(sockfd)); // FIXME poll with zero timeout to double confirm the new connection // FIXME use make_shared if necessary TcpConnectionPtr conn(new TcpConnection(ioLoop, connName, sockfd, localAddr, peerAddr)); connections_[connName] = conn; conn->setConnectionCallback(connectionCallback_);//设置新连接的回调函数 conn->setMessageCallback(messageCallback_); conn->setWriteCompleteCallback(writeCompleteCallback_); conn->setCloseCallback( boost::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafe ioLoop->runInLoop(boost::bind(&TcpConnection::connectEstablished, conn));//将新到来的连接加入到监听事件中}void TcpServer::removeConnection(const TcpConnectionPtr& conn){ // FIXME: unsafe loop_->runInLoop(boost::bind(&TcpServer::removeConnectionInLoop, this, conn));}void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn){ loop_->assertInLoopThread(); LOG_INFO << "TcpServer::removeConnectionInLoop [" << name_ << "] - connection " << conn->name(); size_t n = connections_.erase(conn->name());//根据connection的名字移除connection (void)n; assert(n == 1); EventLoop* ioLoop = conn->getLoop(); ioLoop->queueInLoop( boost::bind(&TcpConnection::connectDestroyed, conn));}
- muduo::Acceptor、TcpServer分析
- Muduo网络库源码分析(五)Acceptor和TcpServer类
- muduo源码分析--TcpServer
- muduo源码分析之Acceptor
- muduo源码分析之TcpServer
- muduo : Acceptor
- Muduo之Acceptor源码分析笔记
- muduo : TcpServer
- muduo源码分析之多线程TcpServer
- Acceptor+TcpConnection+TcpServer
- 【muduo网络库学习】之Acceptor类分析
- muduo网络库学习之EventLoop(三):Socket、Acceptor、TcpServer、TcpConnection(连接建立,接收消息)
- muduo TcpServer类
- muduo源码分析--事件回调层次是怎么传递的Tcpserver Channel TcpConnection
- muduo源码分析--事件如何被关注的 EpollPoller Channel TcpServer
- muduo库的Acceptor类剖析
- muduo的事件分发器-Acceptor
- muduo源码学习(20)-Acceptor封装
- Android Gallery3D源码分析(一)
- springmvc 集合类型绑定
- C++ 类的静态成员详细讲解
- 新浪博客规则发布失败代码(分享)
- 环境的问题真麻烦 message Unable to compile class for JSP:
- muduo::Acceptor、TcpServer分析
- 2015多校第四场1003 hdu 5329 Question for the Leader
- [学习笔记]JavaScript基础--运动基础
- NEUQ 1410: 屌丝的逆袭
- hdu 1003 Max Sum 最大字段和 dp
- 1043. Is It a Binary Search Tree (25)
- 使用VLC做流媒体服务器(直播形式)
- leetcode--Word Break II
- c语言time.h函数库小结