电商网站开发商,教育网站改造方案,乐山企业品牌网站建设,四川省安全社区建设网站关于对 Socket 的认识#xff0c;大致分为下面几个主题#xff0c;Socket 是什么#xff0c;Socket 是如何创建的#xff0c;Socket 是如何连接并收发数据的#xff0c;Socket 套接字的删除等。
Socket 是什么以及创建过程
一个数据包经由应用程序产生#xff0c;进入到…关于对 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但是远程端口还未知此时的状态是 LISTENINGLISTENING 表示应用程序已经打开正在等待与远程主机建立连接关于各种状态之间的转换大家可以阅读笔者的这篇文章 TCP 丫的终于来了最后一个元组是 PID即进程标识符PID 就像我们的身份证号码能够精确定位唯一的进程。
现在你可能对 Socket 有了一个基本的认识现在喝口水休息一下让我们继续探究 Socket。
现在我有个问题Socket 是如何创建的呢Socket 是和应用程序一起创建的。应用程序中有一个 socket 组件在应用程序启动时会调用 socket 申请创建套接字协议栈会根据应用程序的申请创建套接字首先分配一个套接字所需的内存空间这一步相当于是为控制信息准备一个容器但只有容器并没有实际作用所以你还需要向容器中放入控制信息如果你不申请创建套接字所需要的内存空间你创建的控制信息也没有地方存放所以分配内存空间放入控制信息缺一不可。至此套接字的创建就已经完成了。
套接字创建完成后会返回一个套接字描述符给应用程序这个描述符相当于是区分不同套接字的号码牌。根据这个描述符应用程序在委托协议栈收发数据时就需要提供这个描述符。
资料直通车最新Linux内核源码资料文档视频资料https://docs.qq.com/doc/DTmFTc29xUGdNSnZ2
内核学习地址Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈https://ke.qq.com/course/4032547?flowToken1040236
套接字连接
套接字创建完成后最终还是为数据收发服务的在数据收发之前还需要进行一步 connect也就是建立连接的过程。这个连接并不是真实的连接用一根水管插在两个电脑之间。 而是应用程序通过 TCP/IP 协议标准从一个主机通过网络介质传输到另一个主机的过程。
套接字刚刚创建完成后还没有数据也不知道通信对象。在这种状态下即使你让客户端应用程序委托协议栈发送数据它也不知道发送到哪里。所以浏览器需要根据网址来查询服务器的 IP 地址做这项工作的协议是 DNS查询到目标主机后再把目标主机的 IP 告诉协议栈至此客户端这边就准备好了。
在服务器上与客户端一样也需要创建套接字但是同样的它也不知道通信对象是谁所以我们需要让客户端向服务器告知客户端的必要信息IP 地址和端口号。
现在通信双方建立连接的必要信息已经具备只欠一股东南风了。通信双方收到数据之后还需要一块位置来存放这个位置就是缓冲区它是内存的一部分有了缓冲区就能够进行数据的收发操作了。
OK现在客户端想要给服务器发送一条数据该进行哪些操作呢
首先客户端应用程序需要调用 Socket 库中的 connect 方法提供 socket 描述符和服务器 IP 地址、端口号。
connect(描述符、服务器IP地址和端口号)这些信息会传递给协议栈中的 TCP 模块TCP 模块会对请求报文进行封装再传递给 IP 模块进行 IP 报文头的封装然后传递给物理层进行帧头封装之后通过网络介质传递给服务器服务器上会对帧头、IP 模块、TCP 模块的报文头进行解析从而找到对应的套接字套接字收到请求后会写入相应的信息并且把状态改为正在连接。请求过程完成后服务器的 TCP 模块会返回响应这个过程和客户端是一样的如果大家不太清楚报文头的封装过程可以阅读笔者的这篇文章 TCP/IP 基础知识总结
在一个完整的请求和响应过程中控制信息起到非常关键的作用具体的作用我们后面会说。
SYN 就是同步的缩写客户端会首先发送 SYN 数据包请求服务端建立连接。ACK 就是相应的意思它是对发送 SYN 数据包的响应。FIN 是终止的意思它表示客户端/服务器想要终止连接。
由于网络环境的复杂多变经常会存在数据包丢失的情况所以双方通信时需要相互确认对方的数据包是否已经到达而判断的标准就是 ACK 的值。通信双方连接的建立会经过三次握手流程对三次握手详细的介绍可以阅读笔者的这篇文章 TCP 基础知识当所有建立连接的报文都能够正常收发之后此时套接字就已经进入可收发状态了此时可以认为用一根管理把两个套接字连接了起来。当然实际上并不存在这个管子。建立连接之后协议栈的连接操作就结束了也就是说 connect 已经执行完毕控制流程被交回给应用程序。
收发数据
当控制流程从 connect 回到应用程序之后接下来就会直接进入数据收发阶段数据收发操作是从应用程序调用 write 将要发送的数据交给协议栈开始的协议栈收到数据之后执行发送操作。
协议栈不会关心应用程序传输过来的是什么数据因为这些数据最终都会转换为二进制序列协议栈在收到数据之后并不会马上把数据发送出去而是会将数据放在发送缓冲区再等待应用程序发送下一条数据。
为什么收到数据包不会直接发送出去而是放在缓冲区中呢因为只要一旦收到数据就会发送就有可能发送大量的小数据包导致网络效率下降。所以协议栈需要将数据积攒到一定数量才能将其发送出去。至于协议栈会向缓冲区放多少数据这个不同版本和种类的操作系统有不同的说法不过所有的操作系统和种类都会遵循下面这几个标准
第一个判断要素是每个网络包能够容纳的数据长度判断的标准是 MTU它表示的是一个网络包的最大长度。最大长度包含头部所以如果单论数据区的话就会用 MTU - 包头长度由此的出来的最大数据长度被称为 MSS。另一个判断标准是时间当应用程序产生的数据比较少协议栈向缓冲区放置数据效率不高时如果每次都等到 MSS 再发送的话可能因为等待时间太长造成延迟在这种情况下即使数据长度没有到达 MSS也应该把数据发送出去。
协议栈并没有告诉我们怎样平衡这两个因素如果数据长度优先那么效率有可能比较低如果时间优先那又会降低网络的效率。
经过了一段时间。。。。。。 假设我们使用的是长度有限法则此时缓冲区已满协议栈要发送数据了协议栈刚要把数据发送出去却发现无法一次性传输这么大数据量相对的的数据那怎么办呢
在这种情况下发送缓冲区中的数据就会超过 MSS 的长度发送缓冲区中的数据会以 MSS 大小为一个数据包进行拆分拆分出来的每块数据都会加上 TCPIP以太网头部然后被放进单独的网络包中。 到现在网络包已经准备好发往服务器了但是数据发送操作还没有结束因为服务器还未确认是否已经收到网络包。因此在客户端发送数据包之后还需要服务器进行确认。
TCP 模块在拆分数据时会计算出网络包偏移量这个偏移量就是相对于数据从头开始计算的第几个字节并将算好的字节数写在 TCP 头部TCP 模块还会生成一个网络包的序号SYN这个序号是唯一的这个序号就是用来让服务器进行确认的。
服务器会对客户端发送过来的数据包进行确认确认无误之后服务器会生成一个序号和确认号ACK并一起发送给客户端客户端确认之后再发送确认号给服务器。
我们来看一下实际的工作过程 首先客户端在连接时需要计算出序号初始值并将这个值发送给服务器。接下来服务器通过这个初始值计算出 确认号并返回给客户端。初始值在通信过程中有可能会丢弃因此当服务器收到初始值后需要返回确认号用于确认。同时服务器也需要计算出从服务器到客户端方向的序号初始值并将这个值发送给客户端。然后客户端也需要根据服务器发来的初始值计算出确认号发送给服务器至此连接建立完成接下来就可以进入数据收发阶段了。
数据收发阶段中通信双方可以同时发送请求和响应双方也可以同时对请求进行确认。
请求 - 确认机制非常强大通过这一机制我们可以确认接收方有没有收到某个包如果没有收到则重新发送这样一来但凡网络中出现的任何错误我们都可以即使发现并补救。
网卡、集线器、路由器都没有错误补救机制一旦检测到错误就会直接丢弃数据包应用程序也没有这种机制起作用的只是 TCP/IP 模块。
由于网络环境复杂多变所以数据包会存在丢失情况因此发送序号和确认号也存在一定规则TCP 会通过窗口管理确认号我们这篇文章不再赘述大家可以阅读笔者的这篇文章 TCP 基础知识 来寻找答案。
断开连接
当通信双方不再需要收发数据时需要断开连接。不同的应用程序断开连接的时机不同。以 Web 为例浏览器向 Web 服务器发送请求消息Web 服务器再返回响应消息这时收发数据就全部结束了服务器可能会首先发起断开响应当然客户端也有可能会首先发起谁先断开连接是应用程序做出的判断与协议栈无关。 无论哪一方发起断开连接的请求都会调用 Socket 库的 close 程序。我们以服务器断开连接为例服务器发起断开连接请求协议栈会生成断开连接的 TCP 头部其实就是设置 FIN 位然后委托 IP 模块向客户端发送数据与此同时服务器的套接字会记录下断开连接的相关信息。
收到服务器发来 FIN 请求后客户端协议栈会将套接字标记为断开连接状态然后客户端会向服务器返回一个确认号这是断开连接的第一步在这一步之后应用程序还会调用 read 来读取数据。等到服务器数据发送完成后协议栈会通知客户端应用程序数据已经接收完毕。
只要收到服务器返回的所有数据客户端就会调用 close 程序来结束收发操作这时客户端会生成一个 FIN 发送给服务器一段时间后服务器返回 ACK 号至此客户端和服务器的通信就结束了。
删除套接字
通信完成后用来通信的套接字就不再会使用了此时我们就可以删除这个套接字了。不过这时候套接字不会马上删除而是等过一段时间再删除。
等待这段时间是为了防止误操作最常见的误操作就是客户端返回的确认号丢失至于等待多长时间和数据包重传的方式有关。