muduo网络库剖析——套接字Socket类

muduo网络库剖析——套接字Socket类

  • 前情
    • 从muduo到my_muduo
  • 概要
    • socket网络编程
    • socket编程接口介绍
      • 头文件
      • socket
      • bind
      • listen
      • accept
      • accept4
      • connect
  • 框架与细节
    • 成员
    • 函数
    • 使用方法
  • 源码
  • 结尾

前情

从muduo到my_muduo

作为一个宏大的、功能健全的muduo库,考虑的肯定是众多情况是否可以高效满足;而作为学习者,我们需要抽取其中的精华进行简要实现,这要求我们足够了解muduo库。

做项目 = 模仿 + 修改,不要担心自己学了也不会写怎么办,重要的是积累,学到了这些方法,如果下次在遇到通用需求的时候你能够回想起之前的解决方法就够了。送上一段话!

在这里插入图片描述

概要

socket网络编程

套接字(Socket)是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。它是应用程序通过网络协议进行通信的接口,是应用程序与网络协议根进行交互的接口。简单来说,套接字是不同主机间的进程进行双间通信的端点,它构成了单个主机内及整个网络间的编程界面。

socket编程接口介绍

转自比特冬哥。

头文件

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

socket

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

socket()函数类似于 open()函数,它用于创建一个网络通信端点(打开一个网络通信),如果成功则返回一个网络文件描述符,通常把这个文件描述符称为 socket 描述符(socket descriptor),这个 socket 描述符跟文件描述符一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。
该函数包括 3 个参数,如下所示:
domain
参数 domain 用于指定一个通信域;这将选择将用于通信的协议族。可选的协议族如下表所示:
在这里插入图片描述
对于 TCP/IP 协议来说,通常选择 AF_INET 就可以了,当然如果你的 IP 协议的版本支持 IPv6,那么可以选择 AF_INET6。

type
参数 type 指定套接字的类型,当前支持的类型有:
在这里插入图片描述
protocol
参数 protocol 通常设置为 0,表示为给定的通信域和套接字类型选择默认协议。当对同一域和套接字类型支持多个协议时,可以使用 protocol 参数选择一个特定协议。在 AF_INET 通信域中,套接字类型为SOCK_STREAM 的默认协议是传输控制协议(Transmission Control Protocol,TCP 协议)。

在 AF_INET 通信域中,套接字类型为 SOCK_DGRAM 的默认协议时 UDP。
调用 socket()与调用 open()函数很类似,调用成功情况下,均会返回用于文件 I/O 的文件描述符,只不过对于 socket()来说,其返回的文件描述符一般称为 socket 描述符。当不再需要该文件描述符时,可调用close()函数来关闭套接字,释放相应的资源。

如果 socket()函数调用失败,则会返回-1,并且会设置 errno 变量以指示错误类型。

bind

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

bind()函数用于将一个 IP 地址或端口号与一个套接字进行绑定(将套接字与地址进行关联)。将一个客户端的套接字关联上一个地址没有多少新意,可以让系统选一个默认的地址。一般来讲,会将一个服务器的套接字绑定到一个众所周知的地址—即一个固定的与服务器进行通信的客户端应用程序提前就知道的地址(注意这里说的地址包括 IP 地址和端口号)。因为对于客户端来说,它与服务器进行通信,首先需要知道服务器的 IP 地址以及对应的端口号,所以通常服务器的 IP 地址以及端口号都是众所周知的。

调用 bind()函数将参数 sockfd 指定的套接字与一个地址 addr 进行绑定,成功返回 0,失败情况下返回-1,并设置 errno 以提示错误原因。

关于sockaddr的讲解,请看我InetAddress那篇文章。

listen

listen()函数只能在服务器进程中使用,让服务器进程进入监听状态,等待客户端的连接请求,listen()函数在一般在 bind()函数之后调用,在 accept()函数之前调用,它的函数原型是:

int listen(int sockfd, int backlog);

无法在一个已经连接的套接字(即已经成功执行 connect()的套接字或由 accept()调用返回的套接字)上执行 listen()。

参数 backlog 用来描述 sockfd 的等待连接队列能够达到的最大值。在服务器进程正处理客户端连接请求的时候,可能还存在其它的客户端请求建立连接,因为 TCP 连接是一个过程,由于同时尝试连接的用户过多,使得服务器进程无法快速地完成所有的连接请求,那怎么办呢?直接丢掉其他客户端的连接肯定不是一个很好的解决方法。因此内核会在自己的进程空间里维护一个队列,这些连接请求就会被放入一个队列中,服务器进程会按照先来后到的顺序去处理这些连接请求,这样的一个队列内核不可能让其任意大,所以必须有一个大小的上限,这个 backlog 参数告诉内核使用这个数值作为队列的上限。而当一个客户端的连接请求到达并且该队列为满时,客户端可能会收到一个表示连接失败的错误,本次请求会被丢弃不作处理。

accept

服务器调用 listen()函数之后,就会进入到监听状态,等待客户端的连接请求,使用 accept()函数获取客户端的连接请求并建立连接。函数原型如下所示:

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

为了能够正常让客户端能正常连接到服务器,服务器必须遵循以下处理流程:
① 调用 socket()函数打开套接字;
② 调用 bind()函数将套接字与一个端口号以及 IP 地址进行绑定;
③ 调用 listen()函数让服务器进程进入监听状态,监听客户端的连接请求;
④ 调用 accept()函数处理到来的连接请求。

accept()函数通常只用于服务器应用程序中,如果调用 accept()函数时,并没有客户端请求连接(等待连接队列中也没有等待连接的请求),此时 accept()会进入阻塞状态,直到有客户端连接请求到达为止。当有客户端连接请求到达时,accept()函数与远程客户端之间建立连接,accept()函数返回一个新的套接字。这个套接字与 socket()函数返回的套接字并不同,socket()函数返回的是服务器的套接字(以服务器为例),而accept()函数返回的套接字连接到调用 connect()的客户端,服务器通过该套接字与客户端进行数据交互,譬如向客户端发送数据、或从客户端接收数据。

所以,理解 accept()函数的关键点在于它会创建一个新的套接字,其实这个新的套接字就是与执行
connect()(客户端调用 connect()向服务器发起连接请求)的客户端之间建立了连接,这个套接字代表了服务器与客户端的一个连接。如果 accept()函数执行出错,将会返回-1,并会设置 errno 以指示错误原因。

参数 addr 是一个传出参数,参数 addr 用来返回已连接的客户端的 IP 地址与端口号等这些信息。
参数addrlen 应设置为 addr 所指向的对象的字节长度,如果我们对客户端的 IP 地址与端口号这些信息不感兴趣,可以把 arrd 和 addrlen 均置为空指针 NULL。

accept4

这个函数相比于accept多了一些接受的选项,如SOCK_NONBLOCK和SOCK_CLOEXEC.

SOCK_NONBLOCK是一个套接字选项,用于设置或查询套接字的阻塞模式。在阻塞模式下,套接字会等待操作完成或出现错误才会返回,而在非阻塞模式下,套接字会立即返回,不会等待操作完成或出现错误。等价于O_NONBLOCK。

SOCK_CLOEXEC是一个套接字选项,用于设置或查询套接字的close-on-exec标志位。当进程执行新程序时,如果套接字的close-on-exec标志位被设置,则该套接字将被自动关闭。设置close-on-exec标志位的好处是,当进程执行新程序时,可以避免套接字被继承到新程序中,从而避免了潜在的安全问题。

connect

connect()函数原型如下所示:

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

该函数用于客户端应用程序中,客户端调用 connect()函数将套接字 sockfd 与远程服务器进行连接,参数 addr 指定了待连接的服务器的 IP 地址以及端口号等信息,参数 addrlen 指定了 addr 指向的 struct sockaddr对象的字节大小。

客户端通过 connect()函数请求与服务器建立连接,对于 TCP 连接来说,调用该函数将发生 TCP 连接的握手过程,并最终建立一个 TCP 连接,而对于 UDP 协议来说,调用这个函数只是在 sockfd 中记录服务器IP 地址与端口号,而不发送任何数据。

函数调用成功则返回 0,失败返回-1,并设置 errno 以指示错误原因。

框架与细节

成员

在这里插入图片描述
套接字成员。

函数

TCP_NODELAY

setsockopt(sockfd_, IPPROTO_IP, TCP_NODELAY, &flag, sizeof flag);

这段代码是C或C++语言中用于设置套接字选项的函数调用。具体来说,它设置了一个TCP套接字的Nagle算法的禁用选项。

  • setsockopt() 是一个用于设置套接字选项的函数。
  • sockfd_ 是要设置选项的套接字的文件描述符。
  • IPPROTO_IP 是协议级别,表示我们正在设置IP层的选项。
  • TCP_NODELAY 是一个选项,用于禁用Nagle算法。Nagle算法是一种拥塞控制机制,它用于减少网络上的小包数量,以提高数据传输的效率。然而,在某些应用场景中,我们可能需要更快的数据传输速度,此时可以禁用Nagle算法。
  • &flag 是指向一个整数的指针,该整数表示是否禁用Nagle算法(flag 为非零值表示禁用,为零表示启用)。
  • sizeof flag 是选项的长度。

综合来看,这段代码的作用是禁用套接字sockfd_上的Nagle算法,以加快数据传输速度。这在实时应用或需要低延迟的应用中可能是有用的。

SO_REUSEADDR

setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof flag);

这段代码是C或C++语言中用于设置套接字选项的函数调用。具体来说,它设置了一个套接字的地址复用选项。

setsockopt() 是一个用于设置套接字选项的函数。
sockfd_ 是要设置选项的套接字的文件描述符。
SOL_SOCKET 是协议级别,表示我们正在设置套接字级的选项。
SO_REUSEADDR 是一个选项,用于允许套接字在关闭后立即重新使用其地址。这在进行服务器编程时特别有用,因为在服务器重启或关闭后,它通常需要立即重新绑定到相同的地址和端口。
&flag 是指向一个整数的指针,该整数表示是否启用地址复用(flag 为非零值表示启用,为零表示禁用)。
sizeof flag 是选项的长度。
综合来看,这段代码的作用是启用套接字sockfd_上的地址复用功能,以便在关闭后立即重新使用相同的地址。这在服务器编程中是常见的做法,以避免由于地址绑定延迟而导致的延迟或问题。

SO_REUSEPORT

setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT, &flag, sizeof flag);

这段代码是C或C++语言中用于设置套接字选项的函数调用。具体来说,它设置了一个套接字的端口复用选项。

setsockopt() 是一个用于设置套接字选项的函数。
sockfd_ 是要设置选项的套接字的文件描述符。
SOL_SOCKET 是协议级别,表示我们正在设置套接字级的选项。
SO_REUSEPORT 是一个选项,用于允许多个套接字绑定到同一个端口上。这在进行服务器编程时特别有用,特别是在使用如Nginx这样的高性能服务器时,它可以允许多个工作进程绑定到同一个端口上,从而实现负载均衡和容错。
&flag 是指向一个整数的指针,该整数表示是否启用端口复用(flag 为非零值表示启用,为零表示禁用)。
sizeof flag 是选项的长度。
综合来看,这段代码的作用是启用套接字sockfd_上的端口复用功能,以便允许多个套接字绑定到同一个端口上。这在实现高性能服务器和负载均衡时是常见的做法。

SO_KEEPALIVE

setsockopt(sockfd_, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof flag);

这行代码具体设置了一个套接字的 “Keep-Alive” 选项。让我们逐一解析这行代码的各个部分:

sockfd_:
这是要设置选项的套接字的文件描述符。
SOL_SOCKET:
这是协议级别,表示我们正在设置的是套接字级的选项,而不是某个特定于协议的选项。
SO_KEEPALIVE:
这是一个套接字选项,用于启用或禁用 “Keep-Alive” 功能。启用这个功能后,如果套接字在一段时间内没有活动,操作系统会发送一个数据包来检查连接是否仍然有效。这对于检测网络故障或对端系统故障非常有用。
&flag:
这是一个指向整数的指针,表示是否启用 “Keep-Alive” 功能。如果 flag 为非零值,则启用该功能;如果为零,则禁用。
sizeof flag:
这表示选项的长度,这里是 flag 变量的大小。
总结:这行代码用于设置 sockfd_ 套接字的 “Keep-Alive” 功能。如果 flag 非零,则启用该功能;如果为零,则禁用。启用 “Keep-Alive” 可以帮助检测和预防网络连接问题。

使用方法

源码

//Socket.h
#pragma once 

#include <sys/socket.h>
#include <netinet/tcp.h>

#include "Log.h"
#include "noncopyable.h"
#include "InetAddress.h"

class Socket : noncopyable {
public:
    explicit Socket(int sockfd) : sockfd_(sockfd) {} 
    ~Socket();

    int fd() const { return sockfd_; }
    void listen();
    void bind(const InetAddress& localAddr);
    int accept(InetAddress& peerAddr);

    void setTcpNoDelay(bool on);
    void setReuseAddr(bool on);
    void setReusePort(bool on);
    void setKeepAlive(bool on);
private:
    const int sockfd_;
};
//Socket.cc
#include "Socket.h"

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

void Socket::listen() {
    if(::listen(sockfd_, 1024) == -1) {
        LOG_FATAL("%s--%s--%d--%d : listen error\n", __FILE__, __FUNCTION__, __LINE__, errno);
    }
}

void Socket::bind(const InetAddress& localAddr) {
    if (::bind(sockfd_, (sockaddr*)localAddr.getSockAddr(), sizeof(sockaddr)) != 0) {
        LOG_FATAL("%s--%s--%d--%d : bind error\n", __FILE__, __FUNCTION__, __LINE__, errno);
    }
}

int Socket::accept(InetAddress& peerAddr) {
    sockaddr_in* sa;
    bzero(sa, sizeof sa);
    socklen_t st = sizeof(sa);
    int connfd = ::accept4(sockfd_, (sockaddr*)sa, &st, SOCK_NONBLOCK | SOCK_CLOEXEC);
    if (connfd >= 0) {
        peerAddr.setSockAddr(*sa);
    }
    return connfd;
}

void Socket::setTcpNoDelay(bool on) {
    int flag = on ? 1 : 0;
    ::setsockopt(sockfd_, IPPROTO_IP, TCP_NODELAY, &flag, sizeof flag);
}

void Socket::setReuseAddr(bool on) {
    int flag = on ? 1 : 0;
    ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof flag);
}

void Socket::setReusePort(bool on) {
    int flag = on ? 1 : 0;
    ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT, &flag, sizeof flag);
}

void Socket::setKeepAlive(bool on) {
    int flag = on ? 1 : 0;
    ::setsockopt(sockfd_, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof flag);
}

结尾

以上就是套接字Socket类的相关介绍,以及我在进行项目重写的时候遇到的一些问题,和我自己的一些心得体会。发现写博客真的会记录好多你的成长,而且对于一个好的项目,写博客也是证明你确实有过深度思考,并且在之后面试或者工作时遇到同样的问题能够进行复盘的一种有效的手段。所以,希望uu们也可以像我一样,养成写博客的习惯,逐渐脱离菜鸡队列,向大佬前进!!!加油!!!

也希望我能够完成muduo网络库项目的深度学习与重写,并在功能上能够拓展。也希望在完成这个博客系列之后,能够引导想要学习muduo网络库源码的人,更好地探索这篇美丽繁华的土壤。致敬chenshuo大神!!!

鉴于博主只是一名平平无奇的大三学生,没什么项目经验,所以可能很多东西有所疏漏,如果有大神发现了,还劳烦您在评论区留言,我会努力尝试解决问题!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java重修第七天—内部类

通过学习本篇文章可以掌握如下知识 成员内部类 静态内部类 局部内部类 匿名内部类(重点掌握) 内部类 是类中的五大成分之一(成员变量、方法、构造器、内部类、代码块)&#xff0c;其定义为&#xff1a;如果一个类定义在另一个类的内部&#xff0c;这个类就是内部类。 场景…

SqlAlchemy使用教程(四) MetaData 与 SQL Express Language 的使用

四、Database MetaData 与 SQL Express Language 的使用 MetaData对象用于描述表结构&#xff0c;SQL Express Language是DBAPI SQL的统一封装器。MetaData 与SQL Express 语句可以在Core层使用&#xff0c;ORM层基于MetaData, SQL Express基础上做了进一步抽象。本章将介绍在…

如何使用Docker本地搭建Traefik服务并实现公网访问管理界面

文章目录 前言1. Docker 部署 Trfɪk2. 本地访问traefik测试3. Linux 安装cpolar4. 配置Traefik公网访问地址5. 公网远程访问Traefik6. 固定Traefik公网地址 前言 Trfɪk 是一个云原生的新型的 HTTP 反向代理、负载均衡软件&#xff0c;能轻易的部署微服务。它支持多种后端 (D…

电动工具直流调速专用集成电路GS069,具有电源电压范围宽、功耗小、抗干扰能力强等特性

GS069电动工具直流调速电路是CMOS专用集成电路&#xff0c;具有电源电压范 围宽、功耗小、抗干扰能力强等特点。通过外接电阻网络&#xff0c;改变与之相接 的VMOS 管的输出&#xff0c;达到控制电动工具转速的作用。该电路输出幅值宽&#xff0c; 频率变化小&#xff0c;占空比…

pytorch10:正则化(weight_decay、dropout、Batch Normalization)

目录 一、正则化regularization1.概念2.过拟合3.减小方差策略4 正则化--权值衰减 二、正则化-dropout2.1 dropout概念2.2 数据尺度变化2.3 nn.Dropout2.4 两种模式 三、Batch Normalization3.1 ICS现象&#xff08;Internal Covariate Shift&#xff0c;内部协变量偏移)3.2 BN原…

本地搭建Oracle数据库结合内网穿透实现公网环境远程访问

最近&#xff0c;我发现了一个超级强大的人工智能学习网站。它以通俗易懂的方式呈现复杂的概念&#xff0c;而且内容风趣幽默。我觉得它对大家可能会有所帮助&#xff0c;所以我在此分享。点击这里跳转到网站。 文章目录 前言1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2…

解决:ModuleNotFoundError: No module named ‘pymysql’

解决&#xff1a;ModuleNotFoundError: No module named ‘pymysql’ 文章目录 解决&#xff1a;ModuleNotFoundError: No module named pymysql背景报错问题报错翻译报错位置代码报错原因解决方法方法一&#xff0c;直接安装方法二&#xff0c;手动下载安装方法三&#xff0c;…

国图公考:2024山东省事业单位发布招聘公告

更多信息可以登录山东人事考试信息查看&#xff01;

134基于matlab的时间序列预测

基于matlab的时间序列预测&#xff0c;包括最小二乘支持向量机和粒子群优化支持向量机及改进的粒子群优化支持向量机。输出测试结果&#xff0c;具有GUI可视化界面。程序已调通&#xff0c;可直接运行。 134matlab时间序列预测粒子群优化 (xiaohongshu.com)

Transformer原理与代码实现

Transformer原理与代码实现 概览 一、嵌入层 Embedding 二、位置编码 Positional Encoding 三、&#xff08;整合&#xff09;Transformer嵌入层 Transformer Embedding 四、带缩放的点积注意力机制 Scaled Dot-Product Attention 五、多头注意力 Multi-Head Attention 六…

基于ssm的社区流浪动物救助领养系统的设计与开发+vue论文

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;流浪动物信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足…

JavaScript SEO:如何为搜索引擎优化 JS

什么是 JavaScript SEO&#xff1f; JavaScript SEO 是技术 SEO 的一部分&#xff0c;其重点是使使用 JavaScript 构建的网站更容易被搜索引擎抓取、呈现和索引。 常见任务包括以下内容&#xff1a; 优化通过 JavaScript 注入的内容正确实施懒加载遵循内部链接最佳实践预防、…

国图公考:2024年度国考笔试成绩、合格分数线已出

中央机关及其直属机构2024年度考试录用公务员笔试成绩和合格分数线公布&#xff0c;考生可于即日起登录“中央机关及其直属机构2024年度考试录用公务员专题网站”查询。 按照笔试成绩从高到低的顺序&#xff0c;进面人员名单也已经公布。 如果没有进面也可以报考调剂&#xf…

探索自动化测试断言:提升测试效率与质量的关键!

前言 断言在自动化测试中起着关键的作用&#xff0c;它是验证测试结果是否符合预期的重要手段。如果在自动化测试过程中忽视了断言&#xff0c;那么这个测试就失去了其本质的意义&#xff0c;因为我们无法得知测试结果是否达到了预期的效果。因此&#xff0c;断言在自动化测试…

完美解决idea一直indexing,无法操作的问题

今天主要分享一下在使用idea 2020.3版本开发maven项目的时候&#xff0c;一直出现有效件index&#xff0c; 有时候是scaning indexing, 有时候是update indexing, indexing的时候&#xff0c;idea基本上就没办法操作了&#xff0c;连跳入到类或方法里都跳不了。不厌其烦。 于是…

导出功能开发

1.导出功能&#xff08;仅表头&#xff09; package nc.ui.yhlypx.yhlypxfkd.ace.action;import java.awt.event.ActionEvent; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.List;import nc.ui.pubapp.uif2app.model.BillManageModel; imp…

keycloak部署

https://downloads.jboss.org/keycloak/11.0.2/keycloak-11.0.2.zip 1.上传zip 并解压 uzip keycloak-11.0.2.zip 2.创建mysql数据库 CREATE SCHEMA keycloak DEFAULT CHARACTER SET utf8 ; 3.安装mysql的jdbc驱动 下载mysql的JDBC驱动&#xff08;mysql-connector-java-8…

代码随想录 Leetcode15. 三数之和

题目&#xff1a; 代码(首刷看解析 2024年1月15日&#xff09;&#xff1a; class Solution { public:vector<vector<int>> threeSum(vector<int>& nums) {vector<vector<int>> res;int n nums.size();sort(nums.begin(),nums.end());for(…

如何在 Ubuntu 22.04 上使用 LEMP 安装 WordPress

前些天发现了一个人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;最重要的屌图甚多&#xff0c;忍不住分享一下给大家。点击跳转到网站。 如何在 Ubuntu 22.04 上使用 LEMP 安装 WordPress 介绍 WordPress 是互联网上最流行的内容管理系统 (CMS) 之一…

清华裴丹|大模型时代的AIOps

在大模型时代下&#xff0c;运维界普遍关注一些问题&#xff1a;大模型能带来哪些收益&#xff1f;面临哪些技术挑战&#xff1f;与以往的 AIOps小模型是什么关系&#xff1f;有了大模型之后&#xff0c;那么AIOps的整体框架是什么&#xff1f;近期、中期、长期有哪些应用&…