muduo源码解析29-网络库7:tcpconnection类
tcpconnection类:
说明:
之前我们提到了acceptor类负责socket(),bind(),listen()和accept()一个连接。
但是accept()之后的操作,例如和这个连接套接字的读/写,关闭连接等操作都没有实现。
这些操作都将由tcpconnection这个类来实现。
tcpconnection是整个网络库的核心,封装一次TCP连接,注意它不能发起连接
tcpserver和tcpclient都用到了tcpconnection
tcpconnection是为tcpserver服务的
tcpserver(待会讲到)中会有一个acceptor负责接收新的连接,acceptor每接受一个连接时就会为这个连接创建一个tcpconnection。
acceptor在创建tcpconnection时为其为指定四个回调函数,读消息,写完成,连接建立,关闭连接。
被调用处:handleRead();handleWrite()/sendInLoop();connectionEstablished();handleClose();
tcpconnection内部也会有一个socket和一个对应的channel,需要在channel上注册读/写/关闭/错误网络事件,一旦发生这写网络事件,tcpconnection就会去执行相应的回调函数:
读网络事件:channel回调tcpconnection::handleRead()把数据读到m_inputbuffer中
写网络事件:channel回调tcpconnection::handleWrite()把数据通过m_outputbuffer进行处理(见代码)
关闭网络事件:tcpconnection::handleClose()
错误网络事件:tcpconnection::handleError()
一个tcpconenction有四种状态:已连接,未连接,正在连接,正在断开
内部有一个socket和channel,
tcpconenction构造时会设置channel的四个回调函数,read/write/close/error
只要当socket上发生网络事件时.poller::poll()就会返回,channel会被添加到
eventloop::activeChannels,然后调用channel::handleEvent()根据不同的网络事件
调用上面四个回调函数来处理
内部有两块之前的buffer,主要是为了解决高并发情况下读写缓冲区的问题,
inputbuffer:解决包不完整,粘包问题
先把数据全部写到inputbuffer中,之后再进行处理,只当有一个完整的包时,才进行业务处理
outputbuffer:解决当前内核写阻塞的问题
先把数据全部写到outputbuffer中,每当内核可写时,才把outputbuffer中数据写出去
tcpconnection基本上都是通过evenntloop::runInLoop()来实现对于tcp连接的管理的,
例如send,shutdown,forceclose等操作,下面以分别说一说具体的步骤:
先看一看send()函数
tcpconnection通过调用send发送数据,实际上通过eventloop::runInLoop()把发送任务
添加到eventloop中的任务队列中,eventloop会回调tcpconnection::sendInLoop()完成
发送数据的操作.sendInLoop()先尝试write(),如果无法一次全部写完,需要把数据缓存到
outputbuffer中,让channel注册写网络事件,等到内核可写时,socket就会出发可写
网络事件,channel就回调handleWrite(),把outputbuffer中的数据发送出去
只要数据全部写完就会触发m_writeCompleteCallback写完成回调.
再看一看forceclose()函数:
tcpconenction的关闭有两种情况,一种是远方断开连接,此时socket触发close网络事件
channel::handleEvent()会回调&tcpconnection::handleClose()处理
第二种情况是我自己主动调用forceclose()关闭连接,此时会把
tcpconnection::forceCloseInLoop添加到eventloop中的任务队列中,
让eventloop再去主动调用channel::handleClose()关闭连接
可以看出来,两种方法最终都是让channel执行handleClose()关闭tcp连接
再来看一看startread()函数:
startread()实质实在channel上注册一个可读网络事件,只要socket发生可读网络事件
channel就会调用handleEvent(),然后回调tcpconnection::handleRead()来把数据读
到inputbuffer中,不直接使用read得到的数据而先读到inputbuffer的好处之前说过了,防止
粘包/包不完整,在inputbuffer中的数据还会被进一步处理才能使用
tcpconnection.h
#ifndef TCPCONNECTION_H #define TCPCONNECTION_H #include"base/noncopyable.h" #include"base/types.h" #include"net/callbacks.h" #include"net/buffer.h" #include"net/inetaddress.h" #include<memory> #include<boost/any.hpp> struct tcp_info; namespace mymuduo{ namespace net { class channel; class eventloop; class Socket; class tcpconnection:noncopyable,public std::enable_shared_from_this<tcpconnection> { public: //构造函数,初始化成员,设置四个回调函数,Read,Write,Close,ErrorCallback tcpconnection(eventloop* loop,const string& name,int sockfd, const inetaddress& localAddr,const inetaddress& peerAddr); //析构函数,打印一下日志啥也不干 ~tcpconnection(); //获取成员信息:eventloop,name,server/client地址,连接是否建立 eventloop* getLoop() const{return m_loop;} const string& name() const{return m_name;} const inetaddress& localAddress() const {return m_localAddr;} const inetaddress& peerAddress() const {return m_peerAddr;} bool connected() const{return m_state==kConnected;} bool disconnected() const{return m_state==kDisconnected;} //内部利用Socket::getTcpInfo()获取tcp信息 bool getTcpInfo(struct tcp_info*) const; //内部利用Socket::getTcpInfoString()格式化tcp信息 string getTcpInfoString() const; void send(const string& message); void send(const void* message,int len); void send(buffer* message); //关闭tcp连接,并让eventloop回调&TcpConnection::shutdownInLoop() void shutdown(); //让eventloop强制关闭tcp连接,利用eventloop的定时器队列实现(唤醒poller::poll()来关闭),eventloop //回调tcpconnection::forceCloseInLoop() void forceClose(); //强制在seconds秒后关闭tcp连接 void forceCloseWithDelay(double seconds); //sockets::setTcpNoDelay() void setTcpNoDelay(bool on); //eventloop回调tcpconenction::startReadInLoop() void startRead(); //eventloop回调tcpconnection::stopReadInLoop() void stopRead(); bool isReading() const{return m_reading;} void setContext(const boost::any& context){m_context=context;} const boost::any& getContext()const{return m_context;} boost::any* getMutableContext(){return &m_context;} //设置四个回调函数 void setConnectionCallback(const ConnectionCallback& cb){ m_connectionCallback = cb; } void setMessageCallback(const MessageCallback& cb){ m_messageCallback = cb; } void setWriteCompleteCallback(const WriteCompleteCallback& cb) { m_writeCompleteCallback = cb; } void setHighWaterMarkCallback(const HighWaterMarkCallback& cb, size_t highWaterMark) { m_highWaterMarkCallback = cb; m_highWaterMark = highWaterMark; } //获取读/写缓冲区 buffer* inputBuffer(){return &m_inputbuffer;} buffer* outputBuffer(){return &m_outputbuffer;} //仅作为内部使用 void setCloseCallback(const CloseCallback& cb){m_closeCallback=cb;} void connectEstablished(); void connectDestroyed(); private: //tcpconnection 的四种状态,连接未建立,正在建立,已建立,正在断开 enum StateE{kDisconnected,kConnecting,kConnected,kDisconnecting}; //处理读/写网络事件,供channel回调 void handleRead(timestamp receiveTime); void handleWrite(); //处理关闭连接的网络事件,让channel不再关心任何网络事件. void handleClose(); //处理错误事件,利用sockets::getSocketError()来实现 void handleError(); void sendInLoop(const string& message); void sendInLoop(const void* message,size_t len); //关闭tcpconenction void shutdownInLoop(); //供eventloop回调使用,用来强制关闭tcp连接 void forceCloseInLoop(); void setState(StateE s){m_state=s;} //把tcp连接状态转换成字符串形式 const char* stateToString() const; //让channel注册读网络事件 void startReadInLoop(); //让channel不再关心读网络事件 void stopReadInLoop(); eventloop* m_loop; //所在的eventloop const string m_name; //name StateE m_state; //连接状态,有上面四种 bool m_reading; //是否正在读 std::unique_ptr<Socket> m_socket; //server Socket* std::unique_ptr<channel> m_channel; //socket对应的channel const inetaddress m_localAddr; //server地址 const inetaddress m_peerAddr; //client地址 ConnectionCallback m_connectionCallback; //连接成功的回调函数 MessageCallback m_messageCallback; //读事件成功的回调函数 WriteCompleteCallback m_writeCompleteCallback; //写事件完成的回调函数 HighWaterMarkCallback m_highWaterMarkCallback; //...? CloseCallback m_closeCallback; //连接关闭时的回调函数 size_t m_highWaterMark; //...? buffer m_inputbuffer; //TCP读缓冲区 buffer m_outputbuffer; //TCP写缓冲区 boost::any m_context; //...? }; typedef std::shared_ptr<tcpconnection> TcpConnectionPtr; }//namespace net }//namespace mymuduo #endif // TCPCONNECTION_H
tcpconnection.cpp
#include "tcpconnection.h" #include"base/logging.h" #include"net/channel.h" #include"base/weakcallback.h" #include"net/eventloop.h" #include"net/socket.h" #include"net/socketsops.h" #include<errno.h> namespace mymuduo { namespace net { //"net/callbacks.h"中defaultConnectionCallback和defaultMessageCallback的定义 void defaultConnectionCallback(const TcpConnectionPtr& conn) { LOG_TRACE << conn->localAddress().toIpPort() << " -> " << conn->peerAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); // do not call conn->forceClose(), because some users want to register message callback only. } void defaultMessageCallback(const TcpConnectionPtr&, buffer* buf, timestamp) { buf->retrieveAll(); } //构造函数,初始化成员,设置四个回调函数,Read,Write,Close,ErrorCallback tcpconnection::tcpconnection(eventloop* loop,const string& name,int sockfd, const inetaddress& localAddr,const inetaddress& peerAddr) :m_loop(loop),m_name(name),m_state(kConnecting),m_reading(true), m_socket(new Socket(sockfd)), m_channel(new channel(loop,sockfd)), m_localAddr(localAddr), m_peerAddr(peerAddr) { //设置channel::handleEvent()时的四个回调函数,读/写/关闭/错误 m_channel->setReadCallback(std::bind(&tcpconnection::handleRead,this,_1)); m_channel->setWriteCallback(std::bind(&tcpconnection::handleWrite,this)); m_channel->setCloseCallback(std::bind(&tcpconnection::handleClose,this)); m_channel->setErrorCallback(std::bind(&tcpconnection::handleError,this)); LOG_DEBUG << "TcpConnection::ctor[" << m_name << "] at " << this << " fd=" << sockfd; m_socket->setKeepAlive(true);//设置是否开启keepalive判断通信方是否存活 } //析构函数,打印一下日志啥也不干 tcpconnection::~tcpconnection() { LOG_DEBUG << "TcpConnection::dtor[" << m_name << "] at " << this << " fd=" << m_channel->fd() << " state=" << stateToString(); assert(m_state == kDisconnected); } //内部利用Socket::getTcpInfo()获取tcp信息 bool tcpconnection::getTcpInfo(struct tcp_info* tcpi) const { return m_socket->getTcpInfo(tcpi); } //内部利用Socket::getTcpInfoString()格式化tcp信息 string tcpconnection::getTcpInfoString() const { char buf[102]={0}; buf[0]='\0'; m_socket->getTcpInfoString(buf,sizeof buf); return buf; } //发送数据,内部让eventloop回调tcpconnection::sendInLoop()来实现发送数据 void tcpconnection::send(const string& message) { if(m_state==kConnected) { //让eventloop回调tcpconnection::sendInLoop完成发送数据操作 if(m_loop->isInLoopThread()) sendInLoop(message); else { //函数指针fp指向sendInLoop; void (tcpconnection::*fp)(const string& message) = &tcpconnection::sendInLoop; m_loop->runInLoop(std::bind(fp,this,message.data())); } } } void tcpconnection::send(const void* message,int len) { send(string(static_cast<const char*>(message),len)); } void tcpconnection::send(buffer* buf) { if(m_state==kConnected) { if(m_loop->isInLoopThread()) { // sendInLoop(buf->peek(),buf->readableBytes()); buf->retrieveAll(); } else { void (tcpconnection::*fp)(const string& message)=&tcpconnection::sendInLoop; m_loop->runInLoop( std::bind(fp,this,buf->retrieveAllAsString())); } } } //关闭tcp连接,并让eventloop回调&TcpConnection::shutdownInLoop() void tcpconnection::shutdown() { //只有已经建立了连接才可以关闭连接 if(m_state==kConnected) { setState(kDisconnected); //让eventloop回调tcpconnection::shutdownInLoop m_loop->runInLoop(std::bind(&tcpconnection::shutdownInLoop,this)); } } //让eventloop强制关闭tcp连接,利用eventloop的定时器队列实现(唤醒poller::poll()来关闭),eventloop //回调tcpconnection::forceCloseInLoop() void tcpconnection::forceClose() { if(m_state==kConnected || m_state==kDisconnecting) { setState(kDisconnecting); //让eventloop回调tcpconnection::forceCloseInLoop m_loop->queueInLoop(std::bind(&tcpconnection::forceCloseInLoop,shared_from_this())); } } //强制在seconds秒后关闭tcp连接 void tcpconnection::forceCloseWithDelay(double seconds) { if(m_state==kConnected || m_state==kDisconnecting) { setState(kDisconnecting); m_loop->runAfter(seconds, makeWeakCallback(shared_from_this(),&tcpconnection::forceClose)); } } //sockets::setTcpNoDelay() void tcpconnection::setTcpNoDelay(bool on) { m_socket->setTcpNoDelay(on); } //eventloop回调tcpconenction::startReadInLoop() void tcpconnection::startRead() { m_loop->runInLoop(std::bind(&tcpconnection::startReadInLoop,this)); } //eventloop回调tcpconnection::stopReadInLoop() void tcpconnection::stopRead() { m_loop->runInLoop(std::bind(&tcpconnection::stopReadInLoop,this)); } //仅作为内部使用 void tcpconnection::connectEstablished() { m_loop->assertInLoopThread(); assert(m_state==kConnecting); setState(kConnected); m_channel->tie(shared_from_this()); m_channel->enableReading(); m_connectionCallback(shared_from_this()); } void tcpconnection::connectDestroyed() { m_loop->assertInLoopThread(); if(m_state==kConnected) { setState(kDisconnected); m_channel->disableAll(); m_connectionCallback(shared_from_this()); } m_channel->remove(); } //处理读网络事件,供channel回调,把数据读到 m_inputbuffer中 void tcpconnection::handleRead(timestamp receiveTime) { m_loop->assertInLoopThread(); int saveErrno=0; //调用sockets::readv()把数据读到m_inputbuffer中 ssize_t n=m_inputbuffer.readFd(m_channel->fd(),&saveErrno); //读到了数据调用m_messageCallback()回调函数 if(n>0) m_messageCallback(shared_from_this(),&m_inputbuffer,receiveTime); else if(n==0) handleClose(); else { errno=saveErrno; LOG_SYSERR<<"TcpConnection::handleRead"; handleError(); } } //处理写网络事件,把m_outputbuffer中的数据写到m_channel->fd()上,即发送数据 void tcpconnection::handleWrite() { m_loop->assertInLoopThread(); if(m_channel->isWriting()) { ssize_t n=sockets::write(m_channel->fd(),m_outputbuffer.peek(), m_outputbuffer.readableBytes()); if(n>0) //成功写入的字节数 { m_outputbuffer.retrieve(n);//移动写缓冲区的m_writerIndex //写缓冲区中所有数据都发完了,调用写事件完成回调函数 if(m_outputbuffer.readableBytes()==0) m_loop->queueInLoop(std::bind(m_writeCompleteCallback,shared_from_this())); if(m_state==kDisconnecting) shutdownInLoop(); } else { LOG_SYSERR<<"TcpConnection::handleWrite"; } }else { LOG_TRACE<<"Connection fd = "<<m_channel->fd()<<" is down, no more writing"; } } //处理关闭连接的网络事件,让channel不再关心任何网络事件. //主动调用tcpconnection::forceClose()或者远方断开连接都会触发这个回调 void tcpconnection::handleClose() { m_loop->assertInLoopThread(); LOG_TRACE << "fd = " << m_channel->fd() << " state = " << stateToString(); assert(m_state==kConnected || m_state==kDisconnecting); setState(kDisconnected); m_channel->disableAll(); //取关所有网络事件 TcpConnectionPtr guardThis(shared_from_this()); m_connectionCallback(guardThis); m_closeCallback(guardThis); } //处理错误事件,利用sockets::getSocketError()来实现 void tcpconnection::handleError() { int err = sockets::getSocketError(m_channel->fd()); LOG_ERROR << "TcpConnection::handleError [" << m_name << "] - SO_ERROR = " << err << " " << strerror_tl(err); } void tcpconnection::sendInLoop(const string& message) { sendInLoop(message.data(),message.size()); } //在eventloop中回调这个sendInLoop(),用于发送message数据 //内部通过sockets::write()实现发送数据,先发送一次,如果没全部发送完,把数据先 //放到outputbuffer中,让channel注册写网络事件,等到内核可写时,socket就会出发可写 //网络事件,channel就回调handleWrite(),把outputbuffer中的数据发送出去 void tcpconnection::sendInLoop(const void* data,size_t len) { m_loop->assertInLoopThread(); ssize_t nwrote=0; //已经发了多少字节 size_t remaining =len; //还剩下多少字节没发送 bool faultError=false; //错误 if(m_state==kDisconnected) //如果断开了连接 { LOG_WARN<<"disconnected,give up writing"; return; } //if no thing in output queue,try writing directly if(!m_channel->isWriting() && m_outputbuffer.readableBytes()==0) { nwrote=sockets::write(m_channel->fd(),data,len); if(nwrote>=0) { //成功发了nwrote字节 remaining=len-nwrote; //如果全部发完了,让eventloop回调写成功m_writeCompleteCallback函数 if(remaining==0 && m_writeCompleteCallback) m_loop->queueInLoop(std::bind(m_writeCompleteCallback,shared_from_this())); } else { //写失败 nwrote=0; if(errno!=EWOULDBLOCK) { LOG_SYSERR<<"TcpConnection::sendInLoop"; if(errno==EPIPE || errno ==ECONNRESET) faultError=true; } } } assert(remaining<=len); if(!faultError && remaining>0) { //还剩下多少字节没写出去 size_t oldLen=m_outputbuffer.readableBytes(); //发生特定条件,让eventloop回调m_highWaterMarkCallback if(oldLen+remaining>=m_highWaterMark && oldLen<m_highWaterMark && m_highWaterMarkCallback) { m_loop->runInLoop(std::bind(m_highWaterMarkCallback,shared_from_this(), oldLen+remaining)); } //让 写缓冲区 追加 没写完的数据,并设置 m_channel注册写网络事件,用于下次继续写 m_outputbuffer.append(static_cast<const char*>(data)+nwrote,remaining); if(!m_channel->isWriting()) m_channel->enableWriting(); } } //关闭tcpconenction,这个函数由eventloop调用 void tcpconnection::shutdownInLoop() { //保证eventloop在其所在的线程而非当前线程调用该函数 m_loop->assertInLoopThread(); //关闭套接字写操作 if(!m_channel->isWriting()) m_socket->shutdownWrite(); } //供eventloop回调使用,用来强制关闭tcp连接 void tcpconnection::forceCloseInLoop() { m_loop->assertInLoopThread(); if(m_state==kConnected || m_state == kDisconnected) handleClose(); } //把tcp连接状态转换成字符串形式 const char* tcpconnection::stateToString() const { switch (m_state) { case kDisconnected: return "kDisconnected"; case kConnecting: return "kConnecting"; case kConnected: return "kConnected"; case kDisconnecting: return "kDisconnecting"; default: return "unknown state"; } } //让channel注册读网络事件 void tcpconnection::startReadInLoop() { m_loop->assertInLoopThread(); if(!m_reading || !m_channel->isReading()) { m_channel->enableReading(); //让m_channel注册读网络事件 m_reading=true; } } //让channel不再关心读网络事件 void tcpconnection::stopReadInLoop() { m_loop->assertInLoopThread(); if(m_reading || m_channel->isReading()) { m_channel->disableReading(); //让m_channel取关读网络事件 m_reading=false; } } } }
测试:
tcpconnection是为tcpserver服务的,我们待会测试。