首页
关于
友联
Search
1
CTF竞赛权威指南(PWN篇)下载地址
3,213 阅读
2
操作系统实现-异常
957 阅读
3
redis服务器
892 阅读
4
集群聊天服务器-一对一聊天
840 阅读
5
ret2shellcode
768 阅读
操作系统
Redis
PWN
muduo
MongoDB
集群聊天服务器
互联网面试
Go
旅途人生
登录
Search
一个爱代码的coder
累计撰写
38
篇文章
累计收到
101
条评论
首页
栏目
操作系统
Redis
PWN
muduo
MongoDB
集群聊天服务器
互联网面试
Go
旅途人生
页面
关于
友联
搜索到
5
篇与
muduo
的结果
2022-05-08
muduo源码分析之TcpServer模块
博客网址:www.shicoder.top微信:kj11011029欢迎加群聊天 :452380935这次我们开始muduo源代码的实际编写,首先我们知道muduo是LT模式,Reactor模式,下图为Reactor模式的流程图[来源1]然后我们来看下muduo的整体架构[来源1]首先muduo有一个主反应堆mainReactor以及几个子反应堆subReactor,其中子反应堆的个数由用户使用setThreadNum函数设置,mainReactor中主要有一个Acceptor,当用户建立新的连接的时候,Acceptor会将connfd和对应的事件打包为一个channel然后采用轮询的算法,指定将该channel给所选择的subReactor,以后该subReactor就负责该channel的所有工作。TcpServer类我们按照从上到下的思路进行讲解,以下内容我们按照一个简单的EchoServer的实现思路来讲解,我们知道当我们自己实现一个Server的时候,会在构造函数中实例化一个TcpServerEchoServer(EventLoop *loop, const InetAddress &addr, const std::string &name) : server_(loop, addr, name) , loop_(loop) { // 注册回调函数 server_.setConnectionCallback( std::bind(&EchoServer::onConnection, this, std::placeholders::_1) ); server_.setMessageCallback( std::bind(&EchoServer::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) ); // 设置合适的loop线程数量 loopthread 不包括baseloop server_.setThreadNum(3); }于是我们去看下TcpServer的构造函数是在干什么TcpServer::TcpServer(EventLoop *loop, const InetAddress &listenAddr, const std::string &nameArg, Option option) : loop_(CheckLoopNotNull(loop)) , ipPort_(listenAddr.toIpPort()) , name_(nameArg) , acceptor_(new Acceptor(loop, listenAddr, option == kReusePort)) , threadPool_(new EventLoopThreadPool(loop, name_)) , connectionCallback_() , messageCallback_() , nextConnId_(1) , started_(0) { // 当有新用户连接时候,会执行该回调函数 acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this, std::placeholders::_1, std::placeholders::_2)); }我们只需要关注acceptor_(new Acceptor(loop, listenAddr, option == kReusePort))和threadPool_(new EventLoopThreadPool(loop, name_))首先很明确的一点,构造了一个Acceptor,我们首先要知道Acceptor主要就是连接新用户并打包为一个Channel,所以我们就应该知道Acceptor按道理应该实现socket,bind,listen,accept这四个函数。Acceptor::Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport) : loop_(loop), acceptSocket_(createNonblocking()) // socket , acceptChannel_(loop, acceptSocket_.fd()), listenning_(false) { acceptSocket_.setReuseAddr(true); acceptSocket_.setReusePort(true); acceptSocket_.bindAddress(listenAddr); // 绑定套接字 // 有新用户的连接,执行一个回调(打包为channel) acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this)); }其中Acceptor中有个acceptSocket_,其实就是我们平时所用的listenfd,构造函数中实现了socket,bind,而其余的两个函数的使用在其余代码// 开启服务器监听 void TcpServer::start() { // 防止一个TcpServer被start多次 if (started_++ == 0) { threadPool_->start(threadInitCallback_); // 启动底层的loop线程池,这里会按照设定了threadnum设置pool的数量 loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get())); } }我们知道,当我们设置了threadnum之后,就会有一个mainloop,那么这个loop_就是那个mainloop,其中可以看见这个loop_就只做一个事情Acceptor::listen。void Acceptor::listen() { listenning_ = true; acceptSocket_.listen(); // listen acceptChannel_.enableReading(); // acceptChannel_ => Poller } 这里就实现了listen函数,还有最后一个函数accept,我们慢慢向下分析,从代码可以知道acceptChannel_.enableReading()之后就会使得这个listenfd所在的channel对读事件感兴趣,那什么时候会有读事件呢,就是当用户建立新连接的时候,那么我们应该想一下,那当感兴趣的事件发生之后,listenfd应该干什么呢,应该执行一个回调函数呀。注意Acceptor构造函数中有这样一行代码acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this));这就是那个回调,我们去看下handleRead在干嘛。// listenfd有事件发生了,就是有新用户连接了 void Acceptor::handleRead() { InetAddress peerAddr; int connfd = acceptSocket_.accept(&peerAddr); if (connfd >= 0) { // 若用户实现定义了,则执行,否则说明用户对新到来的连接没有需要执行的,所以直接关闭 if (newConnectionCallback_) { newConnectionCallback_(connfd, peerAddr); // 轮询找到subLoop,唤醒,分发当前的新客户端的Channel } else { ::close(connfd); } } ... }这里是不是就实现了accept函数,至此当用户建立一个新的连接时候,Acceptor就会得到一个connfd和其对应的peerAddr返回给mainloop,这时候我们在注意到TcpServer构造函数中有这样一行代码acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this,std::placeholders::_1, std::placeholders::_2));我们给acceptor_设置了一个newConnectionCallback_,于是由上面的代码就可以知道,if (newConnectionCallback_)为真,就会执行这个回调函数,于是就会执行TcpServer::newConnection,我们去看下这个函数是在干嘛。void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr) { // 轮询算法选择一个subloop来管理对应的这个新连接 EventLoop *ioLoop = threadPool_->getNextLoop(); char buf[64] = {0}; snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_); ++nextConnId_; std::string connName = name_ + buf; LOG_INFO("TcpServer::newConnection [%s] - new connection [%s] from %s \n", name_.c_str(), connName.c_str(), peerAddr.toIpPort().c_str()); // 通过sockfd获取其绑定的本地ip和端口 sockaddr_in local; ::bzero(&local, sizeof local); socklen_t addrlen = sizeof local; if (::getsockname(sockfd, (sockaddr*)&local, &addrlen) < 0) { LOG_ERROR("sockets::getLocalAddr"); } InetAddress localAddr(local); // 根据连接成功的sockfd,创建TcpConnection TcpConnectionPtr conn(new TcpConnection( ioLoop, connName, sockfd, // Socket Channel localAddr, peerAddr)); connections_[connName] = conn; // 下面的回调时用户设置给TcpServer,TcpServer又设置给TcpConnection,TcpConnetion又设置给Channel,Channel又设置给Poller,Poller通知channel调用这个回调 conn->setConnectionCallback(connectionCallback_); conn->setMessageCallback(messageCallback_); conn->setWriteCompleteCallback(writeCompleteCallback_); // 设置了如何关闭连接的回调 conn->setCloseCallback( std::bind(&TcpServer::removeConnection, this, std::placeholders::_1) ); // 直接调用connectEstablished ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn)); }这里就比较长了,我先说下大概他干了啥事情:首先通过轮询找到下一个subloop,然后将刚刚返回的connfd和对应的peerAddr以及localAddr构造为一个TcpConnection给subloop,然后给这个conn设置了一系列的回调函数,比如读回调,写回调,断开回调等等。下一章我们来说下上面的代码最后几行在干嘛。
2022年05月08日
105 阅读
0 评论
0 点赞
2022-05-08
muduo源码分析之回调模块
博客网址:www.shicoder.top微信:kj11011029欢迎加群聊天 :452380935这次我们主要来说说muduo库中大量使用的回调机制。muduo主要使用的是利用Callback的方式来实现回调,首先我们在自己的EchoServer构造函数中有这样几行代码 EchoServer(EventLoop *loop, const InetAddress &addr, const std::string &name) : server_(loop, addr, name) , loop_(loop) { // 注册回调函数 server_.setConnectionCallback( std::bind(&EchoServer::onConnection, this, std::placeholders::_1) ); server_.setMessageCallback( std::bind(&EchoServer::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) ); // 设置合适的loop线程数量 loopthread server_.setThreadNum(3); }使用了setConnectionCallback和setMessageCallback,我们去看下TcpServer对setConnectionCallback怎么实现的void setConnectionCallback(const ConnectionCallback &cb) { connectionCallback_ = cb; }这样当有新的连接建立时候,就会执行我们设置的EchoServer::onConnection,这样我们就给TcpServer设置了一个回调函数,同时当TcpServer中的Acceptor接受到一个新的连接,TcpServer就会去用这个connfd和对应的peerAddr建立一个新的TcpConnection,同时TcpServer会给这个TcpConnection设置一个回调,而这个回调就是我们给TcpServer设置的回调 // 根据连接成功的sockfd,创建TcpConnection TcpConnectionPtr conn(new TcpConnection( ioLoop, connName, sockfd, // Socket Channel localAddr, peerAddr)); connections_[connName] = conn; // 下面的回调时用户设置给TcpServer,TcpServer又设置给TcpConnection,TcpConnetion又设置给Channel,Channel又设置给Poller,Poller通知channel调用这个回调 conn->setConnectionCallback(connectionCallback_); conn->setMessageCallback(messageCallback_); conn->setWriteCompleteCallback(writeCompleteCallback_);当已经建立的连接有新消息来的时候,conn->setMessageCallback(messageCallback_);这一行代码表示我们给这个conn设置了一个有消息来的时候回调,我们去看下TcpConnection中对setMessageCallback是怎么处理的 void setConnectionCallback(const ConnectionCallback& cb) { connectionCallback_ = cb; }所以有消息来的时候,就会执行我们所设置的回调函数onMessage。到这里我么就基本知道新连接的建立和旧连接的消息到来应该做什么,下一章我们说一下消息之间发送的Buffer类。
2022年05月08日
71 阅读
0 评论
0 点赞
2022-05-08
muduo源码分析之muduo简单运用
博客网址:www.shicoder.top微信:kj11011029欢迎加群聊天 :452380935今天不先实现muduo项目,我们先来看下muduo库的基本使用,只有了解了如何用,才能在写代码的时候知道自己写的找个函数是干嘛的,实际上是怎么使用的这个函数。首先说简单点,就是定义一个Server,设置两个回调函数// 回调连接相关的事件 void onConnection(const TcpConnectionPtr &conn); // 回调读写事件 void onMessage(const TcpConnectionPtr &conn, Buffer *buffer, Timestamp time);意思就是当有客户连接或者断开连接的时候,需要Server做什么,当连接上有读写事件发生时候,需要Server做什么。比如一个EchoServer,当建立连接的时候,会自动调用onConnection函数,当比如我们发送一个消息时候,会自动调用onMessage函数。还有2个重要函数loop,startserver.start(); loop.loop();这里简答讲下这2个的区别,其实如果和Epoll做对比的话,start就相当于epoll_create,loop就相当于epoll_wait,后面再根据代码具体说明2个的区别。以上就是基本的muduo使用,下一章就开始具体的muduo代码实现。
2022年05月08日
80 阅读
0 评论
1 点赞
2022-05-08
muduo源码分析之Buffer
博客网址:www.shicoder.top微信:kj11011029欢迎加群聊天 :452380935这一次我们来分析下muduo中Buffer的作用,
2022年05月08日
177 阅读
0 评论
0 点赞
2022-05-08
muduo项目介绍
博客网址:www.shicoder.top微信:kj11011029欢迎加群聊天 :452380935在上一个集群聊天服务器项目中,我使用了muduo作为网络库,然后主要实现了业务逻辑等,所以为了深入网络库的代码和实现,我跟着一位老师的代码去实现了muduo库的基本原理和作用,当然只是实现了主体的代码,有些细节便没有深究,以下是自己的目标:
2022年05月08日
244 阅读
0 评论
0 点赞
首页
复制
搜索
前进
后退
重载网页
和我当邻居