muduo源码剖析之TcpServer服务端

简介

TcpServer拥有Acceptor类,新连接到达时new TcpConnection后续客户端和TcpConnection类交互。TcpServer管理连接和启动线程池,用Acceptor接受连接。

服务端封装 - muduo的server端维护了多个tcpconnection
注意TcpServer本身不带Channel,而是使用Acceptor的Channel

成员及属性解析

主要接口

回调setters

这些回调函数会在新连接建立时传递给TcpConnction对象

start

启动threadPool_线程池
在runInLoop中执行acceptor的listen
这里专门设置了一个started_标记,防止多次运行start

核心实现:newConnection

从accept回调中获得的fd动态创建新的TcpConnection对象
为连接对象注册各类回调函数
将连接对象存入connectionMap_映射表里

主要成员

loop

这个loop也是acceptor的loop

acceptor
threadPool

一个EventLoopThreadPool,用来存放io线程的EventLoopThread

socket

服务端用来监听的socket

ConnectionMap

一个连接名和实例(TcpConnectionPtr)的映射容器

TcpServer中回调的传递示意简图:

da77bb9302ff4da9896f0154839d5b82

源码剖析

源码已经编写注释

TcpServer.h

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
// This is a public header file, it must only include public header files.#ifndef MUDUO_NET_TCPSERVER_H
#define MUDUO_NET_TCPSERVER_H#include "muduo/base/Atomic.h"
#include "muduo/base/Types.h"
#include "muduo/net/TcpConnection.h"#include <map>namespace muduo
{
namespace net
{class Acceptor;
class EventLoop;
class EventLoopThreadPool;///
/// TCP server, supports single-threaded and thread-pool models.
///
/// This is an interface class, so don't expose too much details.
class TcpServer : noncopyable
{public:typedef std::function<void(EventLoop*)> ThreadInitCallback;enum Option{kNoReusePort,//不设置端口复用kReusePort,//设置端口复用};//TcpServer(EventLoop* loop, const InetAddress& listenAddr);TcpServer(EventLoop* loop,//mainloopconst InetAddress& listenAddr,const string& nameArg,Option option = kNoReusePort);~TcpServer();  // force out-line dtor, for std::unique_ptr members.const string& ipPort() const { return ipPort_; }const string& name() const { return name_; }EventLoop* getLoop() const { return loop_; }/// Set the number of threads for handling input.////// Always accepts new connection in loop's thread./// Must be called before @c start/// @param numThreads/// - 0 means all I/O in loop's thread, no thread will created.///   this is the default value./// - 1 means all I/O in another thread./// - N means a thread pool with N threads, new connections///   are assigned on a round-robin basis.void setThreadNum(int numThreads);//设置subloop数量void setThreadInitCallback(const ThreadInitCallback& cb)//设置subloop线程初始化时调用的函数{ threadInitCallback_ = cb; }/// valid after calling start()std::shared_ptr<EventLoopThreadPool> threadPool()//返回EventLoopThread线程池{ return threadPool_; }/// Starts the server if it's not listening.////// It's harmless to call it multiple times./// Thread safe.void start();/// Set connection callback./// Not thread safe.void setConnectionCallback(const ConnectionCallback& cb){ connectionCallback_ = cb; }/// Set message callback./// Not thread safe.void setMessageCallback(const MessageCallback& cb){ messageCallback_ = cb; }/// Set write complete callback./// Not thread safe.void setWriteCompleteCallback(const WriteCompleteCallback& cb){ writeCompleteCallback_ = cb; }private:/// Not thread safe, but in loopvoid newConnection(int sockfd, const InetAddress& peerAddr);/// Thread safe.void removeConnection(const TcpConnectionPtr& conn);/// Not thread safe, but in loopvoid removeConnectionInLoop(const TcpConnectionPtr& conn);typedef std::map<string, TcpConnectionPtr> ConnectionMap;EventLoop* loop_;  // the acceptor loopconst string ipPort_;const string name_;std::unique_ptr<Acceptor> acceptor_; // avoid revealing Acceptorstd::shared_ptr<EventLoopThreadPool> threadPool_;ConnectionCallback connectionCallback_;MessageCallback messageCallback_;WriteCompleteCallback writeCompleteCallback_;ThreadInitCallback threadInitCallback_;AtomicInt32 started_;// always in loop threadint nextConnId_;ConnectionMap connections_;
};}  // namespace net
}  // namespace muduo#endif  // MUDUO_NET_TCPSERVER_H

TcpServer.cc

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.// Author: Shuo Chen (chenshuo at chenshuo dot com)#include "muduo/net/TcpServer.h"#include "muduo/base/Logging.h"
#include "muduo/net/Acceptor.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/EventLoopThreadPool.h"
#include "muduo/net/SocketsOps.h"#include <stdio.h>  // snprintfusing namespace muduo;
using namespace muduo::net;TcpServer::TcpServer(EventLoop* loop,const InetAddress& listenAddr,const string& nameArg,Option option): loop_(CHECK_NOTNULL(loop)), //TcpServer所在的主线程下运行的事件驱动循环ipPort_(listenAddr.toIpPort()),/* 服务器负责监听的本地ip和端口 */name_(nameArg),/* 服务器名字,创建时传入 */acceptor_(new Acceptor(loop, listenAddr, option == kReusePort)),/* Acceptor对象,负责监听客户端连接请求,运行在主线程的EventLoop中 */threadPool_(new EventLoopThreadPool(loop, name_)),/* 事件驱动线程池,池中每个线程运行一个EventLoop */connectionCallback_(defaultConnectionCallback),/* 用户传入,有tcp连接到达或tcp连接关闭时调用,传给TcpConnection */messageCallback_(defaultMessageCallback),/* 用户传入,对端发来消息时调用,传给TcpConnection */nextConnId_(1) /* TcpConnection特有id,每增加一个TcpConnection,nextConnId_加一 */
{ /* * 设置回调函数,当有客户端请求时,Acceptor接收客户端请求,然后调用这里设置的回调函数* 回调函数用于创建TcpConnection连接*/acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this, _1, _2));
}TcpServer::~TcpServer()
{loop_->assertInLoopThread();LOG_TRACE << "TcpServer::~TcpServer [" << name_ << "] destructing";for (auto& item : connections_){TcpConnectionPtr conn(item.second);item.second.reset();conn->getLoop()->runInLoop(std::bind(&TcpConnection::connectDestroyed, conn));}
}void TcpServer::setThreadNum(int numThreads)
{assert(0 <= numThreads);threadPool_->setThreadNum(numThreads);
}void TcpServer::start()
{if (started_.getAndSet(1) == 0){threadPool_->start(threadInitCallback_);//启动线程池,threadInitCallback_创建好所有线程后调用的回调函数assert(!acceptor_->listenning());loop_->runInLoop(       //直接调用linsten函数std::bind(&Acceptor::listen, get_pointer(acceptor_)));}
}/* * Acceptor接收客户端请求后调用的回调函数* @param sockfd: 已经接收完成(三次握手完成)后的客户端套接字* @param peerAddr: 客户端地址* * Acceptor只负责接收客户端请求* TcpServer需要生成一个TcpConnection用于管理tcp连接* * 1.TcpServer内有一个EventLoopThreadPool,即事件循环线程池,池子中每个线程都是一个EventLoop* 2.每个EventLoop包含一个Poller用于监听注册到这个EventLoop上的所有Channel* 3.当建立起一个新的TcpConnection时,这个连接会放到线程池中的某个EventLoop中* 4.TcpServer中的baseLoop只用来检测客户端的连接* * 从libevent的角度看就是* 1.EventLoopThreadPool是一个struct event_base的池子,池子中全是struct event_base* 2.TcpServer独占一个event_base,这个event_base不在池子中* 3.TcpConnection会扔到这个池子中的某个event_base中*/
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{loop_->assertInLoopThread();EventLoop* ioLoop = threadPool_->getNextLoop();//从事件驱动线程池中取出一个线程给TcpConnection /* 为TcpConnection生成独一无二的名字 */char buf[64];snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);++nextConnId_;string connName = name_ + buf;LOG_INFO << "TcpServer::newConnection [" << name_<< "] - new connection [" << connName<< "] from " << peerAddr.toIpPort();/* * 根据sockfd获取tcp连接在本地的<地址,端口>* getsockname(int fd, struct sockaddr*, int *size);*/InetAddress localAddr(sockets::getLocalAddr(sockfd));// FIXME poll with zero timeout to double confirm the new connection// FIXME use make_shared if necessary/* 创建一个新的TcpConnection代表一个Tcp连接 */TcpConnectionPtr conn(new TcpConnection(ioLoop,connName,sockfd,localAddr,peerAddr));/* 添加到所有tcp 连接的map中,键是tcp连接独特的名字(服务器名+客户端<地址,端口>) */connections_[connName] = conn;/* 为tcp连接设置回调函数(由用户提供) */conn->setConnectionCallback(connectionCallback_);conn->setMessageCallback(messageCallback_);conn->setWriteCompleteCallback(writeCompleteCallback_);/* * 关闭回调函数,由TcpServer设置,作用是将这个关闭的TcpConnection从map中删除* 当poll返回后,发现被激活的原因是EPOLLHUP,此时需要关闭tcp连接* 调用Channel的CloseCallback,进而调用TcpConnection的handleClose,进而调用removeConnection*/conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafe/* * 连接建立后,调用TcpConnection连接建立成功的函数* 1.新建的TcpConnection所在事件循环是在事件循环线程池中的某个线程* 2.所以TcpConnection也就属于它所在的事件驱动循环所在的那个线程* 3.调用TcpConnection的函数时也就应该在自己所在线程调用* 4.所以需要调用runInLoop在自己的那个事件驱动循环所在线程调用这个函数* 5.当前线程是TcpServer的主线程,不是TcpConnection的线程,如果在这个线程直接调用会阻塞监听客户端请求* 6.其实这里不是因为线程不安全,即使在这个线程调用也不会出现线程不安全,因为TcpConnection本就是由这个线程创建的*/ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}void TcpServer::removeConnection(const TcpConnectionPtr& conn)
{// FIXME: unsafeloop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)
{//关闭连接,把fd从epoll中del掉,要释放connector(包括描述符)和channelloop_->assertInLoopThread();LOG_INFO << "TcpServer::removeConnectionInLoop [" << name_<< "] - connection " << conn->name();size_t n = connections_.erase(conn->name());(void)n;assert(n == 1);EventLoop* ioLoop = conn->getLoop();ioLoop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/148481.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

国民技术Cortex-M0系列单片机IAP升级

考虑到设备部署到现场后有可能需要进行软件升级&#xff0c;之前做过PIC系列单片机的升级&#xff0c;现在想做个国民技术N32G031系列Cortex-M0内核的单片机IAP方案。 因为国民技术系列单片机在很多大程度上都模仿了STM32&#xff0c;所以我想其升级方案极有可能差不多。于是在…

Toolformer论文阅读笔记(简略版)

文章目录 引言方法限制结论 引言 大语言模型在zero-shot和few-shot情况下&#xff0c;在很多下游任务中取得了很好的结果。大模型存在的限制&#xff1a;无法获取最新的信息、无法进行精确的数学计算、无法理解时间的推移等。这些限制可以通过扩大模型规模一定程度上解决&…

视频封面:从视频中提取封面,轻松制作吸引人的视频

在当今的数字时代&#xff0c;视频已成为人们获取信息、娱乐和交流的重要方式。一个吸引人的视频封面往往能抓住眼球&#xff0c;提高点击率和观看率。今天将介绍如何从视频中提取封面&#xff0c;轻松制作吸引人的视频封面。 一、准备素材选择合适的视频片段 首先&#xff0…

ubuntu中用docker部署jenkins,并和码云实现自动化部署

1.部署jenkins docker network create jenkins docker run --name jenkins-docker --rm --detach \--privileged --network jenkins --network-alias docker \--env DOCKER_TLS_CERTDIR/certs \--volume jenkins-docker-certs:/certs/client \--volume jenkins-data:/var/jen…

Java面向对象(高级)-- 单例(Singleton)设计模式

文章目录 一、单例设计模式&#xff08;1&#xff09; 设计模式概述&#xff08;2&#xff09; 何为单例模式&#xff08;3&#xff09; 实现思路&#xff08;4&#xff09; 单例模式的两种实现方式1. 饿汉式2. 懒汉式3. 饿汉式 vs 懒汉式 &#xff08;5&#xff09; 单例模式的…

LeetCode:689. 三个无重叠子数组的最大和(dp C++)

目录 689. 三个无重叠子数组的最大和 题目描述&#xff1a; 实现代码与解析&#xff1a; dp 原理思路&#xff1a; 滑动窗口&#xff1a; 原理思路&#xff1a; 689. 三个无重叠子数组的最大和 题目描述&#xff1a; 给你一个整数数组 nums 和一个整数 k &#xff0c;找…

修完这个 Bug 后,MySQL 性能提升了 300%

最近 MySQL 官方在 8.0.35 上修复了一个 bug&#xff1a; 这个 bug 是由 Mark Callaghan 发现的。Mark 早年在 Google MySQL 团队&#xff0c;后来去了 Meta MySQL&#xff0c;也主导了 RocksDB 的开发。 Mark 在 #109595 的 bug report 给出了非常详细的复现步骤 在官方修复后…

Cross-View Transformers for Real-Time Map-View Semantic Segmentation 论文阅读

论文链接 Cross-View Transformers for Real-Time Map-View Semantic Segmentation 0. Abstract 提出了 Cross-View Transformers &#xff0c;一种基于注意力的高效模型&#xff0c;用于来自多个摄像机的地图视图语义分割使用相机感知的跨视图注意机制隐式学习从单个相机视…

【华为OD机试高分必刷题目】决战(JavaPythonC++动态规划DP实现)

🚀你的旅程将在这里启航!本专栏所有题目均包含优质解题思路,每篇都用了Java&Python&C++三种语言分别解题,高质量解题代码,详细代码讲解,助你深入学习,高分通过! 文章目录 【华为OD机试高分必刷题目】决战(Java&Python&C++动态规划DP实现)题目描述解…

MySQL数据库索引以及使用唯一索引实现幂等性

&#x1f4d1;前言 本文主要是MySQL数据库索引以及使用唯一索引实现幂等性的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1a;CSDN主页放风讲故事 &#x1f30…

【LeetCode:2736. 最大和查询 | 贪心 + 二分 + 单调栈】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

微信小程序会议OA首页-开发说明创建项目关于flex布局关于尺寸单位(rpx)关于WXS轮播图会议信息

目录 1. 创建项目 2. 关于flex布局 3. 关于尺寸单位&#xff08;rpx&#xff09; 4. 关于WXS 4. 轮播图 5. 会议信息 1. 创建项目 基于微信原生开发工具&#xff0c;稳定版 Stable Build (1.06.22010310) 创建项目前&#xff0c;请确定有小程序测试账号 使用向导创建一个…

北京君正客户应用案例:掌静脉3D人脸猫眼视屏智能锁

凯迪仕在今年4月发布了智能锁旗舰新品K70 Pro Max掌静脉3D人脸猫眼视屏智能锁&#xff0c;随即这款新品也成了行业热议的焦点。凯迪仕每次新品都力求突破精益求精&#xff0c;不仅追求科技感、高级感与品质感&#xff0c;而且赋予科技温度&#xff0c;带来人文化的关怀。K70 Pr…

2023.11.19 hadoop之MapReduce

目录 1.简介 2.分布式计算框架-Map Reduce 3.mapreduce的步骤 4.MapReduce底层原理 map阶段 shuffle阶段 reduce阶段 1.简介 Mapreduce是一个分布式运算程序的编程框架&#xff0c;是用户开发“基于hadoop的数据分析应用”的核心框架&#xff1b; Mapreduce核心功能是…

【SpringBoot3+Vue3】二【实战篇】-后端

目录 一、环境搭建 1、数据库脚本 2、pom 3、yml 4、通过mybatis-X生成实体pojo等 4.1 Article 4.2 Category 4.3 User 5、 Mapper 5.1 ArticleMapper 5.2 CategoryMapper 5.3 UserMapper 6、service 6.1 ArticleService 6.2 CategoryService 6.3 UserService …

workman使用手册1.0

workman官网地址&#xff1a;高性能PHP应用容器 workerman 1&#xff1a;把workman项目放到linux服务器后&#xff0c;需要启动你的php文件&#xff0c;才可以使用 定位到项目根目录&#xff1a;例&#xff1a;cd /mnt/workman 启动代码&#xff1a;php outin.php start -d 停…

关于DBMS_STATS.GATHER_DATABASE_STATS_JOB_PROC的一些发现

任务在哪 这个是11g以后的自动收集统计信息的后台任务&#xff0c;10g之前是在dba_scheduler_jobs里查看 SQL> SELECT CLIENT_NAME ,STATUS ,MEAN_INCOMING_TASKS_7_DAYS,MEAN_INCOMING_TASKS_30_DAYS FROM DBA_AUTOTASK_CLIENT WHERE…

计算机视觉:使用opencv实现车牌识别

1 引言 汽车车牌识别&#xff08;License Plate Recognition&#xff09;是一个日常生活中的普遍应用&#xff0c;特别是在智能交通系统中&#xff0c;汽车牌照识别发挥了巨大的作用。汽车牌照的自动识别技术是把处理图像的方法与计算机的软件技术相连接在一起&#xff0c;以准…

Flutter 中在单个屏幕上实现多个列表

今天&#xff0c;我将提供一个实际的示例&#xff0c;演示如何在单个页面上实现多个列表&#xff0c;这些列表可以水平排列、网格格式、垂直排列&#xff0c;甚至是这些常用布局的组合。 下面是要做的&#xff1a; 实现 让我们从创建一个包含产品所有属性的产品模型开始。 …

ZJU Beamer学习手册(二)

ZJU Beamer学习手册基于 Overleaf 的 ZJU Beamer模板 进行解读&#xff0c;本文则基于该模版进行进一步修改。 参考文献 首先在frame文件夹中增加reference.tex文件&#xff0c;文件内容如下。这段代码对参考文献的引用进行了预处理。 \usepackage[backendbiber]{biblatex} \…