重写muduo之TcpConnection

2024-05-12 23:04
文章标签 重写 muduo tcpconnection

本文主要是介绍重写muduo之TcpConnection,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1、 TcpConnection.h

2、 TcpConnection.cc


1、 TcpConnection.h

TcpConnection底层绑定(管理)了一个Channel,Channel有事件被Poller通知后,会调用相应的回调,这些回调也是TcpConnection中包含的方法,将这些方法绑定了一下塞给channel作为回调,如果TcpConnection相应的底层对应的channel还在poller上注册着,还会感知到poller通知的事件,并且调用相应的回调,如果此时它对应的TcpConnection对象没有了(被人remove掉)怎么办?

tie使用弱智能指针记录,在处理事件的时候(handleEvent),肯定是被tie过,在这个方法里面将弱智能指针提升一下,如果不做任何的回调调用就说明TcpConnection这个对象已经没有了。

#pragma once
#include "noncopyable.h"
#include "InetAddress.h"
#include "Callbacks.h"
#include "Buffer.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  TcpConnection属于subLoopstd::unique_ptr<Socket> socket_;std::unique_ptr<Channel> channel_;const InetAddress localAddr_;const InetAddress peerAddr_;//回调ConnectionCallback connectionCallback_;//有新连接时的回调MessageCallback messageCallback_;//有读写消息时的回调WriteCompleteCallback writeCompleteCallback_;//消息发送完成以后的回调HighWaterMarkCallback highWaterMarkCallback_;//高水位回调CloseCallback closeCallback_;size_t highWaterMark_;//设置水位线高度//数据的缓冲区Buffer inputBuffer_;//接收数据的缓冲区Buffer outputBuffer_;//发送数据的缓冲区
};

2、 TcpConnection.cc

当在上层调用了某一connection的shutdown()方法,设置当前服务器端的状态是disconnecting,然后执行shutdownInLoop,因为shutdownInLoop关闭了socket的write端,poller就给channel通知了关闭事件,就回调TcpConnection::handclose()方法,handclose()方法相当于将channel所有的事件都去掉。

#include "TcpConnection.h"
#include "Logger.h"
#include "Socket.h"
#include "Channel.h"
#include "EventLoop.h"#include <functional>
#include <errno.h>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <netinet/tcp.h>
#include <sys/socket.h>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);//保活机制}TcpConnection::~TcpConnection()
{LOG_INFO("TcpConnection::dtor[%s] at fd=%d state=%d\n",name_.c_str(),channel_->fd(),(int)state_);
}void TcpConnection::send(const std::string& buf)
{if(state_==kConnected){if(loop_->isInLoopThread()){sendInLoop(buf.c_str(),buf.size());}else{loop_->runInLoop(std::bind(&TcpConnection::sendInLoop,this,buf.c_str(),buf.size()));}}
}/*** 发送数据 应用写的快,而内核发送数据慢,需要把待发送数据写入缓冲区,而且设置了水位回调
*/
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_第一次开始写数据,而且缓冲区没有待发送数据if(!channel_->isWriting()&&outputBuffer_.readableBytes()==0){nwrote=::write(channel_->fd(),data,len);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;//errno==EWOULDBLOCK  由于非阻塞,没有数据时正常的返回if(errno!=EWOULDBLOCK)//真正的错误{LOG_ERROR("TcpConnection::sendInLoop");if(errno==EPIPE||errno==ECONNRESET)// SIGPIPE  RESET 收到连接重置的请求{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_.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));}
}void TcpConnection::shutdownInLoop()
{if(!channel_->isWriting())//说明outputBuffer中的数据已经全部发送完成{socket_->shutdownWrite();//关闭写端}
}// 连接建立
void TcpConnection::connectEstablished()
{setState(kConnected);//设置连接成功状态,初始状态为kConnectingchannel_->tie(shared_from_this());//绑定channel,让这个channel记录一下TcpConnection对象存活的状态channel_->enableReading();//同poller注册channel的epollin事件//新连接建立,执行回调connectionCallback_(shared_from_this());
}// 连接销毁
void TcpConnection::connectDestroyed()
{if(state_==kConnected){setState(kDisconnected);channel_->disableAll();//把channel的所有感兴趣的事件,从poller中del掉  相当于epoll_ctlconnectionCallback_(shared_from_this());//断开连接}channel_->remove();//把channel从poller中删除掉
}void TcpConnection::handleRead(Timestamp receiveTime)
{int savedErrno=0;ssize_t n=inputBuffer_.readFd(channel_->fd(),&savedErrno);if(n>0){//已建立连接的用户,有可读事件发生了,调用用户传入的回调操作omMessagemessageCallback_(shared_from_this(),&inputBuffer_,receiveTime);}else if(n==0){handleClose();}else{errno=savedErrno;LOG_ERROR("TcpConnection::handleRead");handleError();}
}
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(//loop一定在TcpConnection所对应的线程中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();//将channel感兴趣的事件从poller上全部删除TcpConnectionPtr connPtr(shared_from_this());connectionCallback_(connPtr);//执行连接关闭的回调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);
}

这篇关于重写muduo之TcpConnection的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/983987

相关文章

Ext重写手法

常用的几种方式:1、Ext.apply()和Ext.applyIf()2、Ext.override()3、想做某个类大的修改,可以把该类单独从源码中拿出来,直接修改,然后引用时先应用ext-all.js,再引用从源码中拿出修改的那个类4、obj.prototype.method=function(){}

泛型第二课,派生子类、属性类型、方法重写、泛型擦除

子类(实现类) 子类与父类|接口一样使用泛型子类指定具体的类型子类与父类|接口 同时擦除类型子类泛型,父类|接口 擦除错误:不能子类擦除,父类|接口泛型 package com.pkushutong.genericity3;/*** 父类为泛型类* 1、属性* 2、方法* * 要么同时擦除,要么子类大于等于父类的类型* 不能子类擦除,父类泛型* 1、属性类型* 父类中,随父类型定

Python中的方法重写与多态:解锁编程的无限可能

在编程的世界里,灵活性与扩展性往往是衡量一个语言是否强大、易于维护的关键指标。Python,作为一种被广泛使用的高级编程语言,不仅以其简洁易读的语法赢得了众多开发者的喜爱,更因其支持多种面向对象特性而备受青睐。其中,“方法重写”与“多态”便是两个核心概念,它们不仅能够极大地提高代码的复用性和可维护性,还能帮助我们构建更加灵活、健壮的软件系统。本文将通过一系列由浅入深的例子,带你一起探索这两个概念的

【大数据Java基础-JAVA 面向对象13】面向对象的特征二:继承性 (二) 方法的重写

1.什么是方法的重写(override 或 overwrite)? 子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作. 2. 应用: 重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。 3.举例: class Circle{ public double findArea(){}//求面积 } class Cylinder e

iis7 url重写和重定向

注意不管是重写还是redirect重定向,匹配的url都要写成当前网站的url,也就是真是真实可以访问的,如当前网站ip为127.0.0.1,可以写成^127.0.0.1$ (1)url重写(可以实现伪静态) IIS实现反向代理 新建两个站点,端口分别使用 80 和 81,在DNS中新建A记录,指向该计算机(10.4.34.41) 配置过程如下: 1.在Windows Server

重写equals和hashCode的原则规范

当符合以下条件时不需要重写equals方法:     1.     一个类的每一个实例本质上都是唯一的。     2.     不关心一个类是否提供了“逻辑相等”的测试功能     3.     超类已经改写了equals方法,并且从超类继承过来的行为对于子类也是合适的。     4.     一个类时私有的或者是package私有的,并且可以确定它的equals方法永远不会被调用。(这

Django学习(二)(重写User类)

一、重写User类: 1、首先导入User类: from django.contrib.auth.models import User 2、然后点在User上,按住ctrl 点进去,发现 User类继承AbstractUser Ctrl点进去AbstractUser,然后将此方法全部复制到自己APP的models.py里: 可以修改名字,导入 from django.cont

springBoot重写start run方法

Spring Boot允许开发者自定义应用程序启动过程,如果想要重写start方法,通常是在实现了CommandLineRunner, ApplicationRunner, 或者ApplicationListener<ContextRefreshedEvent>接口的类中。这些接口的目的是在Spring应用上下文初始化完成后执行一些定制的操作。 例如,如果你创建了一个类并实现了Applicati

static_隐式参数_继承_重写_toString_组合JAVA036-042

来源:http://www.bjsxt.com/ 1、S01E036_01static变量和方法 static变量和方法存放在方法区中 2、S01E037_01隐式参数this和super 类的构造方法调用自己的无参构造方法this()或有参构造方法this(a[,b…]):必须位于第一行 3、S01E038_01面向对象的三大特征(继承、封装、多态)之一:继承(extends)

【Java】继承性-方法的重写【主线学习笔记】

文章目录 前言方法的重写1、方法重写的规则示例 2、区分方法的重载与重写重写的示例重载的示例 前言 Java是一门功能强大且广泛应用的编程语言,具有跨平台性和高效的执行速度,广受开发者喜爱。在接下来的学习过程中,我将记录学习过程中的基础语法、框架和实践技巧等,分享学习心得,对自己学习过程进行整理和总结,也希望能为其他学习Java的朋友提供一些帮助和参考。 方法的