📄 00000011.htm
字号:
对于基于TCP BSD socket的连接操作,TCP必须建立一个包括连接信息的TCP消 <BR>息,并将它送到目的IP。TCP消息包含与连接有关的信息,一个唯一标识的消息 <BR>开始顺序号,通过初始化主机来管理的消息大小的最大值,及发送与接收窗口 <BR>大小等等。在TCP内,所有的消息都是编号的,初始的顺序号被用来作为第一消 <BR>息号。Linux选用一个合理的随机值来避免恶意协议冲突。每一从TCP连接的一端 <BR>成功地传到另一端的消息要确认其已经正确到达。未确认的消息将被重传。发送 <BR>与接收窗口的大小是第一个确认到达之前消息的个数。消息尺寸的最大值与网络 <BR>设备有关,它们在初始化请求的最后时刻确定下来。如果接收端的网络设备的消 <BR>息尺寸最大值更小,则连接将以小的一端为准。应用程序发出连接请求后必须等 <BR>待目标应用程序的接受或拒绝连接的响应。TCPsock 期望着一个输入消息,它被 <BR>加入 tcp_listening_hash以便输入TCP消息能被指向这一 sock结构。TCP同时也 <BR>开始计时,当目标应用没有响应请求,则连出连接请求超时。 <BR> <BR>10.4.4 监听 INET BSD Socket <BR> <BR> socket与地址绑定后,能监听指定地址的连入连接请求。一个网络应用程序 <BR>能监听socket而不用先将地址与之绑定;在这个例子中,INET socket层找到一 <BR>个未用的端口号(对这一协议)并自动将它与socket绑定。监听socket函数将 <BR>socket状态设成 TCP_LISTEN,并做其它连入连接所需要的工作。 <BR> <BR> 对于UDP sockets,改变socket的状态就足够了,而TCP现在加了socket的 <BR>sock数据结构到两个hash表中并激活, tcp_bound_hash 表和 tcp_listening_hash <BR>表。这两个表都通过一个基于IP端口号的hash函数来索引。 <BR> <BR> 无论何时,一个激活的监听socket接收一个连入的TCP连接请求,TCP都要 <BR>建立一个新的sock 结构来描述它。最终接收时,这个 sock结构将成为TCP连接 <BR>的底层。它也复制包含连接请求的 sk_buff,并将它放到监听 sock 结构的 <BR>receive_queue 中排队。复制的 sk_buff包含一个指向新建立的 sock 结构的指针。 <BR> <BR>10.4.5 接收连接请求 <BR> <BR> UDP不支持连接的概念,接收INET <BR>socket连接请求只适用于TCP协议,一个监听socket接收操作从原始的监听socket <BR>中复制新的socket结构。接收操作透过支持的协议层,本例是INET,来接收任何连 <BR>入连接请求。如果下层协议,如UDP,不支持连接,INET协议层接收操作将失败。 <BR>否则接收操作透过真实协议层,本例是TCP。接收操作可以是阻塞或非阻塞。在非 <BR>阻塞情况下,如果没有连入连接可接收,则接收操作失败,新建的socket结构被废 <BR>弃。在阻塞情况下,网络应用程序执行接收操作将加上一个等待队列并将之挂起, <BR>直到接收到TCP连接请求。当接收一个连接请求后,包含请求的sk_buff 被废弃, <BR>并且 sock 数据结构返回到INETsocket层,在那与一个新的更早建立的socket结构 <BR>连接。新socket文件描述符(fd)号返回给网络应用程序,然后,应用程序就能在 <BR>socket操作中将这一文件描述符用于新建立的INET BSD socket。 <BR> <BR>10.5 IP层 <BR> <BR>10.5.1 Socket 缓存 <BR> <BR> 每一层协议用另外层提供的服务,这样使用多层网络协议会有一个问题:每 <BR>个协议都要在传送数据时都要加上协议头和协议尾,而数据到达时又要将之去掉。 <BR>这样,在不同的协议间要有数据缓存,每一层需要知道特定协议的头和尾放在哪个 <BR>位置。一个解决办法就是在每一层中都拷贝缓存,但这样做效率就很低。Linux用 <BR>socket缓存或者说 sk_buffs 来在协议层与网络设备驱动之间交换数据。 <BR>sk_buffs 包括指针和字段长度,这样每个协议层就可以通过标准的函数或“方法” <BR>来操作应用程序数据。 <BR> <BR> <BR> <BR>sk_buff 数据结构;每个 sk_buff 有一个数据块与之相连。 <BR>sk_buff 有四个指针,这些指针 用来操作和管理socket缓存的数据: <BR> <BR>head 指向内存中数据区的开头。这一指针在 sk_buff和其相关的数据 <BR> 块分配时就固定了。 <BR>data 指向当前协议数据的开头。这一指针是随当前拥有 sk_buff的是 <BR> 哪个协议层而变化的。 <BR>tail 指向当前协议数据的结尾。同样,这一指针也是随当前拥有 sk_buff <BR> 的是哪个协议层而变化的。 <BR>end 指向内存中数据区的结尾。这一指针在 sk_buff和其相关的数据块 <BR> 分配时固定。 <BR> <BR> len 和 truesize这两个字段分别用来描述当前协议包长度和数据 <BR>缓存总体长度。 sk_buff处理代码提供标准的操作来向应用程序增加和 <BR>移除协议头和协议尾。这就可以安全地操作sk_buff 中的 data , tail <BR>和 len 字段。 <BR> <BR>push 它把 data 指针指向数据区的开始并增加 len。用于在要传输的数 <BR>据开始处增加协议头。 <BR> <BR>pull 它把 data 指针从数据区的开始处移到数据区的结尾处,并减小 <BR>len。用于在已接收的数据开始处移除协议头。 <BR> <BR>put 它把 tail 指针指向数据区的结尾处,并增加 len。用于在要传输 <BR>的数据结尾处增加数据或协议信息。 <BR> <BR>trim 它把 tail 指针指向数据区的开始处,并减小 len。用于在已接 <BR>收的数据尾移除数据或协议信息。 <BR> <BR> sk_buff 结构还包含了用于一些指针,用于在处理过程中存入 sk_buff <BR>的双连接环路列表。通用sk_buff例 程可以将 sk_buff加入到这些列表 <BR>的前面或后面,也可以删除它们。 <BR> <BR>10.5.2 接收IP包 <BR> <BR> 第 dd-chapter 章描述了Linux的网络设备是如何置入内核并初始化的。 <BR>一系列device 数据结构在 dev_base 表中相互连接起来。每个 device结构 <BR>描述了它的设备并提供回调例程,当需要网络驱动来执行工作时,网络协议 <BR>层调用这些例程。这些函数与传输的数据及网络设备地址紧密相关。当一个 <BR>网络设备从网上接收包时,它必须将接收的数据转换成sk_buff 结构。这些 <BR>sk_buff 则被网络驱动加入到了 backlog 队列中。 <BR> <BR> 如果 backlog 队列太长,则丢弃接收的sk_buff。准备好要运行时,网 <BR>络底层将被设置标志。 <BR> <BR> 当网络底层按计划开始运行后,处理 backlog队列之前,任何等待着被 <BR>传输的网络包都由它来处理。 sk_buff决定哪些层处理被接收的包。 <BR> <BR> Linux网络层初始化时,每一协议通过将 packet_type 结构加入到 <BR>ptype_all列表或ptype_base hash表中来 注册它自己。packet_type结构包 <BR>含了协议类型,一个指向网络设备的指针,一个指向协议的接收数据处理例 <BR>程的指针,最后还包括一个指向列表链或hash链中下一个packet_type 结构 <BR>的指针。ptype_all链用于监听从网络设备上接收的所有包,通常不使用它。 <BR>ptype_base hash表是被协议标识符弄乱的,用于决定哪个协议将接收传入的 <BR>网络包。网络底层通过两个表中的一个或多个packet_type 项来匹配传入 <BR>sk_buff的协议类型。协议可以和多于一个的项相匹配,如在监听网上所有的 <BR>传输时要复制多个sk_buff 。 sk_buff 将通过被匹配协议处理例程。 <BR> <BR>10.5.3 发送IP包 <BR> <BR> 应用程序交换数据时要传输包,否则由网络协议在建立连接或支持一个 <BR>已建立的连接时来生成。无论数据是由哪种方法生成的,都要建立一个 sk_buff <BR>来包含数据,当通过协议层时,这些协议层会加上各种头。 <BR> <BR> sk_buff需要通过网络设备传输。首先协议,如IP,需要确定是哪个网 <BR>络设备在用。这有赖于包的最佳路由。对于通过modem连入一个简单网络, <BR>如通过PPP协议,的计算机来说,路由的选择是很简单的。包应该通过本地 <BR>环路设备发送给本地主机,或发送给PPPmodem连接的网关。对于连在以在网 <BR>上的计算机来说,连接在网络上的计算机越多,路由越复杂。 <BR> <BR> 对于每一个被传输的IP包,IP用路由表来为目的IP地址解析路由。从路 <BR>由表中成功地找到目的IP时将返回一个描述了要使用的路由的rtable 结构。 <BR>这包括要用到的源IP地址,网络 device结构的地址,有时还有预建立的硬件 <BR>头。这些硬件头是网络设备特定的,包含了源和目的的物理地址和其它的特 <BR>定媒体信息。如果网络设备是一个以太网设备,并且源和目的地址应是物理 <BR>的以太网地址。硬件头在路由的时候会缓存起来,因为必须将它加到每一个 <BR>要传输的IP包中。硬件头包含的物理地址要用ARP协议来解析。传出的包在地 <BR>址被解析后才会发出。解析了地址后,硬件头被缓存起来以便以后的IP包在 <BR>使用这一接口时不需要再使用ARP。 <BR> <BR>10.5.4 数据分块 <BR> <BR> 每个网络设备都有一个包大小的最大值,发送或接收数据包不能比这一 <BR>值大。IP协议允许将数据分成更小单元以便网络设备能处理。IP协议头有分 <BR>块字段,它里面包含了一个标志和分割偏移量。 <BR> <BR> 当IP包准备要传输时,IP找到网络设备来将IP包发送出去。这个设备是 <BR>从IP路由表中找到的。每一device 结构中有一项 mtu,用来描述最大传输单 <BR>元(以字节为单位)。如果设备的 mtu比要传输的IP包的包大小要小,则IP <BR>包必须被分割成更小的单元。每一单元用一个sk_buff结构来表征;它的IP头 <BR>会被做上标记以标识它是一个分块了的包,其中还包含分割偏移量。最后一 <BR>个包被标识为最后IP单元。如果在分块过程中,IP不能分配sk_buff ,则传 <BR>输失败。 <BR> <BR> 接收IP分块单元要比发送它们要麻烦一些,因为这些IP单元可能以任何 <BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -