muduo源码分析之Acceptor

相关文件

muduo/net/Acceptor.h
muduo/net/Acceptor.cc

//用RAII方法封装socket file descriptor
muduo/net/Socket.h
muduo/net/Socket.cc

//封装了socket相关系统调用(全局函数,位于muduo::net::sockets名称空间中)
muduo/net/SocketsOps.h
muduo/net/SocketsOps.cc

//封装了字节序转换函数(全局函数,位于muduo::net::sockets名称空间中)
muduo/net/Endian.h

作用

Acceptor用于accept接收TCP连接。

Acceptor的数据成员包括Socket、Channel,Acceptor的Socket是listening socket(即server socket)。

Channel 用于观察此Socket的readable事件,并回调Acceptor::handleRead(),handleRead()调用accept()来接收新连接,,并回调用户callback函数。

使用

Acceptor是内部类,共TcpServer使用。
下面是一个使用示例
如果有client连接,回调函数向client发送“how are you?”字符串。

#include <muduo/net/Acceptor.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/InetAddress.h>
#include <muduo/net/SocketsOps.h>

#include <stdio.h>

using namespace muduo;
using namespace muduo::net;

void newConnection(int sockfd, const InetAddress& peerAddr)
{
  printf("newConnection(): accepted a new connection from %s\n",
         peerAddr.toIpPort().c_str());
  ::write(sockfd, "How are you?\n", 13);
  sockets::close(sockfd);
}

int main()
{
  printf("main(): pid = %d\n", getpid());

  InetAddress listenAddr(8888);//端口8888
  EventLoop loop;

  Acceptor acceptor(&loop, listenAddr);
  acceptor.setNewConnectionCallback(newConnection);//设置回调函数
  acceptor.listen();

  loop.loop();
}

Acceptor源码分析

Acceptor类

/// Acceptor of incoming TCP connections.
///
class Acceptor : noncopyable
{
 public:
  typedef std::function<void (int sockfd, const InetAddress&)> NewConnectionCallback;

  Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport);
  ~Acceptor();

  void setNewConnectionCallback(const NewConnectionCallback& cb)
  { newConnectionCallback_ = cb; }

  void listen();

  bool listening() const { return listening_; }

  // Deprecated, use the correct spelling one above.
  // Leave the wrong spelling here in case one needs to grep it for error messages.
  // bool listenning() const { return listening(); }

 private:
  void handleRead(); //通道Channel的回调函数

  EventLoop* loop_;        //所在EventLoop
  Socket acceptSocket_;    //socket类
  Channel acceptChannel_;  //通道
  NewConnectionCallback newConnectionCallback_; //在handleRead()中调用
  bool listening_; //是否在listen
  int idleFd_;
};

构造函数

Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport)
  : loop_(loop),
    acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())),
    acceptChannel_(loop, acceptSocket_.fd()),
    listening_(false),
    idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC))
{
  assert(idleFd_ >= 0);
  acceptSocket_.setReuseAddr(true);
  acceptSocket_.setReusePort(reuseport);
  acceptSocket_.bindAddress(listenAddr);  //绑定地址
  acceptChannel_.setReadCallback(
      std::bind(&Acceptor::handleRead, this)); //通道回调函数
}

通道回调函数

void Acceptor::handleRead()
{
  loop_->assertInLoopThread();
  InetAddress peerAddr;
  //FIXME loop until no more
  int connfd = acceptSocket_.accept(&peerAddr); //accept 新连接
  if (connfd >= 0)
  {
    // string hostport = peerAddr.toIpPort();
    // LOG_TRACE << "Accepts of " << hostport;
    if (newConnectionCallback_)
    {
      newConnectionCallback_(connfd, peerAddr);//调用用户回调函数
    }
    else
    {
      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 libev.
    if (errno == EMFILE)
    {
      ::close(idleFd_);
      idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL);
      ::close(idleFd_);
      idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
    }
  }
}

监听

//Accepter.cc
void Acceptor::listen()
{
  loop_->assertInLoopThread();
  listening_ = true;
  acceptSocket_.listen();
  acceptChannel_.enableReading(); //注册到poller监听事件
}

//Socket.cc
void Socket::listen()
{
  sockets::listenOrDie(sockfd_);
}

//SocketsOps.cc
void sockets::listenOrDie(int sockfd)
{
  int ret = ::listen(sockfd, SOMAXCONN);
  if (ret < 0)
  {
    LOG_SYSFATAL << "sockets::listenOrDie";
  }
}

上一篇:vue-webpack


下一篇:muduo源码分析之Cannel通道