muduo之TcpServer

         TcpServer擁有Acceptor類,新鏈接到達時new TcpConnection後續客戶端和TcpConnection類交互。TcpServer管理鏈接和啓動線程池,用Acceptor接受鏈接。安全

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)

#include "muduo/net/TcpServer.h"

#include "muduo/base/Logging.h"
#include "muduo/net/Acceptor.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/EventLoopThreadPool.h"
#include "muduo/net/SocketsOps.h"

#include <stdio.h>  // snprintf

using namespace muduo;
using namespace muduo::net;

TcpServer::TcpServer(EventLoop* loop,
                     const InetAddress& listenAddr,
                     const string& nameArg,
                     Option option)
  : loop_(CHECK_NOTNULL(loop)), //TcpServer所在的主線程下運行的事件驅動循環
    ipPort_(listenAddr.toIpPort()),/* 服務器負責監聽的本地ip和端口 */
    name_(nameArg),/* 服務器名字,建立時傳入 */
    acceptor_(new Acceptor(loop, listenAddr, option == kReusePort)),/* Acceptor對象,負責監聽客戶端鏈接請求,運行在主線程的EventLoop中 */
    threadPool_(new EventLoopThreadPool(loop, name_)),/* 事件驅動線程池,池中每一個線程運行一個EventLoop */
    connectionCallback_(defaultConnectionCallback),/* 用戶傳入,有tcp鏈接到達或tcp鏈接關閉時調用,傳給TcpConnection */
    messageCallback_(defaultMessageCallback),/* 用戶傳入,對端發來消息時調用,傳給TcpConnection */
    nextConnId_(1) /* TcpConnection特有id,每增長一個TcpConnection,nextConnId_加一 */
{ 
  /* 
   * 設置回調函數,當有客戶端請求時,Acceptor接收客戶端請求,而後調用這裏設置的回調函數
   * 回調函數用於建立TcpConnection鏈接
   */
  acceptor_->setNewConnectionCallback(
      std::bind(&TcpServer::newConnection, this, _1, _2));
}

TcpServer::~TcpServer()
{
  loop_->assertInLoopThread();
  LOG_TRACE << "TcpServer::~TcpServer [" << name_ << "] destructing";

  for (auto& item : connections_)
  {
    TcpConnectionPtr conn(item.second);
    item.second.reset();
    conn->getLoop()->runInLoop(
      std::bind(&TcpConnection::connectDestroyed, conn));
  }
}

void TcpServer::setThreadNum(int numThreads)
{
  assert(0 <= numThreads);
  threadPool_->setThreadNum(numThreads);
}

void TcpServer::start()
{
  if (started_.getAndSet(1) == 0)
  {
    threadPool_->start(threadInitCallback_);//啓動線程池,threadInitCallback_建立好全部線程後調用的回調函數

    assert(!acceptor_->listenning());
    loop_->runInLoop(       //直接調用linsten函數
        std::bind(&Acceptor::listen, get_pointer(acceptor_)));
  }
}

/* 
 * Acceptor接收客戶端請求後調用的回調函數
 * @param sockfd: 已經接收完成(三次握手完成)後的客戶端套接字
 * @param peerAddr: 客戶端地址
 * 
 * Acceptor只負責接收客戶端請求
 * TcpServer須要生成一個TcpConnection用於管理tcp鏈接
 * 
 * 1.TcpServer內有一個EventLoopThreadPool,即事件循環線程池,池子中每一個線程都是一個EventLoop
 * 2.每一個EventLoop包含一個Poller用於監聽註冊到這個EventLoop上的全部Channel
 * 3.當創建起一個新的TcpConnection時,這個鏈接會放到線程池中的某個EventLoop中
 * 4.TcpServer中的baseLoop只用來檢測客戶端的鏈接
 * 
 * 從libevent的角度看就是
 * 1.EventLoopThreadPool是一個struct event_base的池子,池子中全是struct event_base
 * 2.TcpServer獨佔一個event_base,這個event_base不在池子中
 * 3.TcpConnection會扔到這個池子中的某個event_base中
 */
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
  loop_->assertInLoopThread();
  EventLoop* ioLoop = threadPool_->getNextLoop();//從事件驅動線程池中取出一個線程給TcpConnection 
   /* 爲TcpConnection生成獨一無二的名字 */
  char buf[64];
  snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);
  ++nextConnId_;
  string connName = name_ + buf;

  LOG_INFO << "TcpServer::newConnection [" << name_
           << "] - new connection [" << connName
           << "] from " << peerAddr.toIpPort();
  /* 
   * 根據sockfd獲取tcp鏈接在本地的<地址,端口>
   * getsockname(int fd, struct sockaddr*, int *size);
   */
  InetAddress localAddr(sockets::getLocalAddr(sockfd));
  // FIXME poll with zero timeout to double confirm the new connection
  // FIXME use make_shared if necessary
  /* 建立一個新的TcpConnection表明一個Tcp鏈接 */
  TcpConnectionPtr conn(new TcpConnection(ioLoop,
                                          connName,
                                          sockfd,
                                          localAddr,
                                          peerAddr));
   /* 添加到全部tcp 鏈接的map中,鍵是tcp鏈接獨特的名字(服務器名+客戶端<地址,端口>) */
  connections_[connName] = conn;
   /* 爲tcp鏈接設置回調函數(由用戶提供) */
  conn->setConnectionCallback(connectionCallback_);
  conn->setMessageCallback(messageCallback_);
  conn->setWriteCompleteCallback(writeCompleteCallback_);
  /* 
   * 關閉回調函數,由TcpServer設置,做用是將這個關閉的TcpConnection從map中刪除
   * 當poll返回後,發現被激活的緣由是EPOLLHUP,此時須要關閉tcp鏈接
   * 調用Channel的CloseCallback,進而調用TcpConnection的handleClose,進而調用removeConnection
   */
  conn->setCloseCallback(
      std::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafe

  /* 
   * 鏈接創建後,調用TcpConnection鏈接創建成功的函數
   * 1.新建的TcpConnection所在事件循環是在事件循環線程池中的某個線程
   * 2.因此TcpConnection也就屬於它所在的事件驅動循環所在的那個線程
   * 3.調用TcpConnection的函數時也就應該在本身所在線程調用
   * 4.因此須要調用runInLoop在本身的那個事件驅動循環所在線程調用這個函數
   * 5.當前線程是TcpServer的主線程,不是TcpConnection的線程,若是在這個線程直接調用會阻塞監聽客戶端請求
   * 6.其實這裏不是由於線程不安全,即便在這個線程調用也不會出現線程不安全,由於TcpConnection本就是由這個線程建立的
   */
  ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}

void TcpServer::removeConnection(const TcpConnectionPtr& conn)
{
  // FIXME: unsafe
  loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}

void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)
{
  //關閉鏈接,把fd從epoll中del掉,要釋放connector(包括描述符)和channel
  loop_->assertInLoopThread();
  LOG_INFO << "TcpServer::removeConnectionInLoop [" << name_
           << "] - connection " << conn->name();
  size_t n = connections_.erase(conn->name());
  (void)n;
  assert(n == 1);
  EventLoop* ioLoop = conn->getLoop();
  ioLoop->queueInLoop(
      std::bind(&TcpConnection::connectDestroyed, conn));
}

        很少說服務器

相關文章
相關標籤/搜索