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.mzph.cn/news/624259.shtml

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

相关文章

数字孪生+人工智能突破复杂地形和气候提供可靠的电力

利用 Bentley 应用程序实现数字化交付&#xff0c;大大缩短了项目时间和成本&#xff0c;降低了碳排放量 Kalpataru Projects International Limited (KPIL) 正在扩展喀麦隆的电力网络&#xff0c;以改善该国 13% 人口的电网连接和电力供应。根据其项目管理方法&#xff0c;KPI…

Oracle 实战手册 工作实战经验总结

一、基本的数据库管理 高级开发人员需要掌握&#xff0c;了解Oracle数据库运行的基本原理&#xff0c;了解其中的概念。 1、数据库的启动和关闭 2、如何确定Oracle的版本&#xff1f; SQL> select * from v$version 2 / BANNER -------------------------------------…

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

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

【微信小程序独立开发1】项目提出和框架搭建

前言&#xff1a;之前学习小程序开发时仿照别人的页面自己做了一个商城项目和小说项目&#xff0c;最近突发奇想&#xff0c;想从0开发一个关于《宠物日记》的小程序&#xff0c;需求和页面都由自己设计&#xff0c;将在这记录开发的全部流程和过程中遇到的难题等... 1、搭建小…

怎样通过交换机封锁MAC地址

第一步&#xff1a;查询该IP所对应的MAC地址 display arp | include ip地址 第二步&#xff1a;封锁mac地址 mac-address blackhole mac地址 vlan 所属vlan-id 以上操作即可封锁

怎么采集今日头条的资讯或文章-简数采集器

如何使用简数采集器快速采集今日头条新闻的资讯或优质文章&#xff1f; 很遗憾&#xff0c;简数采集器暂时不支持采集今日头条上的新闻和文章&#xff0c;不建议采集。 可以换一个采集源进行采集。 简数采集器采集网页文章非常简单&#xff0c;只需输入对应的网址&#xff0…

MySQL 删除ibdata1时怎么恢复

标题&#xff1a;MySQL InnoDB数据恢复&#xff0c;丢失ibdata1时怎么安全恢复 废话在前&#xff1a; 恭喜你&#xff0c;当你看到这篇文章的时候&#xff0c;说明有可能 你心里已经有一万匹&#x1f40e;在奔腾了。千万不要乱删除ibdata1&#xff0c;有些博客无脑抓取、复制…

方案解决:5G基站节能及数字化管理

截至2023年10月&#xff0c;我国5G基站总数达321.5万个&#xff0c;占全国通信基站总数的28.1%。然而&#xff0c;随着5G基站数量的快速增长&#xff0c;基站的能耗问题也逐渐日益凸显&#xff0c;基站的用电给运营商带来了巨大的电费开支压力&#xff0c;降低5G基站的能耗成为…

腾讯云把向量数据库“卷”到哪一步了?

“不是我不明白&#xff0c;这世界变化快”&#xff0c;崔健在20世纪写下的这句歌词&#xff0c;放在刚刚过去的2023年&#xff0c;也同样适用。技术风向的变化之快&#xff0c;让不少人感到惊讶&#xff0c;向量数据库这一年的潮起潮落&#xff0c;就是一个典型的例子。 2023年…

OpenGL ES之深入解析如何实现图像锐化

一、什么是图像锐化? 图像锐化是一种图像处理技术,其目的是增强图像中的细节和边缘,使图像看起来更加清晰。这一过程通常涉及到突出图像中的高频信息,特别是强调像素之间的灰度变化。通过增强图像的高频细节,图像锐化可以改善图像在人类视觉系统和计算机视觉系统中的感知效…

电商API接口主要应用场景有哪些?

随着互联网技术的不断进步和电商行业的迅猛发展&#xff0c;电商API接口在商品交易、物流配送、客户服务等方面发挥着越来越重要的作用。本文将深入探讨电商API接口的技术原理、应用场景、开发方法以及优缺点。 一、技术原理 电商API接口是基于HTTP、TCP、IP等网络协议实现的…

[Kubernetes]10. k8s部署Goweb+mysql项目实战演练

一.安装docker构建镜像 如果要本地构建镜像的话,对应节点还需要安装docker,安装教程见:[Docker]一.Docker 简介与安装 linux环境,centos8下 docker及docker compose安装教程 k8s部署Goweb+mysql项目有两种方法:第一种是传统部署方法,第二种是通过ConfigMap实现应用配置分离部署…

Soul App:来一场始于“兴趣”,轻松自在的“零糖”社交吧

岁末年终,回顾2023年,这一年你都做了什么呢? 记不清楚没关系,互联网都帮你记录好了。2023年,B站的年轻人当“所见所闻”刷新自身认知时,往往会发送弹幕“啊?”来抒发惊叹。这一年,支付宝“小荷包”的用户中00后占了4成,近一半更开启了“自动攒”计划“无痛攒钱”!携程上,每7…

Bubble – 非实时协作软件如何促成好点子诞生

作为一名用户体验设计师&#xff0c;参加各种各样的会议构成一周工作大部分时间&#xff0c;一个项目的推进离不开大家的共同协作。 身在外企&#xff0c;我们要与不同国家&#xff08;时区&#xff09;不同部门的同事协作&#xff0c;Teams是每天必使用的软件&#xff0c;但是…

【JAVA基础--计算机网络】--TCP三次握手+四次挥手

三次握手四次挥手 写在前面1. 三次握手1.1 作用&#xff1a; 为了在不可靠的信道上建立起可靠的连接&#xff1b;1.2 建立过程1.3 面试提问 2. 四次挥手2.1 作用&#xff1a;为了在不可靠的网络信道中进行可靠的连接断开确认2.2 断开过程2.3 面试提问 写在前面 三次握手建立连…

【MATLAB】tvf_emd_LSTM神经网络时序预测算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 TVF-EMD-LSTM神经网络时序预测算法是一种结合了变分模态分解&#xff08;Variational Mode Decomposition&#xff0c;VMD&#xff09;、经验模态分解&#xff08;Empirical Mode Decompo…

【Python】Pyside2 可视化实现:每秒复制源文件一行到目标文件并打印日志

背景&#xff1a; 博主在某个项目中&#xff0c;需要模拟每秒钟生成一行数据&#xff0c;所以有了该博客的想法&#xff0c;其中有线程的内容&#xff0c;为了防止主界面卡住 效果&#xff1a; 代码&#xff1a; import sys import threading import timeimport openpyxl im…

安泰高压功率放大器在半导体测试中的应用

高压功率放大器在半导体测试中扮演着重要的角色。半导体测试是指对半导体器件进行各种电性能参数测试和质量检测的过程。以下是关于高压功率放大器在半导体测试中的应用的详细介绍。 一、高压信号发生器&#xff1a; 在半导体测试中&#xff0c;需要模拟高压环境下的工作条件以…

如何统一给文件夹名加后缀?这个方法教你一键搞定

随着计算机的普及&#xff0c;我们每天都会处理大量的文件和文件夹。有时候&#xff0c;为了更好地管理和分类文件&#xff0c;我们会给文件夹统一加上后缀。给文件加上后缀后最直接的好处就是方便文件管理。当我们给文件夹加上后缀时&#xff0c;我们可以很容易地根据后缀来判…

new mars3d.graphic.PolygonEntity({计算平面几何中心点及贴地效果展示

1.Mars3d提供了几何图形相关点位的计算方法polyutil&#xff1a; PolyUtil - V3.7.0 - Mars3D API文档 2.通过api可以算出相关经纬度坐标&#xff0c;实现相关中心点的展示 &#xff1a; 功能示例(Vue版) | Mars3D三维可视化平台 | 火星科技 3.相关实现代码&#xff1a; fu…