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大神!!!

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

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

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

相关文章

Vue3-46-Pinia-获取全局状态变量的方式

使用说明 在 Pinia 中&#xff0c;获取状态变量的方式非常的简单 &#xff1a; 就和使用对象一样。 使用思路 &#xff1a; 1、导入Store&#xff1b;2、声明Store对象&#xff1b;3、使用对象。 在逻辑代码中使用 但是 Option Store 和 Setup Store 两种方式定义的全局状态变量…

推荐一款实用的免费开源局域网传输工具!轻松搭建网盘的神器!

本文小编给大家分享一款免费开源软件 Filebrowser&#xff0c;使用它可以轻松搭建属于你的个人网盘和网页版文件管理器。 它能做什么呢&#xff1f;想必大家也都用过百度网盘或阿里云盘之类的云存储服务&#xff0c;将自己的所有数据都存储在别人的服务器上&#xff0c;所谓的在…

如何实现一个好用的全链路多测试环境(HTTP篇)

这里是weihubeats,觉得文章不错可以关注公众号小奏技术&#xff0c;文章首发。拒绝营销号&#xff0c;拒绝标题党 背景 随着公司的发展&#xff0c;研发的系统和开发人员会变得越来越多。但是测试环境却始终只有一个&#xff0c;所以久而久之&#xff0c;我们就发现研发经常遇…

融云 CEO 董晗荣获 51CTO 「2023 年度科技影响力人物奖」

1 月 5 日&#xff0c;由知名 IT 技术媒体 51CTO 主办的第十八届“中国企业年终评选”正式揭晓榜单&#xff0c;融云 CEO 董晗荣获“2023 年度科技影响力人物奖”。关注【融云全球互联网通信云】了解更多 经过十余年的经验积累&#xff0c;“中国企业 IT 印象”已成为各行业客户…

以太网交换机——稳定安全,构筑数据之桥

交换机&#xff0c;起源于集线器和网桥等网络通信设备&#xff0c;它在性能和功能上有了很大的发展&#xff0c;因此逐渐成为搭建网络环境的常用的设备。 随着ChatGPT爆发&#xff0c;因为用户量激增而宕机事件频频发生&#xff0c;云计算应用催生超大规模算力需求&#xff0c;…

CompletableFuture原理与用法

CompletableFuture 是由Java8引入的&#xff0c;这让我们编写清晰可读的异步代码变得更加容易&#xff0c;该类功能比Future 更加强大。 什么是 CompletableFuture 在Java中CompletableFuture用于异步编程&#xff0c;异步通常意味着非阻塞&#xff0c;运行任务单独的线程&…

Stream流递归查询部门树

Java 递归查询树是很常见的功能&#xff0c;也有很多写法&#xff0c;小编这里记录stream流递归部门树写法&#xff0c;自从小编用上stream流之后&#xff0c;是爱不释手&#xff0c;的确是个不错的好东西&#xff0c;话不多说&#xff0c;直接上代码 第一步&#xff1a;先创建…

云联接:揭开SD-WAN神秘面纱,颠覆你对网络的认知!

云联接&#xff08;Cloud Connect&#xff09;源于软件定义广域网&#xff08;SD-WAN&#xff09;。 软件定义广域网由于技术应用性强&#xff0c;近年来从一个由软件定义网络&#xff08;SDN&#xff09;部分衍生的分支概念发展为大规模普适的实践技术&#xff0c;已成为建立…

拼多多今年的校招薪资。。。

拼多多校招情况分析 关于校招情况分析&#xff0c;我们写过了争议巨巨巨巨大的 华为、互联网宇宙厂 字节跳动 以及如今有点高攀不起的新能源车企 比亚迪。 群里收集过小伙伴的意见&#xff0c;除上述大厂以外&#xff0c;大家最感兴趣的还是市值刚超过阿里的砍厂&#xff1a;拼…

算法训练营Day40

#Java #动态规划 Feeling and experiences&#xff1a; 单词拆分&#xff1a;力扣题目链接 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 注意&#xff1a;不要求字典中出现的单词全部都使用&#xff0c;并且字典…

网页设计与网站建设作业html+css+js,一个简易的游戏官网网页

一个简易的游戏网页 浏览器查看 目录结构 部分代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport&…

NLP论文阅读记录 - 2021 | WOS 使用分层多尺度抽象建模和动态内存进行抽象文本摘要

文章目录 前言0、论文摘要一、Introduction1.3本文贡献 二.前提三.本文方法四 实验效果4.1数据集4.2 对比模型4.3实施细节4.4评估指标4.5 实验结果4.6 细粒度分析 五 总结思考 前言 Abstractive Text Summarization with Hierarchical Multi-scale Abstraction Modeling and Dy…

代码随想录 Leetcode202. 快乐数

题目&#xff1a; 代码(首刷自解 2024年1月15日&#xff09;&#xff1a; class Solution { public:bool isHappy(int n) {unordered_set<int> hash;while(n ! 1) {int sum 0;while(n/10 ! 0) {sum (n % 10)*(n % 10);n/10;}sum n*n;if (hash.find(sum) ! hash.end()…

R语言【paleobioDB】——pbdb_richness():绘制指定类群的数量丰度

Package paleobioDB version 0.7.0 paleobioDB 包在2020年已经停止更新&#xff0c;该包依赖PBDB v1 API。 可以选择在Index of /src/contrib/Archive/paleobioDB (r-project.org)下载安装包后&#xff0c;执行本地安装。 Usage pbdb_richness (data, rank, res, temporal_ex…

写点东西《Docker入门(上)》

写点东西《Docker入门&#xff08;上&#xff09;》 环境变量 Docker 镜像 Docker CMD 与 ENTRYPOINT 有什么区别 Docker 中的网络&#xff1a; Docker 存储&#xff1a; Docker 是一个工具&#xff0c;允许开发人员将他们的应用程序及其所有依赖项打包到一个容器中。然后&…

Python图像处理实战:使用PIL库批量添加水印的完整指南【第27篇—python:Seaborn】

文章目录 1. 简介2. PIL库概述3. PIL库中涉及的类4. 实现原理5. 实现过程5.1 原始图片5.2 导入相关模块5.3 初始化数据5.4 水印字体设置5.5 打开原始图片并创建存储对象5.6 计算图片和水印的大小5.7 选择性设置水印文字5.8 绘制文字并设置透明度5.9 遍历获取图片文件并调用绘制…

解决PS“暂存盘已满”错误

问题&#xff1a;PS“暂存盘已满”错误 原因&#xff1a; PS在运行时会将文件的相关数据参数保存到暂存区。当提醒暂存盘满时&#xff0c;说明你当前PS运行的使用盘符空间不足&#xff0c;所以在运行时一定要保留有足够的盘符空间来运行PS。 效果图 解决方案 注意: 我们在使用P…

vue 组件 import make sure to provide the “name“ option.

百度了好多结果&#xff0c;都过时了&#xff0c;例如&#xff1a; 模块引入是否加{} 再比如&#xff1a; 对于递归组件&#xff0c;请确保提供“name”选项。 出现该错误情况之一&#xff1a; 错误由未正确引入组件或子组件引起&#xff0c;如element-ui中form表单组件未引…

css 怎么绘制一个带圆角的渐变色的边框

1&#xff0c;可以写两个样式最外面的div设置一个渐变的背景色。里面的元素使用纯色。但是宽高要比外面元素的小。可以利用里面的元素设置padding这样挡住部分渐变色。漏出来的渐变色就像边框一样。 <div class"cover-wrapper"> <div class"item-cover…

AI Agent:大模型的下一个高地

科技云报道原创。 当所有人都沉浸在与ChatGPT对话的乐趣中&#xff0c;一场静水流深的变革已然启动。 2023年11月&#xff0c;比尔盖茨发表了一篇文章&#xff0c;他表示&#xff0c;AI Agent将是大模型之后的下一个平台&#xff0c;不仅改变每个人与计算机互动的方式&#x…