这几天有去面试,回来之后,懈怠了。接着阅读代码吧!
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类主要成员数据:
EventLoop* loop_;
InetAddress serverAddr_;
States state_; //kDisconnected, kConnecting, kConnected
boost::scoped_ptr 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类主要成员数据:
EventLoop* loop_;
StateE state_;
bool reading_;
boost::scoped_ptr socket_;
boost::scoped_ptr 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
Connector类
connect_初始状态——false
state_初始状态——kDisconnected
TcpConnection类
reading_初始状态——true
state_初始状态——kConnecting
此处列举状态变化,仅仅止于类内,其他类的间接调用不写。X指的是有多种状态的可能,但不代表是全部状态。
socket读写条件 参考 socket可读可写条件