Linux内核Socket通信原理和实例讲解

关于对 Socket 的认识,大致分为下面几个主题,Socket 是什么,Socket 是如何创建的,Socket 是如何连接并收发数据的,Socket 套接字的删除等。

Socket 是什么以及创建过程

一个数据包经由应用程序产生,进入到协议栈中进行各种报文头的包装,然后操作系统调用网卡驱动程序指挥硬件,把数据发送到对端主机。整个过程的大体的图示如下。

我们大家知道,协议栈其实是位于操作系统中的一些协议的堆叠,这些协议包括 TCP、UDP、ARP、ICMP、IP等。

通常某个协议的设计都是为了解决某些问题,比如 TCP 的设计就负责安全可靠的传输数据,UDP 设计就是报文小,传输效率高,ARP 的设计是能够通过 IP 地址查询物理(Mac)地址,ICMP 的设计目的是返回错误报文给主机,IP 设计的目的是为了实现大规模主机的互联互通。

应用程序比如浏览器、电子邮件、文件传输服务器等产生的数据,会通过传输层协议进行传输,而应用程序是不会和传输层直接建立联系的,而是有一个能够连接应用层和传输层之间的套件,这个套件就是 Socket。

在上面这幅图中,应用程序包含 Socket 和解析器,解析器的作用就是向 DNS 服务器发起查询,查询目标 IP 地址。

应用程序的下面就是操作系统内部,操作系统内部包括协议栈,协议栈是一系列协议的堆叠。操作系统下面就是网卡驱动程序,网卡驱动程序负责控制网卡硬件,驱动程序驱动网卡硬件完成收发工作。

在操作系统内部有一块用于存放控制信息的存储空间,这块存储空间记录了用于控制通信的控制信息。其实这些控制信息就是 Socket 的实体,或者说存放控制信息的内存空间就是套接字的实体。

这里大家有可能不太清楚所以然,所以我用了一下 netstat 命令来给大伙看一下套接字是啥玩意。

我们在 Windows 的命令提示符中输入

 netstat -ano
 
 # netstat 用于显示套接字内容 , -ano 是可选选项
 # a 不仅显示正在通信的套接字,还显示包括尚未开始通信等状态的所有套接字
 # n 显示 IP 地址和端口号
 # o 显示套接字的程序 PID

我的计算机会出现下面结果。

图中的每一行都相当于一个套接字,每一列也被称为一个元组,所以一个套接字就是五元组(协议、本地地址、外部地址、状态、PID)。有的时候也被叫做四元组,四元组不包括协议。

比如图中的第一行,它的协议就是 TCP,本地地址和远程地址都是 0.0.0.0,这表示通信还没有开始,IP 地址暂时还未确定,而本地端口已知是 135,但是远程端口还未知,此时的状态是 LISTENING,LISTENING 表示应用程序已经打开,正在等待与远程主机建立连接最后一个元组是 PID,即进程标识符,PID 就像我们的身份证号码,能够精确定位唯一的进程。

现在你可能对 Socket 有了一个基本的认识,现在喝口水,休息一下,让我们继续探究 Socket。

现在我有个问题,Socket 是如何创建的呢?

Socket 是和应用程序一起创建的。应用程序中有一个 socket 组件,在应用程序启动时,会调用 socket 申请创建套接字,协议栈会根据应用程序的申请创建套接字:首先分配一个套接字所需的内存空间,这一步相当于是为控制信息准备一个容器,但只有容器并没有实际作用,所以你还需要向容器中放入控制信息;如果你不申请创建套接字所需要的内存空间,你创建的控制信息也没有地方存放,所以分配内存空间,放入控制信息缺一不可。至此套接字的创建就已经完成了。

套接字创建完成后,会返回一个套接字描述符给应用程序,这个描述符相当于是区分不同套接字的号码牌。根据这个描述符,应用程序在委托协议栈收发数据时就需要提供这个描述符。

套接字连接

套接字创建完成后,最终还是为数据收发服务的,在数据收发之前,还需要进行一步 connect,也就是建立连接的过程。这个连接并不是真实的连接:用一根水管插在两个电脑之间。

而是应用程序通过 TCP/IP 协议标准从一个主机通过网络介质传输到另一个主机的过程。

套接字刚刚创建完成后,还没有数据,也不知道通信对象。在这种状态下,即使你让客户端应用程序委托协议栈发送数据,它也不知道发送到哪里。

所以浏览器需要根据网址来查询服务器的 IP 地址,做这项工作的协议是 DNS,查询到目标主机后,再把目标主机的 IP 告诉协议栈,至此,客户端这边就准备好了。

在服务器上,与客户端一样也需要创建套接字,但是同样的它也不知道通信对象是谁,所以我们需要让客户端向服务器告知客户端的必要信息:IP 地址和端口号。

现在通信双方建立连接的必要信息已经具备,只欠一股东南风了。通信双方收到数据之后,还需要一块位置来存放,这个位置就是缓冲区,它是内存的一部分,有了缓冲区,就能够进行数据的收发操作了。

OK,现在客户端想要给服务器发送一条数据,该进行哪些操作呢?

首先,客户端应用程序需要调用 Socket 库中的 connect 方法,提供 socket 描述符和服务器 IP 地址、端口号。

 connect(<描述符>、<服务器IP地址和端口号>)

这些信息会传递给协议栈中的 TCP 模块,TCP 模块会对请求报文进行封装,再传递给 IP 模块,进行 IP 报文头的封装,然后传递给物理层,进行帧头封装,之后通过网络介质传递给服务器,服务器上会对帧头、IP 模块、TCP 模块的报文头进行解析,从而找到对应的套接字,套接字收到请求后,会写入相应的信息,并且把状态改为正在连接。请求过程完成后,服务器的 TCP 模块会返回响应,这个过程和客户端是一样的。

在一个完整的请求和响应过程中,控制信息起到非常关键的作用(具体的作用我们后面会说)。

  • SYN 就是同步的缩写,客户端会首先发送 SYN 数据包,请求服务端建立连接。

  • ACK 就是相应的意思,它是对发送 SYN 数据包的响应。

  • FIN 是终止的意思,它表示客户端/服务器想要终止连接。

由于网络环境的复杂多变,经常会存在数据包丢失的情况,所以双方通信时需要相互确认对方的数据包是否已经到达,而判断的标准就是 ACK 的值。

当所有建立连接的报文都能够正常收发之后,此时套接字就已经进入可收发状态了,此时可以认为用一根管理把两个套接字连接了起来。

当然,实际上并不存在这个管子。建立连接之后,协议栈的连接操作就结束了,也就是说 connect 已经执行完毕,控制流程被交回给应用程序。

收发数据

当控制流程从 connect 回到应用程序之后,接下来就会直接进入数据收发阶段,数据收发操作是从应用程序调用 write 将要发送的数据交给协议栈开始的,协议栈收到数据之后执行发送操作。

协议栈不会关心应用程序传输过来的是什么数据,因为这些数据最终都会转换为二进制序列,协议栈在收到数据之后并不会马上把数据发送出去,而是会将数据放在发送缓冲区,再等待应用程序发送下一条数据。

为什么收到数据包不会直接发送出去,而是放在缓冲区中呢?

因为只要一旦收到数据就会发送,就有可能发送大量的小数据包,导致网络效率下降。所以协议栈需要将数据积攒到一定数量才能将其发送出去。

至于协议栈会向缓冲区放多少数据,这个不同版本和种类的操作系统有不同的说法,不过,所有的操作系统和种类都会遵循下面这几个标准:

  • 第一个判断要素是每个网络包能够容纳的数据长度,判断的标准是 MTU,它表示的是一个网络包的最大长度。最大长度包含头部,所以如果单论数据区的话,就会用 MTU - 包头长度,由此的出来的最大数据长度被称为 MSS。

  • 另一个判断标准是时间,当应用程序产生的数据比较少,协议栈向缓冲区放置数据效率不高时,如果每次都等到 MSS 再发送的话,可能因为等待时间太长造成延迟,在这种情况下,即使数据长度没有到达 MSS,也应该把数据发送出去。

协议栈并没有告诉我们怎样平衡这两个因素,如果数据长度优先,那么效率有可能比较低;如果时间优先,那又会降低网络的效率。

资料直通车:最新Linux内核源码资料文档+视频资料

内核学习地址:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈

经过了一段时间。。。。。。

假设我们使用的是长度有限法则,此时缓冲区已满,协议栈要发送数据了,协议栈刚要把数据发送出去,却发现无法一次性传输这么大数据量(相对的)的数据,那怎么办呢?

在这种情况下,发送缓冲区中的数据就会超过 MSS 的长度,发送缓冲区中的数据会以 MSS 大小为一个数据包进行拆分,拆分出来的每块数据都会加上 TCP,IP,以太网头部,然后被放进单独的网络包中。

到现在,网络包已经准备好发往服务器了,但是数据发送操作还没有结束,因为服务器还未确认是否已经收到网络包。因此在客户端发送数据包之后,还需要服务器进行确认。

TCP 模块在拆分数据时,会计算出网络包偏移量,这个偏移量就是相对于数据从头开始计算的第几个字节,并将算好的字节数写在 TCP 头部,TCP 模块还会生成一个网络包的序号(SYN),这个序号是唯一的,这个序号就是用来让服务器进行确认的。

服务器会对客户端发送过来的数据包进行确认,确认无误之后,服务器会生成一个序号和确认号(ACK)并一起发送给客户端,客户端确认之后再发送确认号给服务器。

我们来看一下实际的工作过程。

首先,客户端在连接时需要计算出序号初始值,并将这个值发送给服务器。接下来,服务器通过这个初始值计算出 确认号并返回给客户端。初始值在通信过程中有可能会丢弃,因此当服务器收到初始值后需要返回确认号用于确认。

同时,服务器也需要计算出从服务器到客户端方向的序号初始值,并将这个值发送给客户端。然后,客户端也需要根据服务器发来的初始值计算出确认号发送给服务器,至此,连接建立完成,接下来就可以进入数据收发阶段了。

数据收发阶段中,通信双方可以同时发送请求和响应,双方也可以同时对请求进行确认。

请求 - 确认机制非常强大,通过这一机制,我们可以确认接收方有没有收到某个包,如果没有收到则重新发送,这样一来,但凡网络中出现的任何错误,我们都可以即使发现并补救。

网卡、集线器、路由器都没有错误补救机制,一旦检测到错误就会直接丢弃数据包,应用程序也没有这种机制,起作用的只是 TCP/IP 模块。

由于网络环境复杂多变,所以数据包会存在丢失情况,因此发送序号和确认号也存在一定规则,TCP 会通过窗口管理确认号。

断开连接

当通信双方不再需要收发数据时,需要断开连接。不同的应用程序断开连接的时机不同。以 Web 为例,浏览器向 Web 服务器发送请求消息,Web 服务器再返回响应消息,这时收发数据就全部结束了,服务器可能会首先发起断开响应,当然客户端也有可能会首先发起(谁先断开连接是应用程序做出的判断),与协议栈无关。

无论哪一方发起断开连接的请求,都会调用 Socket 库的 close 程序。我们以服务器断开连接为例,服务器发起断开连接请求,协议栈会生成断开连接的 TCP 头部,其实就是设置 FIN 位,然后委托 IP 模块向客户端发送数据,与此同时,服务器的套接字会记录下断开连接的相关信息。

收到服务器发来 FIN 请求后,客户端协议栈会将套接字标记为断开连接状态,然后,客户端会向服务器返回一个确认号,这是断开连接的第一步,在这一步之后,应用程序还会调用 read 来读取数据。等到服务器数据发送完成后,协议栈会通知客户端应用程序数据已经接收完毕。

只要收到服务器返回的所有数据,客户端就会调用 close 程序来结束收发操作,这时客户端会生成一个 FIN 发送给服务器,一段时间后服务器返回 ACK 号,至此,客户端和服务器的通信就结束了。

删除套接字

通信完成后,用来通信的套接字就不再会使用了,此时我们就可以删除这个套接字了。不过,这时候套接字不会马上删除,而是等过一段时间再删除。

等待这段时间是为了防止误操作,最常见的误操作就是客户端返回的确认号丢失,至于等待多长时间,和数据包重传的方式有关。

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

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

相关文章

平板触控笔哪些品牌好?ipad触控笔推荐平价

苹果电容笔与平替电容笔两者需要根据我们的预算以及需求去选择&#xff0c;要是日常多用于用于绘画&#xff0c;建议可以用Apple Pencil&#xff0c;而对于日常仅仅用于学习与记笔记&#xff0c;可以用平替电容笔&#xff0c;由于平替电容笔的品质与表现都非常优秀。小编整理了…

初识进程

文章目录一、进程的概念1. 进程是什么及进程的管理2. Linux 下的 pcb3. 系统调用接口 getpid 和 getppid4. 系统调用接口 fork一、进程的概念 1. 进程是什么及进程的管理 在 Linux下 ./binaryfile 运行一个程序或者在 Windows下双击运行一个程序时&#xff0c;程序就变成了一个…

【百面成神】spring基础12问,你能坚持到第几问

前 言 &#x1f349; 作者简介&#xff1a;半旧518&#xff0c;长跑型选手&#xff0c;立志坚持写10年博客&#xff0c;专注于java后端 ☕专栏简介&#xff1a;java面试宝典&#xff0c;特点&#xff1a;全、精、深、简&#xff0c;力求每个核心知识点1分钟回答好。 &#x1f3…

RK3588平台开发系列讲解(视频篇)RTP H264 码流打包详解

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、单 NALU 封包方式二、组合封包方式三、分片封包方式沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 H264 码流是放在 RTP 的有效载荷部分的。因此有效载荷前面的 RTP 头部跟码流本身是没有关系的,所以我…

Python|蓝桥杯进阶第六卷——搜索

欢迎交流学习~~ 专栏&#xff1a; 蓝桥杯Python组刷题日寄 蓝桥杯进阶系列&#xff1a; &#x1f3c6; Python | 蓝桥杯进阶第一卷——字符串 &#x1f50e; Python | 蓝桥杯进阶第二卷——贪心 &#x1f49d; Python | 蓝桥杯进阶第三卷——动态规划 ✈️ Python | 蓝桥杯进阶…

mysql数据库介绍

&#x1f49c;今天对mysql的一些基础概念进行讲诶横扫&#xff0c;如概念、特点、优势、发展历史等等。对之前内容感兴趣的同学可以参考&#x1f447;&#xff1a; 链接: mysql学习之数据系统概述 &#x1f3be;让我们开始今日份的学习吧&#xff01; 目录1. mysql的基本概念…

十七、网上商城项目(5)

本章概要 购物车 购物车状态管理配置购物车组件 结算页面用户管理 用户状态管理配置用户注册组件用户登录组件 17.8 购物车 在一个电商网站中&#xff0c;购物车在很多页面都需要用到&#xff0c;因此非常适合放在 Vuex 的 store 中进行集中管理。在本项目中&#xff0c;采…

【北大青鸟天府校区的Java专业怎么样?】

北大青鸟天府校区是北大青鸟总部在西南投资兴办的重点校区&#xff0c;拥有极为强大的外部教育资源和就业资源&#xff0c;使咱们的学员能够享受更好、更完善的教学资源。超过23年的办学经验&#xff0c;培养了超过10000位学员&#xff0c;保障就业&#xff0c;学员就业率达到9…

[入门必看]数据结构2.3:线性表的链式表示

[入门必看]数据结构2.3&#xff1a;线性表的链式表示第二章 线性表2.3 线性表的链式表示知识总览2.3.1 单链表的定义2.3.2_1 单链表的插入删除2.3.2_2 单链表的查找2.3.2_3 单链表的建立2.3.3 双链表2.3.4 循环链表2.3.5 静态链表2.3.6 顺序表和链表的比较2.3.1 单链表的定义单…

JUC高级四:Java内存模型之JMM

JUC高级四:Java内存模型之JMM 1. 计算机硬件存储体系(JMM为什么诞生) 因为有这么多级的缓存(cpu和物理主内存的速度不一致的)&#xff0c;CPU的运行并不是直接操作内存而是先把内存里边的数据读到缓存&#xff0c;而内存的读和写操作的时候就会造成不一致的问题 在我们cpu寄存…

WebRTC开源库内部调用abort函数引发程序发生闪退问题的排查

目录 1、初始问题描述 2、使用Process Explorer工具查看到处理音视频业务的rtcmpdll.dll模块没有加载起来 3、使用Dependency Walker工具查看到rtcmpdll.dll依赖的库有问题 4、更新库之后Debug程序启动时就发生异常&#xff0c;程序闪退 5、VS调试时看不到有效的函数调用堆…

十大Python可视化工具,太强了

今天介绍Python当中十大可视化工具&#xff0c;每一个都独具特色&#xff0c;惊艳一方。 Matplotlib Matplotlib 是 Python 的一个绘图库&#xff0c;可以绘制出高质量的折线图、散点图、柱状图、条形图等等。它也是许多其他可视化库的基础。 import matplotlib.pyplot as p…

OpenCV入门(二十)快速学会OpenCV 19 对象测量

OpenCV入门&#xff08;二十&#xff09;快速学会OpenCV 19 对象测量1.对象测量2.多边形拟合3.计算对象中心作者&#xff1a;Xiou 1.对象测量 opencv 中对象测量包括&#xff1a; 如面积&#xff0c;周长&#xff0c;质心&#xff0c;边界框等。 弧长与面积测量&#xff1b; …

《LKD3粗读笔记》(4)进程调度

1、多任务 什么是多任务操作系统&#xff1f; 能同时并发地交互执行多个进程。注意是并发而不是并行。特别地&#xff0c;在多处理机机器上可以实现真正意义上的并行&#xff0c;因为它长了多个脑子多任务操作系统有哪些分类&#xff1f; 非抢占式多任务&#xff08;cooperati…

【云原生】Kubernetes(k8s)部署 MySQL+Dubbo+Nacos服务

一、说明二、部署 MySQL三、部署 Nacos四、部署 Dubbo 服务4.1. 创建镜像仓库的密钥4.2. 部署 provider 服务4.3. 部署 consumer 服务五、测试一、说明 本文介绍基于 Kubernetes(k8s) 环境集成阿里云 私有镜像仓库 来部署一套 Dubbo Nacos 的微服务系统&#xff0c;并使用 Ku…

7个最好的PDF编辑器,帮你像编辑Word一样编辑PDF

PDF 是具有数字思维的组织的重要交流工具。提供高效的工作流程和更好的安全性&#xff0c;可以创建重要文档并与客户、同事和员工共享。文档的布局已锁定&#xff0c;因此无论在什么设备上查看&#xff0c;格式都保持不变。这是让每个人保持一致的好方法——尤其是那些使用Micr…

C++中的引用

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【C】 接下来就要开始进行C的学习路线了&#xff0c;听说这块的内容稍微难一些&#xff0c;不过我相信只要自己好好学习&#xff0c;态度…

java与Spring的循环依赖

java与Spring的循环依赖一、循环依赖是什么有什么危害二、循环依赖在Spring中的体现和类型三、Spirng如何解决循环依赖四、总结一、循环依赖是什么有什么危害 什么是循环依赖 java中循环依赖用一张图来说就是下图&#xff1a;在对象的创建过程中多个对象形成了依赖闭环&#xf…

初识linux之管道

一、进程间通信的概念大家都知道&#xff0c;进程是具有独立性的&#xff0c;因为一个程序运行起来生成进程时&#xff0c;也会生成它的进程结构体&#xff0c;即PCB&#xff0c;然后然后通过进程结构体中的结构体指针找到它的虚拟地址空间&#xff0c;然后再通过它的页表映射到…

C语言——字符函数和字符串函数【详解】(一)

文章目录函数介绍1.strlen2.strcpy3. strcat4. strcmp5. strncpy6. strncat7. strncmp8. strstr函数介绍 求字符串长度 strlen 长度不受限制的字符串函数&#xff08;使用时不安全&#xff09; strcpy strcat strcmp 长度受限制的字符串函数介绍&#xff08;与长度不受限制函数…