浅谈socket

本文深入探讨了Socket在网络编程中的位置,它位于传输层和应用层之间,起到连接管道的作用。介绍了会话层的概念,并讨论了不同类型的IO模型,包括同步阻塞、同步非阻塞、多路复用(select、poll、epoll)以及异步IO。重点阐述了多路复用如何通过select和epoll提高效率,特别是epoll的高效特性。同时,文章提到了信号驱动式I/O在TCP中的局限性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在大家常看到的计网五层模型里,可能找不到socket的位置,因为socket在运输层以上,应用层以下。

这里同情一下计网7层模型。表示层和会话层被阉割掉了。表示层我感觉倒是无所谓,就是格式转换。

会话层还是有点东西的。值得拿出来讲。
在这里插入图片描述
socket其实在第五层,会话层,英文叫session。

看到session是不是很容易联想到web服务端的session,浏览器的session,此session非彼session。但此间隐含的深意是类似的。

我们熟悉的session,是用来标识一个链接的用户属性的,就是用来表明,谁是谁。

session层取这个名字,我觉得也是有点异曲同工之妙的。

我们学习TCP族协议,是不是学到,TCP比IP多了端口号对吧,这个TCP协议吭哧吭哧的,辛辛苦苦的把数据运到了计算机的端口这里,然后呢?

总得有人来接一下这个数据吧,人家大老远跑来的。

TCP咚咚咚,敲门,“你好,这是端口号,80的数据,麻烦签收一下”。

在这里插入图片描述
我愿意称socket为管道连接器。

不管是什么类型的IO,还是什么语言,都少不了bind()这一个操作,意思是让socket监听xx端口的数据流。

数据有很多,管道接口就一个。

但是不同链接来到socket这边可不是混为一谈的,对每个链接accept成功之后,返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接,这样,就把不同的TCP链接分隔开来了,知道谁是谁。

如果一个TCP的服务完成之后,这个描述字就被关闭。
在这里插入图片描述
我们也应该知道,操作系统里,与网络的联通工作,是内核在做的,用户应用是在应用层这边了。

netIO必然有个数据转移的过程。

究竟是应用不停的问socket,“嗨,有我的信息吗?”

还是说等socket来通知应用呢?

应用问了之后,如果socket没有回答,是要一直等待下去吗,还是继续询问呢?socket当时没有数据是直接回答还是等有数据了再回答呢?

这些都是问题,这也引出了IO的同步、异步,阻塞,非阻塞这几种分类。

同步与异步的差异就是,同步是做一件事,必须得到一个结果。而异步,做了一件事之后,可能暂时是没有结果的。应用可以在这个没有结果的时间里,去干别的事情。等到结果来了,再继续下一阶段。

但是不管同步还是异步,目标都是要把事情给做完。

同步阻塞

在这里插入图片描述
同步阻塞,我们由上图可以看出,一切是从recvfrom这个调用开始的。

我们看下这个调用什么意思。


ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

sockfd:标识一个已连接套接口的描述字
buf:接收数据缓冲区。
len:缓冲区长度。
flags:调用操作方式。是以下一个或者多个标志的组合体,具体可选内容,可以参考百度百科
from:(可选)指针,指向装有源地址的缓冲区。
fromlen:(可选)指针,指向from缓冲区长度值。

这个函数,是接收一个描述字的数据的内容的。flags可以操控阻塞或者不阻塞,以及数据返回的结果等等设置。

描述字我们上面说过了,是scoket的accept被触发后,内核为这个新的tcp链接生成的标识。

我们都知道TCP有3次握手,4次挥手,在这期间,也有数据在不断的传递的,标识这个TCP链接的描述字,就一直在内核里存着,直到链接彻底结束为止。

那这个描述字代表的链接,有没有数据?这是个问题。也是应用迫切需要知道的。

同步阻塞:

recvfrom拿着描述字问内核要数据,内核没有数据,那就两边都干等着,直到内核有数据,返回过去,才进行下一步。

同步非阻塞

在这里插入图片描述
不断的拿着描述字,去询问内核现在该tcp有没有数据传来,没有的话直接返回没有。

如果有数据,这个数据复制期间,也是阻塞的。

上面这两种,同一时间只能处理单个用户,这肯定不是我们想要看到的,主要的限制点在哪里,因为我们每次recvfrom都是一个socket描述字。

多路复用

这个名词听上去有点高大上,redis就是因为多路复用,才可以单线程也能有这么高的效率。

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

maxfdp : 需要监视的最大文件描述符加1
readfds:需要检测的可读文件描述符的集合
writefds:可写文件描述符的集合
errorfds:异常文件描述符的集合
timeout:等待时间,如果是null,则表示阻塞式等待,直到有事件就绪,才会返回。0表示非阻塞式等待,没有事件就立即返回,大于0表示等待的时间

返回值:大于0表示就绪的文件描述符的个数,0表示等待时间到了,小于0表示调用失败

多路复用select版本,不是一开始就去recvfrom的,而是先用select去查询,所有scoket描述字,瞅瞅它里面有没有内容(就绪),可写描述符,就被存到writefds里,返回就绪的数目。
在这里插入图片描述

在这里插入图片描述
多路复用还有poll版,epoll版,最强的还是epoll。

epoll没有,需要监视的最大文件描述符加1(select里的maxfdp),这个限制

“epoll对于句柄事件的选择不是遍历的,是事件响应的,就是句柄上事件来就马上选择出来,不需要遍历整个句柄链表,因此效率非常高,内核将句柄用红黑树保存的,IO效率不随FD数目增加而线性下降。

相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。”

参考

非阻塞异步

在这里插入图片描述
aio_read调用异步读取,直接返回,等待有数据返回之后,才会进行后续的步骤。

信号驱动式(Signal-Driven) I/O

在这里插入图片描述
感觉跟异步IO差不多,对于TCP而言,信号驱动的I/O方式近乎无用,因为导致这种通知的条件为数众多,每一个来进行判别会消耗很大资源,与前几种方式相比优势尽失

总的比较

在这里插入图片描述

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

rgbhi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值