Muduo_Day7(Connector,TcpClient)

Connector类

Connector只负责建立socket连接,不负责创建TcpConnection对象,它的newConnectionCallback回调的参数是socket文件描述符.Muduo中的Connector带有自动重连的功能,并且重试的事件间隔逐渐延长,直至30s.与被动连接的Acceptor类相比,缺少一个acceptSocket_ 成员,因为Connector 是创建一个新的sockfd 并connect 它.因为我们这的Connector是主动发起连接.
调用函数的时序为:Connector::start->Connector::stopInLoop->Connector::connect.
其中的Connector::connect()函数:

void Connector::connect()
{
  int sockfd = sockets::createNonblockingOrDie();   // 创建非阻塞套接字
  int ret = sockets::connect(sockfd, serverAddr_.getSockAddrInet());
  int savedErrno = (ret == 0) ? 0 : errno;
  switch (savedErrno)
  {
    case 0:
    case EINPROGRESS:   // 非阻塞套接字,未连接成功返回码是EINPROGRESS表示正在连接
    case EINTR:
    case EISCONN:           // 连接成功
      connecting(sockfd);
      break;

    case EAGAIN:
    case EADDRINUSE:
    case EADDRNOTAVAIL:
    case ECONNREFUSED:
    case ENETUNREACH:
      retry(sockfd);        // 重连
      break;

    case EACCES:
    case EPERM:
    case EAFNOSUPPORT:
    case EALREADY:
    case EBADF:
    case EFAULT:
    case ENOTSOCK:
      LOG_SYSERR << "connect error in Connector::startInLoop " << savedErrno;
      sockets::close(sockfd);   // 不能重连,关闭sockfd
      break;

    default:
      LOG_SYSERR << "Unexpected error in Connector::startInLoop " << savedErrno;
      sockets::close(sockfd);
      // connectErrorCallback_();
      break;
  }
}

其中会根据savedError来选择适当的执行函数,或者retry()或者connecting().
connecting()函数:

void Connector::connecting(int sockfd)
{
  setState(kConnecting);
  assert(!channel_);
  // Channel与sockfd关联
  channel_.reset(new Channel(loop_, sockfd));
  // 设置可写回调函数,这时候如果socket没有错误,sockfd就处于可写状态
  channel_->setWriteCallback(
      boost::bind(&Connector::handleWrite, this)); // FIXME: unsafe
  // 设置错误回调函数
  channel_->setErrorCallback(
      boost::bind(&Connector::handleError, this)); // FIXME: unsafe
  channel_->enableWriting();        // 让Poller关注可写事件
}

当调用此函数时,sockfd便处于可写的状态(内核缓冲区不为满),而且poller关注可写的事件,触发回调Connector::handleWrite()函数,在handleWrite()里面需要removeAndResetChannel(),因此此时连接建立,故不用再关注channel的可写事件,最终会执行 channel_.reset(); 即把channel析构了.连接成功后调用newConnectionCallback_(sockfd).

void Connector::handleWrite()
{
    LOG_TRACE << "Connector::handleWrite " << state_;

    if (state_ == kConnecting)
    {
        int sockfd = removeAndResetChannel();   // 从poller中移除关注,并将channel置空
        // socket可写并不意味着连接一定建立成功
        // 还需要用getsockopt(sockfd, SOL_SOCKET, SO_ERROR, ...)再次确认一下。
        int err = sockets::getSocketError(sockfd);
        ......
        else    // 连接成功
        {
            setState(kConnected);
            if (connect_)
            {
                newConnectionCallback_(sockfd);     // 回调
            }

        }
    }
}

测试用例

#include "Connector.h"
#include "EventLoop.h"

#include 

muduo::EventLoop* g_loop;

void connectCallback(int sockfd)
{
  printf("connected.\n");
  g_loop->quit();
}

int main(int argc, char* argv[])
{
  muduo::EventLoop loop;
  g_loop = &loop;
  muduo::InetAddress addr("127.0.0.1", 9981);
  muduo::ConnectorPtr connector(new muduo::Connector(&loop, addr));
  connector->setNewConnectionCallback(connectCallback);
  connector->start();
  loop.loop();
}

运行结果:

20191013 11:29:40.428847Z 14273 DEBUG Connector ctor[0x1e76f40] - Connector.cc:31
-1
115
20191013 11:29:40.429139Z 14273 WARN  Channel::handle_event() POLLHUP - Channel.cc:50
20191013 11:29:40.429157Z 14273 ERROR Connector::handleError - Connector.cc:190
20191013 11:29:40.429223Z 14273 INFO  Connector::retry - Retry connecting to 127.0.0.1:9981 in 500 milliseconds.  - Connector.cc:205

一次连接不成功,会一直反复尝试,直到连接成功.
实际上,Connector类一般不会单独使用,一般是作为TcpClient的成员,其与TcpServer类较为相似(都有newConnection和removeConnection这两个成员函数),但每个TCPClient只管理一个TcpConnection,加上Connector之后,具备了TcpConnection断开之后重新连接的功能.还有一点不同是在于TcpServer可以有多个Reactor,即mainReactor_Threadpool(subReactor)模式,但TcpClient只能有一个事件循环Eventloop,即也只能有一个Reactor,由它来处理TcpConnection上的事件.当然我们可以多开几个TcpClient一起绑定到同一个EventLoop对象上,也就是多个TcpConnection.
TcpClient类
数据成员:

typedef boost::shared_ptr ConnectorPtr;
ConnectorPtr connector_;    // 用于主动发起连接
TcpConnectionPtr connection_; // Connector连接成功以后,得到一个TcpConnection,只管理一个TcpConnection

在构造函数中:

//设置连接成功回调函数
connector_->setNewConnectionCallback(
    boost::bind(&TcpClient::newConnection, this, _1));

主动发起连接成功之后,会运行TcpClient::newConnection()函数.

void TcpClient::newConnection(int sockfd)
{
  loop_->assertInLoopThread();
  InetAddress peerAddr(sockets::getPeerAddr(sockfd));
  char buf[32];
  snprintf(buf, sizeof buf, ":%s#%d", peerAddr.toIpPort().c_str(), nextConnId_);
  ++nextConnId_;
  string connName = name_ + buf;
  InetAddress localAddr(sockets::getLocalAddr(sockfd));
  TcpConnectionPtr conn(new TcpConnection(loop_,
                                          connName,
                                          sockfd,
                                          localAddr,
                                          peerAddr));

  conn->setConnectionCallback(connectionCallback_);
  conn->setMessageCallback(messageCallback_);
  conn->setWriteCompleteCallback(writeCompleteCallback_);
  conn->setCloseCallback(
      boost::bind(&TcpClient::removeConnection, this, _1)); // FIXME: unsafe
  {
    MutexLockGuard lock(mutex_);
    connection_ = conn;     // 保存TcpConnection
  }
  conn->connectEstablished();       // 这里回调connectionCallback_
}

另外一点,TcpServer或者TcpClient对象,通过调用setxxxxCallback()函数来注册回调函数,最终都会实际上设置的是TcpConnection对象的xxxxCallback_成员,这些函数会在有事件发生时被调用,比如OnConnection()和OnMessage().
测试用例
服务端:echo回射服务器

#include 
#include 
#include 

#include 

#include 

using namespace muduo;
using namespace muduo::net;

class TestServer
{
 public:
  TestServer(EventLoop* loop,
             const InetAddress& listenAddr)
    : loop_(loop),
      server_(loop, listenAddr, "TestServer")
  {
    server_.setConnectionCallback(
        boost::bind(&TestServer::onConnection, this, _1));
    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,
                 Buffer* buf,
                 Timestamp receiveTime)
  {
    string msg1 = buf->retrieveAllAsString();
    string msg(msg1);
    printf("onMessage(): received %zd bytes from connection [%s] at %s\n",
           msg.size(),
           conn->name().c_str(),
           receiveTime.toFormattedString().c_str());
    conn->send(msg);
  }

  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();
}

客户端程序:

#include 
#include 

#include 
#include 
#include 

#include 

#include 

using namespace muduo;
using namespace muduo::net;

class TestClient
{
 public:
  TestClient(EventLoop* loop, const InetAddress& listenAddr)
    : loop_(loop),
      client_(loop, listenAddr, "TestClient"),
      stdinChannel_(loop, 0)
  {
    client_.setConnectionCallback(
        boost::bind(&TestClient::onConnection, this, _1));
    client_.setMessageCallback(
        boost::bind(&TestClient::onMessage, this, _1, _2, _3));
    //client_.enableRetry();
    // 标准输入缓冲区中有数据的时候,回调TestClient::handleRead
    stdinChannel_.setReadCallback(boost::bind(&TestClient::handleRead, this));
    stdinChannel_.enableReading();      // 关注可读事件
  }

  void connect()
  {
    client_.connect();
  }

 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, Buffer* buf, Timestamp time)
  {
    string msg1 = buf->retrieveAllAsString();
    string msg(msg1);
    printf("onMessage(): recv a message [%s]\n", msg.c_str());
    LOG_TRACE << conn->name() << " recv " << msg.size() << " bytes at " << time.toFormattedString();
  }

  // 标准输入缓冲区中有数据的时候,回调该函数
  void handleRead()
  {
    char buf[1024] = {0};
    fgets(buf, 1024, stdin);
    buf[strlen(buf)-1] = '\0';      // 去除\n
    client_.connection()->send(buf);
  }

  EventLoop* loop_;
  TcpClient client_;
  Channel stdinChannel_;        // 标准输入Channel
};

int main(int argc, char* argv[])
{
  LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid();
  EventLoop loop;
  InetAddress serverAddr("127.0.0.1", 8888);
  TestClient client(&loop, serverAddr);
  client.connect();
  loop.loop();
}

分别运行两个程序,并在客户端中输入"aaaa",会被回射回来,运行结果:
服务端输出:
20191013 12:03:25.884639Z 19542 TRACE IgnoreSigPipe Ignore SIGPIPE - EventLoop.cc:51
main(): pid = 19542
20191013 12:03:25.884858Z 19542 TRACE updateChannel fd = 4 events = 3 - EPollPoller.cc:104
20191013 12:03:25.884905Z 19542 TRACE EventLoop EventLoop created 0x7FFEBA471BD0 in thread 19542 - EventLoop.cc:76
20191013 12:03:25.884924Z 19542 TRACE updateChannel fd = 5 events = 3 - EPollPoller.cc:104
20191013 12:03:25.885039Z 19542 TRACE updateChannel fd = 6 events = 3 - EPollPoller.cc:104
20191013 12:03:25.885059Z 19542 TRACE loop EventLoop 0x7FFEBA471BD0 start looping - EventLoop.cc:108
20191013 12:03:28.221151Z 19542 TRACE poll 1 events happended - EPollPoller.cc:65
20191013 12:03:28.221476Z 19542 TRACE printActiveChannels {6: IN } - EventLoop.cc:271
20191013 12:03:28.221549Z 19542 INFO TcpServer::newConnection [TestServer] - new connection [TestServer:0.0.0.0:8888#1] from 127.0.0.1:53952 - TcpServer.cc:93
20191013 12:03:28.221599Z 19542 DEBUG TcpConnection TcpConnection::ctor[TestServer:0.0.0.0:8888#1] at 0x12E2680 fd=8 - TcpConnection.cc:65
20191013 12:03:28.221617Z 19542 TRACE newConnection [1] usecount=1 - TcpServer.cc:111
20191013 12:03:28.221646Z 19542 TRACE newConnection [2] usecount=2 - TcpServer.cc:113
20191013 12:03:28.221664Z 19542 TRACE connectEstablished [3] usecount=6 - TcpConnection.cc:238
20191013 12:03:28.221673Z 19542 TRACE updateChannel fd = 8 events = 3 - EPollPoller.cc:104
onConnection(): new connection [TestServer:0.0.0.0:8888#1] from 127.0.0.1:53952
20191013 12:03:28.221717Z 19542 TRACE connectEstablished [4] usecount=6 - TcpConnection.cc:243
20191013 12:03:28.221729Z 19542 TRACE newConnection [5] usecount=2 - TcpServer.cc:123
20191013 12:03:29.640547Z 19542 TRACE poll 1 events happended - EPollPoller.cc:65
20191013 12:03:29.640608Z 19542 TRACE printActiveChannels{8: IN } - EventLoop.cc:271
20191013 12:03:29.640623Z 19542 TRACE handleEvent [6] usecount=2 - Channel.cc:67
onMessage(): received 4 bytes from connection [TestServer:0.0.0.0:8888#1] at 20191013 12:03:29.640545
20191013 12:03:29.640733Z 19542 TRACE handleEvent [12] usecount=2 - Channel.cc:69
^C
输出中fd = 6是监听套接字,fd=8是返回的已连接套接字,连接建立调用OnConnection(),因为客户端输入两串数据,fd=8产生两次可读事件,调用两次onMessage().
客户端输出:
20191013 12:03:28.220602Z 19543 TRACE IgnoreSigPipe Ignore SIGPIPE - EventLoop.cc:51
20191013 12:03:28.220775Z 19543 INFO pid = 19543, tid = 19543 - TcpClient_test.cc:77
20191013 12:03:28.220821Z 19543 TRACE updateChannel fd = 4 events = 3 - EPollPoller.cc:104
20191013 12:03:28.220869Z 19543 TRACE EventLoop EventLoop created 0x7FFD9CF15DB0 in thread 19543 - EventLoop.cc:76
20191013 12:03:28.220893Z 19543 TRACE updateChannel fd = 5 events = 3 - EPollPoller.cc:104
20191013 12:03:28.220924Z 19543 DEBUG Connector ctor[0xFCE4A0] - Connector.cc:33
20191013 12:03:28.220942Z 19543 INFO TcpClient::TcpClient[TestClient] - connector 0xFCE4A0 - TcpClient.cc:72
20191013 12:03:28.220957Z 19543 TRACE updateChannel fd = 0 events = 3 - EPollPoller.cc:104
20191013 12:03:28.220980Z 19543 INFO TcpClient::connect[TestClient] - connecting to 127.0.0.1:8888 - TcpClient.cc:106
20191013 12:03:28.221114Z 19543 TRACE updateChannel fd = 6 events = 4 - EPollPoller.cc:104
20191013 12:03:28.221146Z 19543 TRACE loop EventLoop 0x7FFD9CF15DB0 start looping - EventLoop.cc:108
20191013 12:03:28.221165Z 19543 TRACE poll 1 events happended - EPollPoller.cc:65
20191013 12:03:28.221423Z 19543 TRACE printActiveChannels {6: OUT } - EventLoop.cc:271
20191013 12:03:28.221441Z 19543 TRACE handleWrite Connector::handleWrite 1 - Connector.cc:169
20191013 12:03:28.221458Z 19543 TRACE updateChannel fd = 6 events = 0 - EPollPoller.cc:104
20191013 12:03:28.221479Z 19543 TRACE removeChannel fd = 6 - EPollPoller.cc:147
20191013 12:03:28.221553Z 19543 DEBUG TcpConnection TcpConnection::ctor[TestClient:127.0.0.1:8888#1] at 0xFCE6F0 fd=6 - TcpConnection.cc:65
20191013 12:03:28.221585Z 19543 TRACE connectEstablished [3] usecount=3 - TcpConnection.cc:238
20191013 12:03:28.221594Z 19543 TRACE updateChannel fd = 6 events = 3 - EPollPoller.cc:104
onConnection(): new connection [TestClient:127.0.0.1:8888#1] from 127.0.0.1:8888
20191013 12:03:28.221653Z 19543 TRACE connectEstablished [4] usecount=3 - TcpConnection.cc:243
aaaa
20191013 12:03:29.640345Z 19543 TRACE poll 1 events happended - EPollPoller.cc:65
20191013 12:03:29.640415Z 19543 TRACE printActiveChannels {0: IN } - EventLoop.cc:271
20191013 12:03:29.640769Z 19543 TRACE poll 1 events happended - EPollPoller.cc:65
20191013 12:03:29.640819Z 19543 TRACE printActiveChannels {6: IN } - EventLoop.cc:271
20191013 12:03:29.640834Z 19543 TRACE handleEvent [6] usecount=2 - Channel.cc:67
onMessage(): recv a message [aaaa]
20191013 12:03:29.640894Z 19543 TRACE onMessage TestClient:127.0.0.1:8888#1 recv 4 bytes at 20191013 12:03:29.640768 - TcpClient_test.cc:58
20191013 12:03:29.640914Z 19543 TRACE handleEvent [12] usecount=2 - Channel.cc:69
20191013 12:03:32.560680Z 19543 TRACE poll 1 events happended - EPollPoller.cc:65
20191013 12:03:32.560750Z 19543 TRACE printActiveChannels {6: IN } - EventLoop.cc:271
20191013 12:03:32.560768Z 19543 TRACE handleEvent [6] usecount=2 - Channel.cc:67
20191013 12:03:32.560795Z 19543 TRACE handleClose fd = 6 state = 2 - TcpConnection.cc:369
20191013 12:03:32.560805Z 19543 TRACE updateChannel fd = 6 events = 0 - EPollPoller.cc:104
onConnection(): connection [TestClient:127.0.0.1:8888#1] is down
20191013 12:03:32.560843Z 19543 TRACE handleClose [7] usecount=3 - TcpConnection.cc:377
20191013 12:03:32.560865Z 19543 TRACE handleClose [11] usecount=3 - TcpConnection.cc:380
20191013 12:03:32.560874Z 19543 TRACE handleEvent [12] usecount=2 - Channel.cc:69
20191013 12:03:32.560883Z 19543 TRACE removeChannel fd = 6 - EPollPoller.cc:147
20191013 12:03:32.560897Z 19543 DEBUG ~TcpConnection TcpConnection::dtor[TestClient:127.0.0.1:8888#1] at 0xFCE6F0 fd=6 - TcpConnection.cc:72
20191013 12:03:42.561752Z 19543 TRACE poll nothing happended - EPollPoller.cc:74

20191013 12:03:52.571864Z 19543 TRACE poll nothing happended - EPollPoller.cc:74

由前面的分析可知,一个进程默认打开0(输入),1(输出),2(错误)文件描述符,该例子中0为标准输入,且mainReactor中:epollfd_ = 3; timerfd_ = 4; wakeupFd_ = 5; sockfd_ = 6; idleFd_ = 7;,fd=6是客户端连接的套接字,刚开始连接成功,fd=6可写事件发生,但马上把connector的channel移除关注并析构,并构造TcpConnection。在命令行输入一串数据,标准输入可读事件发生,等服务器回射回来,fd=6可读事件发生,调用OnMessage().我们首先ctrl+c 掉服务器,客户端发现此连接已经down掉,就会析构TcpConnection,顺便关闭套接字,当然事件循环还在继续,因为如前面所说,有可能EventLoop绑定了多个TcpClient。

你可能感兴趣的:(Muduo_Day7(Connector,TcpClient))