muduo_net库源码分析(九)(TcpServer类、TcpConnection类)

在这里插入图片描述
在这里插入图片描述

TcpServer类

TcpServer类中的数据成员如下:
在这里插入图片描述

Acceptor类的对象是TcpServer类的成员!Acceptor类与TcpServer类是组合关系。

TcpConnection类

TcpConnection类与Acceptor类非常类似,都有两个重要的数据成员:Socket类的对象和Channel类的对象。只是Acceptor类专门用来处理监听事件,建立新连接,每建立一个新连接就会创建一个TcpConnection类的对象;而TcpConnection类在连接建立完成后用来处理事件(这个事件就不再是监听事件了,而是各个TcpConnection类的对象所关注的事件)。

调用顺序(核心!!!!!!)

调用1

创建TcpServer类的对象–>通过该对象调用TcpServer::start(该函数可以跨线程调用,且多次调用是无害的)–>在TcpServer::start中判断其Acceptor类的对象成员acceptor_是否处于监听的状态,如果acceptor_未处于监听的状态,那么通过EventLoop::runInLoop向IO线程布置任务,即让IO线程通过acceptor_的指针调用Acceptor::listen–>Acceptor::listen中调用 Channel::enableReading 注册可读事件(即把监听事件挂到epoll的红黑树上去)

调用2

创建EventLoop类的对象–>通过该对象调用EventLoop::loop–>EventLoop::loop中调用Poller::poll(多态)返回活跃的Channel对象–>调用Channel::handleEvent–>在Channel::handleEvent中,如果到来的是监听事件,由于监听事件是读事件,所以调用读事件回调函数(这里已经在Acceptor类的构造函数中通过其Channel对象调用Channel::setReadCallback将Acceptor::handleRead设置为读事件回调函数)–>在Acceptor::handleRead中,除了调用::accept外,还调用了应用层回调函数 Acceptor::newConnectionCallback_(这里已经在TcpServer类的构造函数中调用Acceptor::setNewConnectionCallback将应用层回调函数newConnectionCallback_设置为TcpServer::newConnection)–>在TcpServer::newConnection中,首先创建了TcpConnection类的对象,并将其包装成boost::shared_ptr<TcpConnection>类型的对象,加入std::map(即连接列表)中;接着将TcpServer类中的连接到来时的回调函数connectionCallback_和消息到来时的回调函数messageCallback_通过调用TcpConnection::setConnectionCallback、TcpConnection::setMessageCallback 赋值给TcpConnection类中的connectionCallback_和messageCallback_;最后,调用TcpConnection::connectEstablished–>在TcpConnection::connectEstablished中,先调用 Channel::enableReading 注册可读事件(即把当前TcpConnection类的对象关注的事件挂到epoll的红黑树上去),再调用连接到来时的回调函数TcpConnection::connectionCallback_

调用3

创建EventLoop类的对象–>通过该对象调用EventLoop::loop–>EventLoop::loop中调用Poller::poll(多态)返回活跃的Channel对象–>调用Channel::handleEvent–>在Channel::handleEvent中,如果到来的是某个TcpConnection类的对象所关注的事件,由于该事件是读事件,所以调用读事件回调函数(这里已经在TcpConnection类的构造函数中通过其Channel对象调用Channel::setReadCallback将TcpConnection::handleRead设置为读事件回调函数)–>在TcpConnection::handleRead中,调用消息到来时的回调函数TcpConnection::messageCallback_

调用4

创建TcpServer类的对象–>通过该对象调用TcpServer::setConnectionCallback、TcpServer::setMessageCallback,设置TcpServer类中的连接到来时的回调函数connectionCallback_和消息到来时的回调函数messageCallback_

测试代码

测试代码也很好理解,就是做了如下几件事:
1.创建TcpServer类的对象;
2.通过该TcpServer类的对象调用TcpServer::setConnectionCallback、TcpServer::setMessageCallback,设置TcpServer::connectionCallback_和TcpServer::messageCallback_;
3.通过该TcpServer类的对象调用TcpServer::start
Reactor_test08.cc

#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/InetAddress.h>

#include <stdio.h>

using namespace muduo;
using namespace muduo::net;
//自定义的新连接到来时的回调函数
void onConnection(const TcpConnectionPtr& conn)
{
  if (conn->connected())
  {
    printf("onConnection(): new connection [%s] from %s\n",
           conn->name().c_str(),
           conn->peerAddress().toIpPort().c_str());
  }
  else
  {
    printf("onConnection(): connection [%s] is down\n",
           conn->name().c_str());
  }
}
//自定义的消息到来时的回调函数
void onMessage(const TcpConnectionPtr& conn,
               const char* data,
               ssize_t len)
{
  printf("onMessage(): received %zd bytes from connection [%s]\n",
         len, conn->name().c_str());
}

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

  InetAddress listenAddr(8888);
  EventLoop loop;

  TcpServer server(&loop, listenAddr, "TestServer");
  server.setConnectionCallback(onConnection);
  server.setMessageCallback(onMessage);
  server.start();

  loop.loop();
}

作者在这里又进一步封装,多搞出了一个TestServer类,类中记录了所属的IO线程(即所属的EventLoop对象,one loop per thread),使程序相对于Reactor_test08.cc整体看起来更加清爽、简洁。
Reactor_test09.cc

#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/InetAddress.h>

#include <boost/bind.hpp>

#include <stdio.h>

using namespace muduo;
using namespace muduo::net;
//作者在这里又进一步封装,多搞出了一个TestServer类,类中记录了所属的IO线程(即所属的EventLoop对象,one loop per thread),使程序整体看起来更加清爽、简洁。
class TestServer
{
 public:
  TestServer(EventLoop* loop,
             const InetAddress& listenAddr)
    : loop_(loop),
      server_(loop, listenAddr, "TestServer")
  {
    server_.setConnectionCallback(
        boost::bind(&TestServer::onConnection, this, _1));//类的成员函数使用bind
    server_.setMessageCallback(
        boost::bind(&TestServer::onMessage, this, _1, _2, _3));
  }

  void start()
  {
	  server_.start();
  }

 private:
  //自定义的新连接到来时的回调函数
  void onConnection(const TcpConnectionPtr& conn)
  {
    if (conn->connected())
    {
      printf("onConnection(): new connection [%s] from %s\n",
             conn->name().c_str(),
             conn->peerAddress().toIpPort().c_str());
    }
    else
    {
      printf("onConnection(): connection [%s] is down\n",
             conn->name().c_str());
    }
  }
  //自定义的消息到来时的回调函数
  void onMessage(const TcpConnectionPtr& conn,
                   const char* data,
                   ssize_t len)
  {
    printf("onMessage(): received %zd bytes from connection [%s]\n",
           len, conn->name().c_str());
  }

  EventLoop* loop_;
  TcpServer server_;
};


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

  InetAddress listenAddr(8888);
  EventLoop loop;

  TestServer server(&loop, listenAddr);
  server.start();

  loop.loop();
}

在这里插入图片描述
新开一个终端:
在这里插入图片描述
原终端:
在这里插入图片描述
新终端中发送消息:
在这里插入图片描述
原终端:
在这里插入图片描述

源码

Callbacks.h

// This is a public header file, it must only include public header files.

#ifndef MUDUO_NET_CALLBACKS_H
#define MUDUO_NET_CALLBACKS_H

#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>

#include <muduo/base/Timestamp.h>

namespace muduo
{
/*
// Adapted from google-protobuf stubs/common.h
// see License in muduo/base/Types.h
template<typename To, typename From>
inline ::boost::shared_ptr<To> down_pointer_cast(const ::boost::shared_ptr<From>& f) {
  if (false) {
    implicit_cast<From*, To*>(0);
  }

#ifndef NDEBUG
  assert(f == NULL || dynamic_cast<To*>(get_pointer(f)) != NULL);
#endif
  return ::boost::static_pointer_cast<To>(f);
}*/

namespace net
{

// All client visible callbacks go here.
/*
class Buffer;
*/
class TcpConnection;
typedef boost::shared_ptr<TcpConnection> TcpConnectionPtr;

typedef boost::function<void()> TimerCallback;

typedef boost::function<void (const TcpConnectionPtr&)> ConnectionCallback;
/*typedef boost::function<void (const TcpConnectionPtr&)> CloseCallback;
typedef boost::function<void (const TcpConnectionPtr&)> WriteCompleteCallback;
typedef boost::function<void (const TcpConnectionPtr&, size_t)> HighWaterMarkCallback;

// the data has been read to (buf, len)
typedef boost::function<void (const TcpConnectionPtr&,
                              Buffer*,
                              Timestamp)> MessageCallback;

void defaultConnectionCallback(const TcpConnectionPtr& conn);
void defaultMessageCallback(const TcpConnectionPtr& conn,
                            Buffer* buffer,
                            Timestamp receiveTime);
							*/
typedef boost::function<void (const TcpConnectionPtr&,
                              const char* data,
                              ssize_t len)> MessageCallback;

}
}

#endif  // MUDUO_NET_CALLBACKS_H

TcpServer.h

// This is a public header file, it must only include public header files.

#ifndef MUDUO_NET_TCPSERVER_H
#define MUDUO_NET_TCPSERVER_H

#include <muduo/base/Types.h>
#include <muduo/net/TcpConnection.h>

#include <map>
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>

namespace muduo
{
namespace net
{

class Acceptor;
class EventLoop;

///
/// TCP server, supports single-threaded and thread-pool models.
///
/// This is an interface class, so don't expose too much details.
class TcpServer : boost::noncopyable
{
 public:
  //typedef boost::function<void(EventLoop*)> ThreadInitCallback;

  //TcpServer(EventLoop* loop, const InetAddress& listenAddr);
  TcpServer(EventLoop* loop,
            const InetAddress& listenAddr,
            const string& nameArg);
  ~TcpServer();  // force out-line dtor, for scoped_ptr members.

  const string& hostport() const { return hostport_; }
  const string& name() const { return name_; }

  /// 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; }


 private:
  /// Not thread safe, but in loop
  void newConnection(int sockfd, const InetAddress& peerAddr);

  typedef std::map<string, TcpConnectionPtr> ConnectionMap;//typedef boost::shared_ptr<TcpConnection> TcpConnectionPtr;

  EventLoop* loop_;  // the acceptor loop  //Acceptor对象acceptor_所属的EventLoop
  const string hostport_;		// 服务端口
  const string name_;			// 服务名
  boost::scoped_ptr<Acceptor> acceptor_; // avoid revealing Acceptor //TcpServer类与Acceptor类是组合关系
  ConnectionCallback connectionCallback_;//连接到来时的回调函数
  MessageCallback messageCallback_;//消息到来时的回调函数
  bool started_;
  // always in loop thread
  int nextConnId_;				// 下一个连接ID
  ConnectionMap connections_;	// 连接列表 //typedef std::map<string, boost::shared_ptr<TcpConnection> TcpConnectionPtr> ConnectionMap;
};

}
}

#endif  // MUDUO_NET_TCPSERVER_H

TcpServer.cc

#include <muduo/net/TcpServer.h>

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

#include <boost/bind.hpp>

#include <stdio.h>  // snprintf

using namespace muduo;
using namespace muduo::net;

TcpServer::TcpServer(EventLoop* loop,
                     const InetAddress& listenAddr,
                     const string& nameArg)
  : loop_(CHECK_NOTNULL(loop)),
    hostport_(listenAddr.toIpPort()),
    name_(nameArg),
    acceptor_(new Acceptor(loop, listenAddr)),//重要!创建Acceptor类的对象acceptor_(Acceptor类与TcpServer类是组合关系)
    started_(false),
    nextConnId_(1)
{
  //Acceptor::handleRead函数中会回调用应用层回调函数 Acceptor::newConnectionCallback_
  //这里通过Acceptor::setNewConnectionCallback将应用层回调函数newConnectionCallback_设置为TcpServer::newConnection
  //_1对应的是socket文件描述符,_2对应的是对等方的地址(InetAddress)
  acceptor_->setNewConnectionCallback(
      boost::bind(&TcpServer::newConnection, this, _1, _2));
}

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

// 该函数多次调用是无害的
// 该函数可以跨线程调用!
void TcpServer::start()
{
  if (!started_)
  {
    started_ = true;
  }

  if (!acceptor_->listenning())//判断Acceptor类的对象acceptor_是否处于监听的状态
  {
	// get_pointer返回原生指针
  //如果acceptor_未处于监听的状态,那么通过EventLoop::runInLoop向IO线程布置任务,即让IO线程通过acceptor_的指针调用Acceptor::listen
    loop_->runInLoop(
        boost::bind(&Acceptor::listen, get_pointer(acceptor_)));//类的成员函数使用boost::bind
  }
}

void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
  loop_->assertInLoopThread();//断言当前在IO线程中
  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(loop_,
                                          connName,
                                          sockfd,
                                          localAddr,
                                          peerAddr));//创建TcpConnection类的对象,并创建其对应的智能指针对象
  connections_[connName] = conn;//加入std::map(即连接列表)中
  conn->setConnectionCallback(connectionCallback_);//设置TcpConnection类的连接到来时的回调函数
  conn->setMessageCallback(messageCallback_);//设置TcpConnection类的消息到来时的回调函数

  conn->connectEstablished();//调用TcpConnection::connectEstablished
}


TcpConnection.h

// This is a public header file, it must only include public header files.

#ifndef MUDUO_NET_TCPCONNECTION_H
#define MUDUO_NET_TCPCONNECTION_H

#include <muduo/base/Mutex.h>
#include <muduo/base/StringPiece.h>
#include <muduo/base/Types.h>
#include <muduo/net/Callbacks.h>//Callbacks.h中包含了各个回调函数类型的typedef
//#include <muduo/net/Buffer.h>
#include <muduo/net/InetAddress.h>

//#include <boost/any.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>

namespace muduo
{
namespace net
{

class Channel;
class EventLoop;
class Socket;

///
/// TCP connection, for both client and server usage.
///
/// This is an interface class, so don't expose too much details.
//TcpConnection类与Acceptor类是类似的,都有两个重要的数据成员:Socket类的对象和Channel类的对象
class TcpConnection : boost::noncopyable,
                      public boost::enable_shared_from_this<TcpConnection>
{
 public:
  /// Constructs a TcpConnection with a connected sockfd
  ///
  /// User should not create this object.
  TcpConnection(EventLoop* loop,
                const string& name,
                int sockfd,
                const InetAddress& localAddr,
                const InetAddress& peerAddr);
  ~TcpConnection();

  EventLoop* getLoop() const { return loop_; }
  const string& name() const { return name_; }
  const InetAddress& localAddress() { return localAddr_; }
  const InetAddress& peerAddress() { return peerAddr_; }
  bool connected() const { return state_ == kConnected; }

  void setConnectionCallback(const ConnectionCallback& cb)
  { connectionCallback_ = cb; }

  void setMessageCallback(const MessageCallback& cb)
  { messageCallback_ = cb; }

  // called when TcpServer accepts a new connection
  void connectEstablished();   // should be called only once

 private:
  enum StateE { /*kDisconnected, */kConnecting, kConnected/*, kDisconnecting*/ };//连接的状态
  void handleRead(Timestamp receiveTime);
  void setState(StateE s) { state_ = s; }

  EventLoop* loop_;			//Channel对象channel_所属的EventLoop
  string name_;				// 连接名
  StateE state_;  // FIXME: use atomic variable
  // we don't expose those classes to client.
  boost::scoped_ptr<Socket> socket_;
  boost::scoped_ptr<Channel> channel_;
  InetAddress localAddr_;
  InetAddress peerAddr_;
  ConnectionCallback connectionCallback_;
  MessageCallback messageCallback_;
};

typedef boost::shared_ptr<TcpConnection> TcpConnectionPtr;

}
}

#endif  // MUDUO_NET_TCPCONNECTION_H

TcpConnection.cc

#include <muduo/net/TcpConnection.h>

#include <muduo/base/Logging.h>
#include <muduo/net/Channel.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/Socket.h>
#include <muduo/net/SocketsOps.h>

#include <boost/bind.hpp>

#include <errno.h>
#include <stdio.h>

using namespace muduo;
using namespace muduo::net;

TcpConnection::TcpConnection(EventLoop* loop,
                             const string& nameArg,
                             int sockfd,
                             const InetAddress& localAddr,
                             const InetAddress& peerAddr)
  : loop_(CHECK_NOTNULL(loop)),
    name_(nameArg),
    state_(kConnecting),
    socket_(new Socket(sockfd)),
    channel_(new Channel(loop, sockfd)),
    localAddr_(localAddr),
    peerAddr_(peerAddr)
{
  //通过Channel对象调用Channel::setReadCallback将TcpConnection::handleRead设置为读事件回调函数,_1是事件发生时间
  channel_->setReadCallback(
      boost::bind(&TcpConnection::handleRead, this, _1));
  LOG_DEBUG << "TcpConnection::ctor[" <<  name_ << "] at " << this
            << " fd=" << sockfd;
  socket_->setKeepAlive(true);
}

TcpConnection::~TcpConnection()
{
  LOG_DEBUG << "TcpConnection::dtor[" <<  name_ << "] at " << this
            << " fd=" << channel_->fd();
}

void TcpConnection::connectEstablished()
{
  loop_->assertInLoopThread();
  assert(state_ == kConnecting);
  setState(kConnected);
  channel_->tie(shared_from_this());
  channel_->enableReading();	//调用 Channel::enableReading 注册可读事件(即把TcpConnection关注的事件挂到epoll的红黑树上去)

  connectionCallback_(shared_from_this());
}

void TcpConnection::handleRead(Timestamp receiveTime)
{
  loop_->assertInLoopThread();
  char buf[65536];
  ssize_t n = ::read(channel_->fd(), buf, sizeof buf);
  messageCallback_(shared_from_this(), buf, n);//调用消息到来时的回调函数
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值