当前位置: 首页 > news >正文

muduo源码剖析--Inetaddress/Socket/Acceptor

InetAddress类

实际上封装了传入socket地址类,包括ip、端口以及具体的协议簇

// 封装socket地址类型
class InetAddress
{
public:explicit InetAddress(uint16_t port = 0, std::string ip = "127.0.0.1");explicit InetAddress(const sockaddr_in &addr): addr_(addr){}std::string toIp() const;std::string toIpPort() const;uint16_t toPort() const;const sockaddr_in *getSockAddr() const { return &addr_; }void setSockAddr(const sockaddr_in &addr) { addr_ = addr; }private:sockaddr_in addr_;  //构造socket的地址类
};

就是封装,没什么好说的,直接上cpp实现:

InetAddress::InetAddress(uint16_t port, std::string ip)
{::memset(&addr_, 0, sizeof(addr_));addr_.sin_family = AF_INET; //IPV4addr_.sin_port = ::htons(port); // 本地字节序转为网络字节序addr_.sin_addr.s_addr = ::inet_addr(ip.c_str());
}std::string InetAddress::toIp() const
{// addr_char buf[64] = {0};::inet_ntop(AF_INET, &addr_.sin_addr, buf, sizeof buf);return buf;
}std::string InetAddress::toIpPort() const
{// ip:portchar buf[64] = {0};::inet_ntop(AF_INET, &addr_.sin_addr, buf, sizeof buf);size_t end = ::strlen(buf);uint16_t port = ::ntohs(addr_.sin_port);sprintf(buf+end, ":%u", port);return buf;
}uint16_t InetAddress::toPort() const
{return ::ntohs(addr_.sin_port);
}

Socket类

实际封装socketfd的类,socket地址采取上述封装的InetAddress类, 对bind、listen、accept函数进行了封装

// 封装socket fd
class Socket : noncopyable
{
public:explicit Socket(int sockfd): sockfd_(sockfd){}~Socket();int fd() const { return sockfd_; }void bindAddress(const InetAddress &localaddr);void listen();int accept(InetAddress *peeraddr);void shutdownWrite();//设置socket的相关属性void setTcpNoDelay(bool on);void setReuseAddr(bool on);void setReusePort(bool on);void setKeepAlive(bool on);private:const int sockfd_;
};

bind、listen、accept函数实现

void Socket::bindAddress(const InetAddress &localaddr)
{if (0 != ::bind(sockfd_, (sockaddr *)localaddr.getSockAddr(), sizeof(sockaddr_in))){LOG_FATAL("bind sockfd:%d fail\n", sockfd_);}
}void Socket::listen()
{if (0 != ::listen(sockfd_, 1024)){LOG_FATAL("listen sockfd:%d fail\n", sockfd_);}
}int Socket::accept(InetAddress *peeraddr)
{/*** 1. accept函数的参数不合法* 2. 对返回的connfd没有设置非阻塞* Reactor模型 one loop per thread* poller + non-blocking IO**/sockaddr_in addr;socklen_t len = sizeof(addr);::memset(&addr, 0, sizeof(addr));// fixed : int connfd = ::accept(sockfd_, (sockaddr *)&addr, &len);int connfd = ::accept4(sockfd_, (sockaddr *)&addr, &len, SOCK_NONBLOCK | SOCK_CLOEXEC); //接收连接if (connfd >= 0){peeraddr->setSockAddr(addr);  //传出参数}return connfd;  //返回接收的fd
} 

设置socket相关属性的实现:

void Socket::setTcpNoDelay(bool on)//延迟发送
{int optval = on ? 1 : 0;::setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)); // TCP_NODELAY包含头文件 <netinet/tcp.h>
}
void Socket::setReuseAddr(bool on) //ip复用
{int optval = on ? 1 : 0;::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); // TCP_NODELAY包含头文件 <netinet/tcp.h>
}
void Socket::setReusePort(bool on)//端口复用
{int optval = on ? 1 : 0;::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); // TCP_NODELAY包含头文件 <netinet/tcp.h>
}
void Socket::setKeepAlive(bool on)//连接保活
{int optval = on ? 1 : 0;::setsockopt(sockfd_, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)); // TCP_NODELAY包含头文件 <netinet/tcp.h>
}

半关闭shutdownWrite()

void Socket::shutdownWrite()
{if (::shutdown(sockfd_, SHUT_WR) < 0){LOG_ERROR("shutdownWrite error");}
}

释放,也即关闭所管理的socketfd

Socket::~Socket()
{::close(sockfd_);
}

Acceptor类

封装了服务端端接收新连接请求的套接字(acceptFd),复用InetAddress、Socket类

class Acceptor : noncopyable
{
public:using NewConnectionCallback = std::function<void(int sockfd, const InetAddress &)>; //新连接到来的回调Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport);~Acceptor();void setNewConnectionCallback(const NewConnectionCallback &cb) { NewConnectionCallback_ = cb; }bool listenning() const { return listenning_; }void listen();private:void handleRead();EventLoop *loop_; // Acceptor用的就是用户定义的那个baseLoop 也称作mainLoopSocket acceptSocket_; //接收新连接请求的fdChannel acceptChannel_; //acceptfd所绑定的channelNewConnectionCallback NewConnectionCallback_;bool listenning_;
};

构造函数实现:主要初始化acceptfd,以及设置相应的回调


static int createNonblocking()  //创建非阻塞的套接字
{int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);if (sockfd < 0){LOG_FATAL("%s:%s:%d listen socket create err:%d\n", __FILE__, __FUNCTION__, __LINE__, errno);}return sockfd;
}Acceptor::Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport): loop_(loop), acceptSocket_(createNonblocking()), acceptChannel_(loop, acceptSocket_.fd()) //绑定channel,loop为baseLoop, listenning_(false)
{acceptSocket_.setReuseAddr(true);acceptSocket_.setReusePort(true);acceptSocket_.bindAddress(listenAddr);// TcpServer::start() => Acceptor.listen() 如果有新用户连接 要执行一个回调(accept => connfd => 打包成Channel => 唤醒subloop)// baseloop监听到有事件发生 => acceptChannel_(listenfd) => 执行该回调函数acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this));
}

监听listen()->Socket::listen()

void Acceptor::listen()
{listenning_ = true;acceptSocket_.listen();         // listenacceptChannel_.enableReading(); // acceptChannel_注册至Poller !重要
}

执行新连接请求,并执行相应的回调

// listenfd有事件发生了,就是有新用户连接了
void Acceptor::handleRead()
{InetAddress peerAddr;int connfd = acceptSocket_.accept(&peerAddr);if (connfd >= 0){if (NewConnectionCallback_){NewConnectionCallback_(connfd, peerAddr); // 轮询找到subLoop 唤醒并分发当前的新客户端的Channel}else{::close(connfd);}}else{LOG_ERROR("%s:%s:%d accept err:%d\n", __FILE__, __FUNCTION__, __LINE__, errno);if (errno == EMFILE){LOG_ERROR("%s:%s:%d sockfd reached limit\n", __FILE__, __FUNCTION__, __LINE__);/*原实现是,先创建一个ldfd占住一个位置(open("dev\null"))如果文件描述符打开达到上限,那么先关闭当前的ldfd,利用空出来的这个ldfd去接收新连接,接收完之后在关闭这个连接,然后再(open("dev\null"))将ldfd占住这个位置,达到一个比较优雅的处理方式。*/}}
}

释放操作:

Acceptor::~Acceptor()
{acceptChannel_.disableAll();    // 把从Poller中感兴趣的事件删除掉acceptChannel_.remove();        // 调用EventLoop->removeChannel => Poller->removeChannel 把Poller的ChannelMap对应的部分删除
}

相关文章:

muduo源码剖析--Inetaddress/Socket/Acceptor

InetAddress类 实际上封装了传入socket地址类&#xff0c;包括ip、端口以及具体的协议簇 // 封装socket地址类型 class InetAddress { public:explicit InetAddress(uint16_t port 0, std::string ip "127.0.0.1");explicit InetAddress(const sockaddr_in &…...

域名过户操作流程及常见问题

模板添加及模板过户操作流程&#xff1a; 一、添加模板操作流程&#xff1a; 1.在业务管理-域名管理-模板管理中找到“添加模板” 2.选择所有者类型&#xff08;个人或是企业/组织&#xff09;&#xff0c;填写新的域名所有者资料&#xff0c;填写无误后点击“确定”。 目前…...

多国拟发ChatGPT禁令 关“野兽”的笼子要来了?

“人工智能想越狱“、”AI产生自我意识”、“AI终将杀死人类”、“硅基生命的进化”.......曾经只在在赛博朋克等科技幻想中出现的剧情&#xff0c;在今年走向现实&#xff0c;生成式自然语言模型正在遭受前所未有的质疑。 聚光灯下最瞩目的那个是ChatGPT&#xff0c;3月底到4…...

深度学习中,Params参数量和FLOPs计算量分别指什么

在深度学习中&#xff0c;参数量和计算量是两个重要的概念。 参数量&#xff1a; 参数量指的是深度神经网络中需要学习的参数数量。在深度学习中&#xff0c;每个神经元都有一个权重&#xff0c;这些权重是需要通过训练来确定的。深度神经网络中的参数量是指所有权重的数量之…...

1分钟快速制作思维导图「ChatGPT+XMind」—— 跟上时代的脚步,这辈子就起飞了 - 第5篇

历史文章&#xff08;文章累计460&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 S…...

生成与获取token

public class JwtUtils {// TOKEN的有效期1小时&#xff08;S&#xff09;private static final int TOKEN_TIME_OUT 1 * 3600;// 加密KEYprivate static final String TOKEN_SECRET "itcast";// 生成Tokenpublic static String getToken(Map params){long current…...

【人工智能】ChatGTP从入门到精通

当谈论自然语言处理和文本生成技术时&#xff0c;Chat GPT 是一个备受瞩目的话题。作为一种基于深度学习的语言模型&#xff0c;Chat GPT 在近几年里已经展现出了惊人的能力&#xff0c;可以生成几乎无法区分与人类写作的文本&#xff0c;并在自然语言处理领域的各种任务中都表…...

电脑桌面图标间距突然变大怎么恢复

1. WindowsR打开 > 输入regedit 按住WindowsR打开运行&#xff0c;输入regedit并点击确定。 2. 双击Control Panel 双击展开HKEY_CURRENT_USER&#xff0c;双击展开Control Panel&#xff0c;双击展开Desktop。 3. 更改间距 点击打开WindowMetrics&#xff0c; 双击打开…...

详解各版本Web服务器限制请求体大小的方法

前言 我们在上传大文件的时候通常会把请求体大小限制设置的很大&#xff0c;但平常没有这种业务需求的时候一般就会做出限制&#xff0c;避免异常请求的进入&#xff0c;我们该怎么去设置这个限制呢&#xff1f; 一般来说&#xff0c;Linux服务器没有直接限制请求参数包大小的…...

二叉树_详解

目录 1. 树型结构 1.1 概念 1.2 概念 1.3 树的表示形式 1.4 树的应用 2. 二叉树 2.1 概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 二叉树的存储 2.5 二叉树的基本操作 2.5.1 前置说明 2.5.2 二叉树的遍历 2.5.3 二叉树的基本操作 1. 树型结构 1.1 概念 …...

LOTO示波器电源环路增益分析客户实测

我们在之前有文章介绍过LOTO示波器信号源扫频测电源环路增益稳定性的方法和过程&#xff0c;可以参考演示视频如下&#xff1a; https://www.ixigua.com/7135738415382790663?logTaga843d537a27090d5117b 或者阅读对应的文章&#xff1a;《LOTO示波器 实测 开环增益频响曲线/电…...

Netty主要组件

: 在Netty中有很多重要的组件, 每个组件职业不同, 担负不同的功能。 组件一 NioEventLoop 在它的底层封装了Selector, 实现多路复用, 由唯一绑定的一个线程去进行三大步骤循环操作: 监听事件,处理事件,执行任务。 组件二 NioServerSocketChannel NioSocketChannel 一个是服务…...

Linux系统【centos7】常用基础命令教程

今天我来介绍一下Linux系统的基础知识。 首先&#xff0c;我们需要了解Linux是什么。Linux是一种免费且开放源代码的操作系统&#xff0c;它被广泛用于服务器、移动设备和嵌入式系统。 接下来&#xff0c;我们需要了解基本的Linux命令。其中一些基本命令包括&#xff1a; 1.…...

【Redis学习】Redis入门概述

Redis是什么 Redis:REmote Dictionary Server(远程字典服务器) 官网介绍&#xff1a;The open source, in-memory data store used by millions of developers as a database, cache, streaming engine, and message broker.&#xff08;被数百万开发人员用作数据库、缓存、流…...

nodejs微服务:Consul集群

Consule集群 1 &#xff09;概述 Consul是HashiCorp 公司推出的开源工具&#xff0c;用于实现分布式系统的服务发现与配置Consul是分布式的、高可用的、可横向扩展的, 完成consul的安装后&#xff0c;必须运行agentagent可以运行为 server模式、client模式, 每个数据中心至少…...

spring事务处理

系列文章目录 Spring中事务的处理相关内容的学习 文章目录系列文章目录前言一、Spring事务简介二、案例&#xff1a;银行账户转账1.题目要求和思路分析2.实现步骤3.实现结构三、spring事务角色四、spring事务相关配置五、案例&#xff1a;转账业务追加日志1.题目要求和思路分析…...

2023 年博客之星的入围规则

目的 感谢各位博主和社区的大力支持&#xff0c;我们的博客之星活动成为了 IT界非常知名的博主荣誉的象征&#xff0c;博主在这个过程中也给大家贡献了很多优质内容。 在过去的几年中&#xff0c;博主们给我们博客之星活动提了很多建议&#xff0c;其中最强烈的一点就是&#…...

【新2023Q2押题JAVA】华为OD机试 - 查找树中的元素 or 查找二叉树节点

最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:查找树中的元素 or 查找二叉树…...

MySQL 日志:undo log、redo log、binlog 有什么用?

目录一、bin log1.作用2.刷盘时机3.日志格式二、redo log1.为什么需要redo log2.基本概念3.作用3.刷盘时机三、undo log1.作用四、Mysql的时机顺序五、redo log 与 binlog 的两阶段提交六、总结一、bin log 1.作用 MySQL的bin log日志是用来记录MySQL中增删改时的记录日志。 …...

ETL 与 ELT的关键区别

ETL 和 ELT 之间的主要区别在于数据转换发生的时间和地点 — 这些变化可能看起来很小&#xff0c;但会产生很大的影响&#xff01; ETL 和 ELT 是数据团队引入、转换并最终向利益干系人公开数据的两种主要方式。它们是与现代云数据仓库和 ETL 工具的开发并行发展的流程。 在任…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

Python 实现 Web 静态服务器(HTTP 协议)

目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1&#xff09;下载安装包2&#xff09;配置环境变量3&#xff09;安装镜像4&#xff09;node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1&#xff09;使用 http-server2&#xff09;详解 …...

安卓基础(Java 和 Gradle 版本)

1. 设置项目的 JDK 版本 方法1&#xff1a;通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分&#xff0c;设置 Gradle JDK 方法2&#xff1a;通过 Settings File → Settings... (或 CtrlAltS)…...

xmind转换为markdown

文章目录 解锁思维导图新姿势&#xff1a;将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件&#xff08;ZIP处理&#xff09;2.解析JSON数据结构3&#xff1a;递归转换树形结构4&#xff1a;Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...

算术操作符与类型转换:从基础到精通

目录 前言&#xff1a;从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符&#xff1a;、-、*、/、% 赋值操作符&#xff1a;和复合赋值 单⽬操作符&#xff1a;、--、、- 前言&#xff1a;从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...

python基础语法Ⅰ

python基础语法Ⅰ 常量和表达式变量是什么变量的语法1.定义变量使用变量 变量的类型1.整数2.浮点数(小数)3.字符串4.布尔5.其他 动态类型特征注释注释是什么注释的语法1.行注释2.文档字符串 注释的规范 常量和表达式 我们可以把python当作一个计算器&#xff0c;来进行一些算术…...