Posix API

article/2025/6/22 19:28:41

Posix API与网络协议栈

  • Posix API与网络协议栈
    • socket
    • bind
    • listen
    • accept
    • connect
    • recv和send

整个网络之间的通信贯穿起来都离不开网络协议栈这个东西,网络协议栈主要负责主要负责网络间数据的通信,自顶向下可分为五层:应用层,传输层,网络层,数据链路层和物理层。

在这里插入图片描述
网络协议栈各部分所处位置与操作系统有这密切的关系:

  • 应用层是位于用户层的。 这部分代码是由网络协议的开发人员来编写的,比如HTTP协议、HTTPS协议以及SSH协议等。
  • 传输层和网络层是位于操作系统层的。其中传输层最经典的协议叫做TCP协议,网络层最经典的协议叫做IP协议,这就是我们平常所说的TCP/IP协议。
  • 数据链路层是位于驱动层的。 其负责真正的数据传输。

客户端与服务端之间的交互,遍布整个网络协议栈,接下来我们来介绍一些相关的接口

Posix API与网络协议栈

socket

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

创建套接字的接口,参数如下:

  • domain:创建套接字的域或者叫做协议家族,也就是创建套接字的类型。该参数就相当于cpp struct sockaddr结构的前16个位。如果是本地通信就设置为AF_UNIX,如果是网络通信就设置为AF_INET(IPv4)或AF_INET6(IPv6)。
  • type:创建套接字时所需的服务类型。其中最常见的服务类型是SOCK_STREAMSOCK_DGRAM,如果是基于UDP的网络通信,我们采用的就是SOCK_DGRAM,叫做用户数据报服务,如果是基于TCP的网络通信,我们采用的就是SOCK_STREAM,叫做流式套接字,提供的是流式服务。
  • protocol:创建套接字的协议类别。你可以指明为TCP或UDP,但该字段一般直接设置为0就可以了,设置为0表示的就是默认,此时会根据传入的前两个参数自动推导出你最终需要使用的是哪种协议。

返回值:

  • 套接字创建成功返回一个文件描述符,创建失败返回-1,同时错误码会被设置。

socket 接口的作用我们就可以理解为两个:

  • 分配 fd ,在网络通信中,一般都是以 fd 的形式来进行的,创建套接字,就相当于分配了一个 fd ,打开了一个文件,他其实就是使用的 bitmap 的形式来进行分配的,从前往后,某个文件描述符被使用就将其占用的 bit 为置为1。
  • 分配一个 tcb 控制块,主要就是对当前任务的一个描述,注意,这儿是并没有分配发送缓冲区和接收缓冲区的。

socke如何被调用?

我们们都是在用户层编写代码,soket 接口属于系统调用接口,也就是说,当一个可执行程序创建以后,此时操作系统就会为其分配一个进程,然后这个进程根据调用顺序,调用到我们的 socket 接口。

bind

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

绑定套接字,参数如下:

  • sockfd:绑定的文件的文件描述符。也就是我们创建套接字时获取到的文件描述符。
  • addr:网络相关的属性信息,包括协议家族、IP地址、端口号等。
  • addrlen:传入的addr结构体的长度。

返回值说明:

  • 绑定成功返回0,绑定失败返回-1,同时错误码会被设置。

对于 bind 接口来说,他的作用其实就是将 socket 分配的 fd,以及网络相关的属性信息(协议家族、IP地址、端口号)绑定到对应的 tcb 控制块上面,因为调用 socket 接口只是单纯的打开了一个文件,接下要来要如何做就需要 bind 接口来进行,实际上就是将文件与网络关联了起来。

struct sockaddr_in 结构体

/* Structure describing an Internet socket address.  */
struct sockaddr_in{__SOCKADDR_COMMON (sin_);in_port_t sin_port;			/* Port number.  */struct in_addr sin_addr;		/* Internet address.  *//* Pad to size of `struct sockaddr'.  */unsigned char sin_zero[sizeof (struct sockaddr)- __SOCKADDR_COMMON_SIZE- sizeof (in_port_t)- sizeof (struct in_addr)];};
  • sin_family:表示协议家族。
  • sin_port:表示端口号,是一个16位的整数。
  • sin_addr:表示IP地址,是一个32位的整数。

在绑定之前我们就需要创建一个struct sockaddr_in类型的结构体将对应的网络属性信息填充到这个结构体中,发送到网络之前需要将端口号设置为网络序列,由于端口号是16位的,因此我们需要使用前面说到的htons函数将端口号转为网络序列。此外,由于网络当中传输的是整数IP,我们需要调用inet_addr函数将字符串IP转换成整数IP,然后再将转换后的整数IP进行设置。

struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); // 0.0.0.0
serveraddr.sin_port = htons(port);              // 0 ~ 1023

INADDR_ANY

/* Address to accept any incoming messages.  */
#define	INADDR_ANY		((in_addr_t) 0x00000000)

我们使用的服务器对应的 IP 不一定就是公网 IP ,那么就会存在不能被直接绑定的问题,就不能被外网所访问,此时就需要 bind 0 ,而INADDR_ANY其实就是0,在绑定INADDR_ANY以后,此时我们的服务器就可以被外网所访问了。

绑定INADDR_ANY也是有好处的,一个服务器可能会对应多张网卡,多张网卡也就意味着可能会存在多个 IP 地址,但是一台服务器对应比如说 2000 这个端口号只有一个,如果此时服务器绑定了某个 ip 地址,那么就只有该 IP 地址的数据可以通过 2000 这个端口发送给服务器,其他网卡的数据发送不了,绑定了INADDR_ANY以后,也就代表着任意网卡的数据发送来以后,都可以被 2000 这个端口接收到发送给服务器。

listen

int listen(int sockfd, int backlog);

设置套接字的监听状态,参数说明:

  • sockfd:需要设置为监听状态的套接字对应的文件描述符。
  • backlog:全连接队列的最大长度。如果有多个客户端同时发来连接请求,此时未被服务器处理的连接就会放入连接队列,该参数代表的就是这个全连接队列的最大长度,一般不要设置太大,设置为5或20即可。

返回值说明:

  • 监听成功返回0,监听失败返回-1,同时错误码会被设置。

listen 接口实际上就是将对应的 tcb 控制块中的 TCP_STATUS 由 CLOSED 设置为 LISTEN这个状态,如下图所示:
在这里插入图片描述
我们就可以理解为,在进行了前两步操作以后,此时如果没有 listen 此时连接请求是会被拒绝的,举个例子,你要找酒店前台小姐姐,但是当前前台小姐姐正在培训,你就找不到,只有他正式上岗以后,你才可以找到他,正式上岗的这个操作就相当于 listen。

理解listen的第二个参数

客户端调用 connect 函数以后,此时就代表了三次握手的开始,三次握手是一个持续的过程,并不是发生在某一个调用接口里面,当服务端接收到第一次握手请求的时候,此时就会保存一个半连接队列,然后继续走流程,直到第三次握手结束时刻,此时半连接队列就会切换成为全连接队列,全连接队列的数量正是由 listen 的第二个参数进行控制的,也就是他的参数 + 1。其实从 TCP 协议的演变过程中,这个参数也在变化(syn 半连接队列数量 -> syn 半连接队列数量 + accept 全连接队列数量 -> accept 全连接队列数量)
在这里插入图片描述

accept

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

参数说明:

  • sockfd:特定的监听套接字,表示从该监听套接字中获取连接。
  • addr:对端网络相关的属性信息,包括协议家族、IP地址、端口号等。
  • addrlen:调用时传入期望读取的addr结构体的长度,返回时代表实际读取到的addr结构体的长度,这是一个输入输出型参数。

返回值说明:

  • 获取连接成功返回接收到的套接字的文件描述符,获取连接失败返回-1,同时错误码会被设置

对于 accept 来说,他其实就是建立连接的一个过程,他的作用之一也是分配 fd ,我们将监听套接字的 fd 传进去以后,他会给我们返回一个 fd,这个才是真实进行事件处理的 fd ,第二个作用就是建立fd 与 tcb 控制块之间的映射关系。

理解 accept 通过 ET 模式实现

在 recator 的实现中,我们运用的 epoll 相关的方法,我们知道,epoll 分为 LT 工作模式与 ET 工作模式,对于 accept 来说,他好像是更适用于 LT 模式来进行实现的,因为只要时间一发生,就会通知进行处理,非常的适用于 accept 接口。

那么,在 ET 工作模式中如何操作 accept 呢?其实也很简单,运用循环的方式进行实现,直到最终给我们返回的值为 -1 了,当前就可以 break 掉了,我们来看一段伪代码:
在这里插入图片描述

connect

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

发起连接请求的函数,参数如下:

  • sockfd:特定的套接字,表示通过该套接字发起连接请求。
  • addr:对端网络相关的属性信息,包括协议家族、IP地址、端口号等。
  • addrlen:传入的addr结构体的长度。

返回值说明:

  • 连接或绑定成功返回0,连接失败返回-1,同时错误码会被设置。

对于 connect 来说,他是用于客户端的接口,在客户端调用 connect 接口以后,此时就会进行三次握手的流程了,需要注意的是,三次握手只能是由客户端发起,服务端是不会发起三次握手的。

recv和send

ssize_t recv(int sockfd, void *buf, size_t len, int flags);ssize_t send(int sockfd, const void *buf, size_t len, int flags);

参数如下:

  • sockfd:特定的文件描述符,表示从该文件描述符中读取数据。
  • buf:数据的存储位置,表示将读取到的数据存储到该位置。调用 send 时就表示发送的数据是存到该位置的。
  • count:数据的个数,表示从该文件描述符中读取数据的字节数。
  • flags:一般都设置为0;

在之前的系统编程中,我们学习过 write/read 函数,其实作用跟这两个接口是一样的,我们回忆一下当中的逻辑:

  • 读端一直读,写端不写了,读端读取完毕以后就阻塞住;
  • 写端一直写,读端不读,此时写满了也就不会再写了,也会阻塞住;
  • 写端关闭,读端将数据读取到 0 以后也会阻塞住;
  • 读端关闭,此时就系统就会直接将写端 kill 掉,因为读端不会再读取数据了。

我们还需要注意,TCP/UDP 两种模式下他们发送数据的方式是不一样的,一个是面向报文的形式,一个是面向字节流的形式,

如何理解面向数据报?

对于 UDP 协议来说,是不会进行报文分割的,也就是说,应用层交给 UDP 多长的报文,UDP 就会原模原样的发送给内核,并不会分割,举个例子:应用层发送 100 个字节的数据,那么 UDP 就会将这100 个字节直接发送给内核,这就叫面向数据报。

如何理解面向字节流?

对于 TCP 协议来说,会存在报文分割的现象,也就是说,应用下发报文以后, TCP 并不会向 UDP 那样,直接一股脑的全部发出去,他如果识别到报文太长的话,就会将其进行分割,分成几部分分开进行发送,如果对应的报文太短,就会先保存先来,等到足够长度在一起发送。

对于recv/send/read/write等这些函数来说,根本意义上他们其实属于一种拷贝函数,那么如何去理解呢?

以 TCP 通信为例,调用 socket 通信以后,内核中会创建一个发送缓冲区与一个接收缓冲区,当调用 send 函数发数据时,就会将这些数据写入到这个发送缓冲区当中,此时 send 函数就返回了,这些数据就由 TCP 自行进行处理,至于怎么发就向上面所描述的一样。

接收也是,网卡驱动会将数据先发到接收缓冲区当中,然后应用调用 recv 函数读取接收缓冲区当中的数据,这里也是可以按任意字节数据的数据进行读取的,这种对于 TCP 来说,他并不关心你发过来的是什么数据,他只关心自己只要将数据放进对应的接收缓冲区即可,在他看来就是一个一个字节的数据,至于发什么,他并不会去管,这种就叫面向字节流。

注意,UDP 协议中并不会存在真正意义上的发送缓冲区,因为 UDP 是面向数据报的,但是会有对应的接收缓冲区,因为如果上一次的数据还没有处理完毕,就会将这次的数据暂存于接收缓冲区当中,等待上一次数据处理完毕在进行处理,防止丢包的现象发生。

在这里插入图片描述


http://www.hkcw.cn/article/AlRmNenhmy.shtml

相关文章

Flask+LayUI开发手记(七):头像的上传及突破static目录限制

看了看,上篇开发手记是去年8月份写的,到现在差2个月整一年了。停更这么长时间,第一个原因是中间帮朋友忙一个活,那个技术架构是用springboot的,虽然前端也用layUI,但和Flask-python完全不搭界,所…

第一篇:揭示模型上下文协议(MCP):AI的通用连接器

模型上下文协议(MCP)是 Anthropic 于 2024 年末推出的一项开放标准,旨在彻底改变人工智能(AI)模型与外部数据源及工具的连接方式。它被誉为 AI 应用的“USB-C 接口”,其核心目的是标准化 AI 助理与数据所在…

(九)学生写作画像可视化

在上次报告中提到的无法正确识别登录状态的问题已经解决,现在调用后端api时可以正确load_user并得到登录状态。 登录状态问题解决后,本次主要实现的是学生写作画像的数据可视化,学生可以登陆后查看自己之前的作文列表与历史各维度得分&#x…

国家能源集团称从未设拓展中心 警惕假冒机构

国家能源集团称从未设拓展中心 警惕假冒机构。国家能源集团近期发现有不法分子假冒其名义在全国多地设立所谓“拓展中心”,涉嫌从事违法活动,并通过抖音、微信视频号等网络平台进行虚假宣传。对此,国家能源集团发布严正声明,强调从未在全国任何地区设立任何“拓展中心”或“…

官方证实!美国停止对华芯片设计软件(EDA)销售 打压“中国芯”再升级

面对中国在先进芯片领域取得的重大突破,美国特朗普政府将目光转向了设计芯片的软件,试图借此打压“中国芯”。英国《金融时报》5月28日报道称,美国政府已采取措施限制向中国出售此类产品。美国彭博社也报道了类似消息,称美国商务部工业与安全局已致函部分头部美国电子设计自…

乌称对俄军机发动大规模袭击 摧毁41架战略轰炸机

俄乌第二轮谈判定于6月2日举行。谈判前夕,乌克兰声称对俄罗斯实施了一次特别行动,摧毁了41架俄战略轰炸机。然而,俄媒认为乌方宣称的战果并不符合实际。乌克兰国家安全局在社交媒体上表示,6月1日晚对俄罗斯发起了一次代号为“蛛网”的特种作战行动,袭击了俄军的战略轰炸机…

离婚,失业,失亲……她40岁后的人生那么难?美丽无关年龄

离婚,失业,失亲……她40岁后的人生那么难?美丽无关年龄!美丽与年龄无关,即便他人不再认同你。都说年龄是女人最大的秘密,尤其是生儿育女之后,“衰老”这个词便成了女人内心最大的恐惧和痛处。如今,三八妇女节甚至被迫更名为“三八女神节”,以消除称呼上的忌讳。然而,…

巴西登革热疑似及确诊病例超143万 疫情严峻引发关注

巴西卫生部发布消息称,截至6月2日,今年该国登革热疑似及确诊病例已达到1430300例,死亡病例为1075例,另有818例疑似死亡病例正在调查中。圣保罗州、米纳斯吉拉斯州和巴拉那州是目前巴西登革热疫情最为严重的地区。责任编辑:zhangxiaohua

四川古蔺警方通报7人殴打未成年人 误会引发暴力事件

四川古蔺县警方于6月2日通报了一起涉及未成年人的暴力事件。据报道,两名未成年人在骑车时发出笑声,被15岁的陈某甲误以为是在嘲笑自己。随后,陈某甲与其他六人一起在地下停车场对这两名未成年人进行了殴打。目前,七名涉案人员均已被警方抓获,其中两人因涉嫌犯罪被刑事拘留…

多位男星减肥成功 压力已给到沈腾 贾冰瘦身引热议

5月31日,贾冰的妻子发布了一段视频,并配文“从此我家多了个瘦子”。在两人合影中,贾冰明显瘦了很多。评论区里大家纷纷询问他如何瘦下来的,甚至有人表示瘦得认不出来了。贾冰的妻子回复说,主要是通过少吃(一天只吃一顿)和运动来减肥的。贾冰在评论区幽默地回应说:“一次…

任务25:绘制全局时间线(TimeLine)

任务描述 知识点: DjangoECharts时间线图重 点: ECharts时间线图内 容: 参考ECharts官网示例创建timeline.js,绘制时间线图引入js文件,并调用绘图函数时间线图形配置项微调任务指导 1、参考ECharts官网示例:(https://echarts.apache.org/examples/zh/index.html),…

2025年渗透测试面试题总结-青藤云[校招]红队攻防岗(题目+回答)

安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 青藤云[校招]红队攻防岗 1. 数据库提权 2. Redis计划任务提权 3. 绕过杀软执行数据库命令 4. SQL Server…

HCIP:MPLS LDP的原理和配置

目录 一、MPLS LDP的原理 1.回顾MPLS 2.MPLS LDP的基本概念 3.LDP的工作过程主要分为两部分 ​4.LDP会话: 5.LDP的对等体 6.LDP的消息 7.LDP工作原理 8.标签的发布和管理 二、MPLD LDP的实验配置 1.配置IP地址和OSPF 2.配置MPLS LDP 3.查看各个设备的…

这个地级市,“含潮量”全国顶流? 潮玩之都的转型之路

这个地级市,“含潮量”全国顶流? 潮玩之都的转型之路!东莞市中心矗立着一座12米高的巨型潮玩雕塑“劳拉”,其轮廓以彩色线条勾勒,充满科幻感。这座2021年落成的城市新地标脚下,延伸出一条由设计工作室、潮玩展销中心和沉浸式体验馆串联的“潮玩大道”。这里不定期举行沙龙…

男子机场取车遭一嗨租车拦车 店员态度恶劣引发争议

男子机场取车遭一嗨租车拦车 店员态度恶劣引发争议!6月2日,苏先生在昆明长水机场停车时遇到了麻烦。他将车停在B2层S1区C7车位,该车位未标注“专用车位”且无人值守。然而当他返回取车时,“一嗨租车”的工作人员告知他该车位已被他们租下,并称他的车停到了他们的店里。苏先…

今年第一批吃菌子中毒的人出现了!小伙吃野生菌后竟开始隔空抓凤凰

近日,云南临沧,胡先生吃野生菌后中毒致幻,躺在病床上隔空抓物。胡先生称自己看到了凤凰、乌贼、水母、蜘蛛、螃蟹......除胡先生外,此前一对昆明情侣因吃见手青中毒,在医院过的“520”近日,有IP地址为昆明的网友发帖称,自己和丈夫在家做菌子吃,结果两人都出现中毒症状,…

乒超联赛门票开售 雄安首迎顶尖赛事

6月9日至11日,2025赛季中国乒乓球俱乐部超级联赛常规赛第一阶段比赛将在河北雄安新区雄安体育中心体育馆举行。门票于6月3日18:00在秀动票务平台开售,票价从288元到788元不等。2025赛季乒超联赛包括男子和女子团体赛,分为三个阶段的常规赛和总决赛,从6月持续到12月。常规赛…

【iOS】ARC 与 Autorelease

ARC 与 Autorelease 文章目录 ARC 与 Autorelease前言何为ARC内存管理考虑方式自己生成的对象,自己持有非自己生成的对象,自己也可以持有不再需要自己持有的对象时释放非自己持有的对象无法释放 ARC的具体实现编译期和运行期ARC做的事情ARC实现: __autoreleasing 与 Autoreleas…

成都一断头路骑手受伤倒地不幸身亡

6月2日,一位网友发视频称,他在前往四川乐山游玩的路上误入成都一个胡同,发现一名摩托车骑手受伤倒在地上,于是帮忙拨打了120急救电话。该网友表示,此事于6月1日发生在成都天府新区东山大道三段附近一处路段,骑手因伤势过重离世,“如果能更早发现,或许还能被抢救回来。”…

媒体:汽车行业的价格战该“刹车”了 行业协会倡议维护健康发展

“618”限时促销活动期间,部分国内车企发起了大幅降价活动,最高直降5.3万元,这标志着新能源汽车市场新一轮“价格战”开始。山东交通广播汽车栏目制作人、资深汽车媒体人胡明表示,这次价格战始于一家车企在5月23日发起的大规模降价,主要原因是该品牌面临更多有竞争力的对手…