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服务的,我们待会测试。

 

posted @ 2020-09-02 19:24  WoodInEast  阅读(298)  评论(0编辑  收藏  举报