muduo 25 TcpConnection

目录

TcpConnection功能:

 TcpConnection成员变量:

TcpConnection构造函数

TcpConnection回调函数

TcpConnection::handleRead 

TcpConnection::handleWrite

TcpConnection::handleClose

TcpConnection::handleError

TcpConnection::send

TcpConnection::sendInLoop

TcpConnection::connectEstablished()

TcpConnection::connectDestroyed()

TcpConnection::shutdown()

TcpConnection::shutdownInLoop()​编辑

TcpConnection.h

TcpConnection.cc


TcpConnection功能:

  • TcpConnection主要负责连接,就是客户端和服务器之间建立的连接;
  • TcpConnection表示的是通信连接成功用户在服务端的一种数据的封装的表示;
  • mainloop通过acceptor获取一个新用户的连接以后,就会将相应的socket、Channel装在TcpConnection,通过轮询算法交给subloop;

TCP connection是用在什么地方呢?

        它主要就是用来打包呢,成功连接服务器的客户端的这么一条通信链路,

 1.TCP server调用new connection,mainloop拿到连接成功的通信connfd,mainloop通过轮询选择出来的一个subloop。

2. 最终将connfd打包成TopConnection,将这个新的TcpConnection保存到TcpServer中的connections_哈希表中,并将connfd和它的Channel注册到subloop的Poller,也就是epoll上; (TcpServer:newConnection函数就是做这个事的)

 * TcpServer => Acceptor => 有一个新用户连接,通过accept函数拿到connfd

 * =》 TcpConnection 设置回调 =》 Channel =》 Poller =》

        Channel的回调操作(Channel::handleEventWithGuard())

 TcpConnection成员变量:

  • Socket和Channel: TcpConnection成员变量包含Socket和Channel (Channel中的fd就是封装好的Socket)
  • 回调函数:TcpConnection里面有用户预设置好的回调函数                            (ConnectionCalback: 连接成功执行的回调;                                                   MessageCallback: 连接上有数据发生执行的回调;                                        WriteCompleteCallback:服务端给客户端将数据send完之后的回调;HighWaterMarkCallback:高水位执行的回调;CloseCallback: 断开连接的回调)
  • Buffer缓冲区

​​​​​​​

TcpConnection构造函数

①: 检查传入loop是否为空,注意这里TcpConnection都是在subloop中的;

②: 创建一个Socket;

③: 打包一个channel;

④:给channel设置的回调函数。 

TcpConnection回调函数

TcpConnection::handleRead 

①:把fd中数据端读到inputBuffer接收数据缓冲区中,成功,返回接收数据大小;

②: 设置成功读取数据的回调函数messageCallback,share_from_this返回当前TcpConnection对象的一个智能指针;

③:客户端断开连接回调;

④:错误回调。

TcpConnection::handleWrite

①:发送缓冲区通过fd发送数据;

②: 发送缓冲区中可读数据为0,说明发送完成;

③: 执行消息发送完后的回调;

④: 读完数据正在关闭状态,关闭写端

⑤: 调用handleWrite但是channel此时是不可写状态,打印日志

TcpConnection::handleClose

 ①: 设置连接状态为已经断开连接;

②: 把channel设置为不感兴趣,相当于从poller中退出;

③:执行连接关闭的回调;这个回调在连接成功和连接关闭都会响应;

④:专门另做了一个关闭连接的回调。实际上在④中也可以完成。

TcpConnection::handleError

::getsockopt() 是一个系统调用函数,用于获取指定套接字选项的值。

具体解释如下:

channel_->fd() 是一个套接字文件描述符,它表示要获取选项值的套接字。

SOL_SOCKET 是一个常量,表示选项所属的协议层,它指示 getsockopt() 获取的是套接字级别的选项。

SO_ERROR 是一个选项名,表示要获取的具体选项。

&optval 是一个指向变量的指针,用于接收获取到的选项值。

&optlen 是一个指向变量的指针,表示 optval 缓冲区的大小,也用于返回实际选项值的大小。

TcpConnection::send

        用户会给TcpSever注册一个MessageCallback回调函数,就是已建立连接有读写事件的时候,MessageCalback会响应,MessageCallback处理完业务代码后,会通过这个send方法给客户端用户返回信息!

         state_必须要是kConnected状态,必须是已连接状态才能使用send函数将数据放到发送缓冲区中。

        ①:判断一下该loop是不是在当前线程下,如果是,直接调用TcpConnection::sendInLoop函数发送数据;

        ②:如果不是,通过runInLoop找到loop_所在线程,并通过绑定函数bind将TcpConnection::sendInLoop绑定到loop中。

TcpConnection::sendInLoop

 ①:判断此时我们的channel不处于正在读的状态,且现在读缓冲区中没有数据,说明这是第一次开始写数据;

 ②: nwrote >= 0说明发送成功,nwrote 是发送成功的数据大小,更新remaining剩余还没有发送完的数据大小,如果为空说明发送完毕,调用发送完的回调函数writeCompleteCallback_;

③: nwrote < 0发送失败,nwrote 重置为0,因为nwrote代表已发送数据量,打印错误信息;

④: 表示没有发生错误但是remaining>0代表上一次数据还没发送完毕,要进行水位控制操作,

如果oldLen(已经在发送缓冲区中的数据部分)小于highWaterMark_(水位线),但是oldLen+remaining>highWaterMark_这个时候就要控制数据发送的速度了。这里主要是因为我们数据从应用到发送过来是非阻塞IO速度很快,但内核处理数据相对较满,需要控制两者速度一直才能更好的处理事件。

⑤: 把remaining剩余数据append添加到outputBuffer_之上。

TcpConnection::connectEstablished()

创建连接的时候使用

         将TcpConnection的一个shared_ptr通过tie函数传过去,并使用一个weak_ptr接收,用一个弱智能指针绑定TcpConnection资源,监视TcpConnection资源作用!防止TcpConnection资源被释放;防止TcpConnection被remove掉之后,Channel底层调用的回调函数就没了!(强弱智能指针搭配使用,既可以监控对象的状态,还可以解决多线程访问共享对象的安全问题! )

TcpConnection::connectDestroyed()

TcpConnection::shutdown()

TcpConnection::shutdownInLoop()

TcpConnection.h

#pragma once

#include "noncopyable.h"
#include "InetAddress.h"
#include "Callbacks.h"
#include "Buffer.h"
#include "Timestamp.h"

#include <memory>
#include <string>
#include <atomic>

class Channel;
class EventLoop;
class Socket;

/**
 * TcpServer => Acceptor => 有一个新用户连接,通过accept函数拿到connfd
 * =》 TcpConnection 设置回调 =》 Channel =》 Poller =》 Channel的回调操作
 * 
 */ 
class TcpConnection : noncopyable, public std::enable_shared_from_this<TcpConnection>
{
public:
    TcpConnection(EventLoop *loop, 
                const std::string &name, 
                int sockfd,
                const InetAddress& localAddr,
                const InetAddress& peerAddr);
    ~TcpConnection();

    EventLoop* getLoop() const { return loop_; }
    const std::string& name() const { return name_; }
    const InetAddress& localAddress() const { return localAddr_; }
    const InetAddress& peerAddress() const { return peerAddr_; }

    bool connected() const { return state_ == kConnected; }//查看是否成功了

    //发送数据
    void send(const std::string &buf);
    //关闭连接
    void shutdown();

    void setConnectionCallback(const ConnectionCallback& cb)
    { connectionCallback_ = cb; }

    void setMessageCallback(const MessageCallback& cb)
    { messageCallback_ = cb; }

    void setWriteCompleteCallback(const WriteCompleteCallback& cb)
    { writeCompleteCallback_ = cb; }

    void setHighWaterMarkCallback(const HighWaterMarkCallback& cb, size_t highWaterMark)
    { highWaterMarkCallback_ = cb; highWaterMark_ = highWaterMark; }

    void setCloseCallback(const CloseCallback& cb)
    { closeCallback_ = cb; }

    //连接建立
    void connectEstablished();
    //连接销毁
    void connectDestroyed();
private:
    //连接状态 : 已经断开 正在连接 连接成功 正在断开
    enum StateE {kDisconnected, kConnecting, kConnected, kDisconnecting};
    void setState(StateE state) { state_ = state; }

    void handleRead(Timestamp receiveTime);
    void handleWrite();
    void handleClose();
    void handleError();

    void sendInLoop(const void* message, size_t len);
    void shutdownInLoop();

    EventLoop *loop_;//这里绝对不是baseLoop, 因为TcpConnection都是在subLoop里面管理的
    const std::string name_; //该连接名字
    std::atomic_int state_; // 枚举 标识连接状态
    bool reading_;

    //这里和Acceptor类似   Acceptor=》mainLoop    TcpConenction=》subLoop
    std::unique_ptr<Socket> socket_;
    std::unique_ptr<Channel> channel_;

    const InetAddress localAddr_;//当前主机IP地址端口号 
    const InetAddress peerAddr_;//对端IP地址端口号 

    ConnectionCallback connectionCallback_;//有新连接时的回调
    MessageCallback messageCallback_;//有读写消息时的回调
    WriteCompleteCallback writeCompleteCallback_;//消息发送完成以后的回调
    HighWaterMarkCallback highWaterMarkCallback_;//水位 控制发送数据的速度
    CloseCallback closeCallback_;
    size_t highWaterMark_;//水位标志

    Buffer inputBuffer_;//接收数据的缓冲区
    Buffer outputBuffer_;//发送数据的缓冲区
};

TcpConnection.cc

#include "TcpConnection.h"
#include "Logger.h"
#include "Socket.h"
#include "Channel.h"
#include "EventLoop.h"

#include <functional>
#include <errno.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <strings.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <string>

static EventLoop* CheckLoopNotNull(EventLoop *loop)
{
    if (loop == nullptr)
    {
        LOG_FATAL("%s:%s:%d TcpConnection Loop is null! \n", __FILE__, __FUNCTION__, __LINE__);
    }
    return loop;
}

TcpConnection::TcpConnection(EventLoop *loop, //构造函数 
                const std::string &nameArg, 
                int sockfd,
                const InetAddress& localAddr,
                const InetAddress& peerAddr)
    : loop_(CheckLoopNotNull(loop))
    , name_(nameArg)
    , state_(kConnecting)
    , reading_(true)
    , socket_(new Socket(sockfd))
    , channel_(new Channel(loop, sockfd))
    , localAddr_(localAddr)
    , peerAddr_(peerAddr)
    , highWaterMark_(64*1024*1024) //超过64M就到水位线了,要停止发送 
{
    //下面给channel设置相应的回调函数,poller给channel通知感兴趣的事件发生了,channel会回调相应的操作函数
    channel_->setReadCallback(
        std::bind(&TcpConnection::handleRead, this, std::placeholders::_1)
    );
    channel_->setWriteCallback(
        std::bind(&TcpConnection::handleWrite, this)
    );
    channel_->setCloseCallback(
        std::bind(&TcpConnection::handleClose, this)
    );
    channel_->setErrorCallback(
        std::bind(&TcpConnection::handleError, this)
    );

    LOG_INFO("TcpConnection::ctor[%s] at fd=%d\n", name_.c_str(), sockfd);
    socket_->setKeepAlive(true);//启动Tcp Socket的保活机制
}


TcpConnection::~TcpConnection()//析构函数 
{
    LOG_INFO("TcpConnection::dtor[%s] at fd=%d state=%d \n", 
        name_.c_str(), channel_->fd(), (int)state_);
}

// onmessage处理完业务结束 通过send给客户端返回处理结果数据
// 对外都提供string类型的  
void TcpConnection::send(const std::string &buf)
{
    if (state_ == kConnected)//连接成功状态
    {
        if (loop_->isInLoopThread())//当前loop是不是在对应的线程 
        {
            sendInLoop(buf.c_str(), buf.size());
        }
        else
        {
            loop_->runInLoop(std::bind(
                &TcpConnection::sendInLoop,
                this,
                buf.c_str(),
                buf.size()
            ));
        }
    }
}

/**
 * 发送数据  应用因为是非阻塞IO它写的快, 而内核发送数据慢,需要两者速度匹配
 * 需要把待发送数据写入缓冲区, 而且设置了水位回调
 */ 
void TcpConnection::sendInLoop(const void* data, size_t len)
{
    ssize_t nwrote = 0; //已发送数据
    size_t remaining = len; //还没发送的数据
    bool faultError = false;

    //之前调用过该connection的shutdown,不能再进行发送了
    if (state_ == kDisconnected)
    {
        LOG_ERROR("disconnected, give up writing!");
        return;
    }

    //表示channel_第一次开始写数据,而且缓冲区没有待发送数据 
    //(Channel向缓冲区写数据,给客户端应用响应)
    if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0)
    {
        nwrote = ::write(channel_->fd(), data, len); //向发送缓冲区中 传入data
        if (nwrote >= 0) //发送成功
        {
            remaining = len - nwrote; //剩余还没有发送完的数据
            if (remaining == 0 && writeCompleteCallback_)
            {
                //既然在这里数据全部发送完成,就不用再给channel设置epollout事件了
                loop_->queueInLoop(
                    std::bind(writeCompleteCallback_, shared_from_this())
                );
            }
        }
        else//nwrote < 0发送失败
        {
            nwrote = 0;
            if (errno != EWOULDBLOCK)
            {
                LOG_ERROR("TcpConnection::sendInLoop");
                if (errno == EPIPE || errno == ECONNRESET) // SIGPIPE  RESET, 表示对端的socket的重置
                {
                    faultError = true;
                }
            }
        }
    }

    //说明当前这一次write,并没有把数据全部发送出去,剩余的数据需要保存到缓冲区当中,然后给channel
    //注册epollout事件,poller发现tcp的发送缓冲区有空间,会通知相应的sock-channel,调用writeCallback_回调方法
    //也就是调用TcpConnection::handleWrite方法,把发送缓冲区中的数据全部发送完成
    if (!faultError && remaining > 0) 
    {
        //目前发送缓冲区剩余的待发送数据的长度
        size_t oldLen = outputBuffer_.readableBytes();
        //发送缓冲区剩余数据 + 剩余还没发送的数据 >= 水位线  
        //且发送缓冲区剩余数据小于水位线 就原本是小于的加上这一些大于了
        if (oldLen + remaining >= highWaterMark_
            && oldLen < highWaterMark_
            && highWaterMarkCallback_)
        {
            loop_->queueInLoop(
                std::bind(highWaterMarkCallback_, shared_from_this(), oldLen+remaining)
            );
        }
        //把待发送数据发送到outputBuffer缓冲区上
        outputBuffer_.append((char*)data + nwrote, remaining);
        if (!channel_->isWriting())
        {
            channel_->enableWriting();//这里一定要注册channel的写事件,否则poller不会给channel通知epollout
        }
    }
}

//关闭连接
void TcpConnection::shutdown()
{
    if (state_ == kConnected)
    {
        setState(kDisconnecting); // 正在断开连接 不会正真的断开连接需要等待发送缓冲区为空
        loop_->runInLoop(
            std::bind(&TcpConnection::shutdownInLoop, this)
        );
    }
}

//回去底层调用handleClose方法
void TcpConnection::shutdownInLoop()
{
    //调用了shutdown并不是真正断开连接 只是把状态设置为 kDisconnecting 
    //等待数据发送完,标志就是!channel_->isWriting() channel没在写了 调用shutdownWrite()彻底关闭
    if (!channel_->isWriting())//说明outputBuffer中的数据已经全部发送完成
    {
        socket_->shutdownWrite();//关闭写端
    }
}

//连接建立 创建连接
void TcpConnection::connectEstablished()
{
    setState(kConnected);
    channel_->tie(shared_from_this());
    channel_->enableReading();//向poller注册channel的epollin事件

    //新连接建立,执行回调
    connectionCallback_(shared_from_this());
}

//连接销毁 连接关闭
void TcpConnection::connectDestroyed()
{
    if (state_ == kConnected)
    {
        setState(kDisconnected);
        channel_->disableAll(); // 把channel的所有感兴趣的事件,从poller中del掉
        connectionCallback_(shared_from_this());
    }
    channel_->remove();//把channel从poller中删除掉
}

//表示fd有数据可读
void TcpConnection::handleRead(Timestamp receiveTime)
{
    int savedErrno = 0;
    ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
    if (n > 0)
    {
        //已建立连接的用户,有可读事件发生了,调用用户传入的回调操作onMessage
        //shared_from_this:获取当前TcpConnection对象的一个智能指针
        messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
    }
    else if (n == 0)//客户端断开连接
    {
        handleClose();
    }
    else
    {
        errno = savedErrno;
        LOG_ERROR("TcpConnection::handleRead");
        handleError();
    }
}

//表示fd可写数据
void TcpConnection::handleWrite()
{
    if (channel_->isWriting()) 
    {
        int savedErrno = 0;
        ssize_t n = outputBuffer_.writeFd(channel_->fd(), &savedErrno);//发送数据
        if (n > 0)
        {
            outputBuffer_.retrieve(n);
            if (outputBuffer_.readableBytes() == 0) //发送完成
            {
                channel_->disableWriting();
                if (writeCompleteCallback_)
                {
                    //唤醒loop_对应的thread线程,执行回调
                    loop_->queueInLoop(
                        std::bind(writeCompleteCallback_, shared_from_this())
                    );
                }
                if (state_ == kDisconnecting) //读完数据正在断开状态(发送缓冲区为空且是正在断开连接状态)
                {
                    shutdownInLoop(); //真正关闭连接
                }
            }
        }
        else
        {
            LOG_ERROR("TcpConnection::handleWrite");
        }
    }
    else //调用handleWrite但是channel此时是不可写状态
    {
        LOG_ERROR("TcpConnection fd=%d is down, no more writing \n", channel_->fd());
    }
}

//poller => channel::closeCallback => TcpConnection::handleClose
void TcpConnection::handleClose()
{
    LOG_INFO("TcpConnection::handleClose fd=%d state=%d \n", channel_->fd(), (int)state_);
    setState(kDisconnected);
    channel_->disableAll();

    TcpConnectionPtr connPtr(shared_from_this());
    connectionCallback_(connPtr);//执行连接关闭的的回调(connectionCallback_回调函数 连接成功和关闭都会响应)
    closeCallback_(connPtr);//关闭连接的回调  执行的是TcpServer::removeConnection回调方法
}

void TcpConnection::handleError()
{
    int optval;
    socklen_t optlen = sizeof optval;
    int err = 0;
    
    if (::getsockopt(channel_->fd(), SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)
    {
        err = errno;
    }
    else
    {
        err = optval;
    }
    LOG_ERROR("TcpConnection::handleError name:%s - SO_ERROR:%d \n", name_.c_str(), err);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值