muduo网络库net篇四:TCP(1)

这几天有去面试,回来之后,懈怠了。接着阅读代码吧!

TcpClient类

TcpClient类主要成员数据:

  EventLoop* loop_;
  ConnectorPtr connector_; // avoid revealing Connector
  ConnectionCallback connectionCallback_;
  MessageCallback messageCallback_;
  WriteCompleteCallback writeCompleteCallback_;
  int nextConnId_;
  TcpConnectionPtr connection_; // @GuardedBy mutex_

connector_、connection_分别是Connector类和TcpConnection类对象的共享指针。connector_在构造函数中初始化,connection_则要等到socket可写时通过TcpClient::newConnection赋值。

传统的客户端socket模型:
创建socket——连接connect服务器——发送send数据——关闭socket
所以TcpClient应该具有上述功能,实际上对外的API只有如下三个:

  void connect();  //创建并连接socket
  void disconnect(); //断开连接
  void stop(); //关闭socket

connect()实际是通过connector_的API创建socket并连接服务器。
stop()关闭socket也是通过connector_的API完成的。
disconnect()禁止写操作。

Connector类

Connector类主要成员数据:

  EventLoop* loop_;
  InetAddress serverAddr_;
  States state_; //kDisconnected, kConnecting, kConnected
  boost::scoped_ptr<Channel> channel_; //connecting()中设置
  NewConnectionCallback newConnectionCallback_;
  int retryDelayMs_;

在connect()中非阻塞创建的socket,需要根据errno进一步判断socket状态进行下列三种操作之一:
1、重新连接——retry(int sockfd)
2、关闭socket
3、设置状态为连接中并监听socket的写事件——connecting(int sockfd)

当事件发生时,调用回调函数(见EventLoop::loop()中),其为:

Connector::handleWrite()  //POLLOUT
Connector::handleError()  //POLLERR | POLLNVAL

socket可写时——handleWrite(),先从[e]poll中移除事件,获取socket状态,并根据状态执行下面两种操作之一:
1、重新连接
2、设置状态为已连接并调用newConnectionCallback_(sockfd)

newConnectionCallback_回调函数:在TcpClient构造函数中设置为TcpClient::newConnection

注:在handleWrite()中channel_并没有立即释放,而是放到EventLoop::pendingFunctors_中——因为channel_还在使用中。

TcpConnection类

TcpConnection类主要成员数据:

  EventLoop* loop_;
  StateE state_; 
  bool reading_;
  boost::scoped_ptr<Socket> socket_;
  boost::scoped_ptr<Channel> channel_;

在上述TcpClient类中提到TcpClient::connection_是在TcpClient::newConnection赋值,该函数初始化之后调用如下:

conn->connectEstablished(); //connection_ = conn

设置状态为已连接并设置channel_可读(监听读事件)。

channel_回调函数均在构造函数中初始化,读事件回调函数为handleRead(Timestamp receiveTime):

  ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
  if (n > 0)
  {
    messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
  }
  else if (n == 0)
  {
    handleClose();
  }
  else
  {
    errno = savedErrno;
    LOG_SYSERR << "TcpConnection::handleRead";
    handleError();
  }

先读到输入缓冲区,再由消息回调函数处理。

TcpConnection::send()函数重载,用于发送数据,最终都是调用:

sendInLoop(const void* data, size_t len)

只有在写事件未被监听,且输出缓冲区为空时,才直接写到socket中,且完成后写完成回调函数加入loop func队列。未写完成或未写的数据添加到输出缓冲区,并设置channel_可写。

写事件回调函数为handleWrite(),此函数把输出缓冲区数据写到socket,输出缓冲区为空时,移除监听写事件,并调用写完成回调函数。状态为关闭连接中时,关闭socket写(shutdown)防止写入。

小结

1、客户端代码逻辑:
由TcpClient类创建客户端对象,建立连接时内部由connector_(Connector对象)完成
connector_不仅完成socket的创建连接,而且会监听写事件,其回调函数handleWrite()最终会调用TcpClient::newConnection
newConnection构造connection_(TcpConnection对象)且调用connectEstablished()监听socket读事件(connection_状态由连接中->已连接),最后调用连接回调函数connectionCallback_
当socket可读时,数据会读到输入缓冲区,再由消息回调函数处理。
TcpConnection提供send()重载函数,可以完成数据的写,其利用了输出缓冲区,不再赘述。

2、状态变化:

TcpClient类
retry_初始状态——false、connect_初始状态——ture

Created with Raphaël 2.1.2 X X retry_(ture) retry_(ture) connect_(ture) connect_(ture) connect_(false) connect_(false) enableRetry() connect() disconnect() stop()

Connector类
connect_初始状态——false

Created with Raphaël 2.1.2 X X connect_(ture) connect_(ture) connect_(false) connect_(false) start() restart() stop()

state_初始状态——kDisconnected

Created with Raphaël 2.1.2 X X kDisconnected kDisconnected kConnecting kConnecting kConnected kConnected restart() connecting() handleWrite() stopInLoop()

TcpConnection类
reading_初始状态——true

Created with Raphaël 2.1.2 X X reading_(ture) reading_(ture) reading_(false) reading_(false) startReadInLoop() stopReadInLoop()

state_初始状态——kConnecting

Created with Raphaël 2.1.2 kConnecting kConnecting kConnected kConnected kDisconnecting kDisconnecting kDisconnected kDisconnected X X connectEstablished() shutdown() forceClose() connectDestroyed() handleClose()

此处列举状态变化,仅仅止于类内,其他类的间接调用不写。X指的是有多种状态的可能,但不代表是全部状态。

socket读写条件 参考 socket可读可写条件


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值