网络编程——网络分层模型及一些你应该知道的TCP等网络基础常识

引言

相信作为Android 开发者,网络通信相关的开发肯定不会陌生,尤其是互联网APP离开网络则失去了赖以生存的土壤,虽然作为APP开发者我们使用到的更多只是应用层的协议——HTTP、HTTPs、Sockect等,而且得益于我们成熟的Java语言以及第三方开源库,仅仅只需要几句代码就可以完成与服务端之间的通信,但是我一向是尽量遵循知其然,而知其所以然,尤其是最近在阅读OKHttp的源码和想要实现自定义通信协议的时,发现很多以前在计算机网络课程中被忽略了细节原来那么重要,于是再去翻开《计算机网络(第7版)》重温了一遍相关知识,结合自己的经验,总结了一些我认为是一些必须要了解的常识,也可以学习借鉴它的设计思想用到自己的以后的开发中。

一、计算网络体系结构(网络分层模型)概述

教科书中把计算机网络的各层及其协议栈称为网络的体系结构,通俗理解为了易于实现和维护,把一次网络通信任务划分为若干个子任务并分配到对应的子层,每层独立完成各自负责的任务,各层之间独立又互相协同完成一次网络通信任务(或许理解有点些许偏差),如下图所示(实际应用中还是TCP/IP四层体系结构
在这里插入图片描述

  • TCP/IP的四层协议中,TELNET、FTP、SMTP、POP3、IMAP、HTTP、RTMP等这种协议就是运行中应用层
  • TCP/IP的四层协议中,传输层是用于数据的传输的,典型协议就是TCP、UDP协议
  • TCP/IP的四层协议中,网络层是整个TCP/IP协议的核心,这些都是由操作系统实现的TCP/IP协议栈支持的,用于将目标数据发往网络或主机,所以IPV4 、IPV6协议运行在这层

由此对于我们开发者来说只能在应用层做文章(所谓的自定义协议其实也是定义应用层的协议,运输层之下的改动起来及其困难,因为这些逻辑是由操作系统的TCP/IP协议栈去实现的,终端要想接入互联网必须是实现了TCP/IP协议栈),应该重点关注应用层和网络层。

二、五层协议

1、应用层(Application Layer)

应用层是网络体系结构中的最高层,其任务是通过应用进程(主机中正在运行的程序)间的交互完成特定网络通信,对于不同的网络通信需要持有不同的协议,比如支持万维网的HTTP协议、支持邮件的SMTP、支持文件传送的FTP等等,应用层的基本交互单元被称为报文(message)。

2、传输层(Transport Layer)

运输层主要负责向两个主机进程之间的通信提供数据传输服务,主机的应用进程利用该服务传送应用层报文,协议为TCPUDP

2.1、传输控制协议TCP(Transmission Control Protocol)

提供面向连接的、可靠的数据传输服务,其数据传输的单位是报文段(segment)

2.2、用户数据报协议UDP(User Datagram Protocol)

提供无连接的,尽最大努力的数据传输服务(无法保证数据传输的可靠性,会发生丢包),其数据传输的基本单位是用户数据报

3、网络层(Network Layer)

在发送数据时,网络层负责把运输层产生的报文段或者用户数据报封装成或者分组(由于网络层使用IP协议,所以又叫为IP 数据报 或数据报)进行传送,除此之外还负责选择出合适的路由,使源主机运输层所传下来的IP数据报通过“网络”找到目的主机。

4、数据链路层(Data Link Layer)

两台主机的数据传输是在一段一段的链路上传送的,在两个相邻节点间的链路传送数据时,链路层负责将网络层传下来的IP数据报组装成并且在相邻链路上传送(每一帧包括数据和必要的控制信息);在接收数据时通过控制信息接收端知道一个帧从哪个比特开始到哪个比特结束从而取出数据部分并上交到网络层。

5、物理层(Physical Layer)

在物理层上是以比特为基本单位进行传输的,所有的数据都转为0或者1的比特流,这一层主要涉及到的计算机通信方面的专业知识,略…

三、Socket

Socket 又称套接字,实现了TCP/IP协议,应用程序可以通过Sockect向网络发出请求或者应答网络请求。在Java中,Socket和ServerSocket类位于java.net包下。ServerSocket用于服务端,Socket则是建立网络连接时使用的,连接成功时,应用程序两端都会产生一个Scoket实例,操作这个实例,完成所需的会话,对于同一网络连接来说,套接字是平等的,无任何差别并不会因为在服务器端或者客户端而产生不同的级别,所以不管是Socket还是 ServerSocket它们的真正工作都是通过Socket类及其子类完成的。
在这里插入图片描述

四、TCP协议

1、TCP/IP协议概述

众所周知,世界上所有HTTP通信都是由TCP/IP承载的,而TCP/IP协议是全球计算机及网络设备使用的一种最常见的分组交换网络分层协,TCP 是以太网协议(最底层的以太网协议Ethernet规定了电子信号如何组成数据包packet,解决了子网内部的点对点通信)和 IP 协议的上层协议,同时也是应用层协议的下层协议。因此理论上,客户端应用程序打开一条TCP连接就可以连接到世界上任务服务器上的应用程序。而IP 协议只是一个地址协议,并不保证数据包的完整。如果路由器丢包(比如缓存满了,新进来的数据包就会丢失),就需要发现丢了哪一个包,以及如何重新发送这个包,这就要依靠 TCP 协议。简而言之,TCP 协议的作用是保证数据通信的完整性和可靠性,防止丢包。

2、TCP 包的结构

以太网数据包(packet)的大小是固定的,最初是1518字节,后面增加到1522字节甚至更大。其中 1500 字节是负载(payload),22字节是头信息(head)。IP 数据包在以太网数据包的负载里面,它也有自己的头信息,最少需要20字节,所以 IP 数据包的负载最多为1480字节。
在这里插入图片描述
如上所示IP 数据包在以太网数据包里面,而TCP 数据包则在 IP 数据包的负载里面且它的头信息最少也需要20字节,因此 TCP 数据包的最大负载是 1480 - 20 = 1460 字节。再加上 IP 和 TCP 协议往往有额外的头信息,所以 TCP 负载实际为1400字节左右。因此,一条1500字节的信息需要两个 TCP 数据包。

3、TCP的标志控制

标志位说明
ACK确认确认编号(Acknowledgement Number)栏有效。大多数情况下该标志位是置位的。TCP报头内的确认编号栏内包含的确认编号(w+1,Figure1)为下一个预期的序列编号,同时提示远端系统已经成功接收所有数据。
PSH推该标志置位时,接收端不将该数据进行队列处理,而是尽可能快将数据转由应用处理。在处理 telnet 或 rlogin 等交互模式的连接时,该标志总是置位的。
RST复位复位标志有效。用于复位相应的TCP连接。
SYN同步同步序列编号(Synchronize Sequence Numbers)栏有效。该标志仅在三次握手建立TCP连接时有效。它提示TCP连接的服务端检查序列编号,该序列编号为TCP连接初始端(一般是客户端)的初始序列编号。在这里,可以把TCP序列编号看作是一个范围从0到4,294,967,295的32位计数器。通过TCP连接交换的数据中每一个字节都经过序列编号。在TCP报头中的序列编号栏包括了TCP分段中第一个字节的序列编号。
FIN结束带有该标志置位的数据包用来结束一个TCP回话,但对应端口仍处于开放状态,准备接收后续数据。服务端处于监听状态,客户端用于建立连接请求的数据包(IP packet)按照TCP/IP协议堆栈组合成为TCP处理的分段(segment)。
分析报头信息TCP层接收到相应的TCP和IP报头,将这些信息存储到内存中。
检查TCP校验和(checksum)标准的校验和位于分段之中(Figure2)。如果检验失败,不返回确认,该分段丢弃,并等待客户端进行重传。
查找协议控制块(PCB{})TCP查找与该连接相关联的协议控制块。如果没有找到,TCP将该分段丢弃并返回RST。(这就是TCP处理没有端口监听情况下的机制) 如果该协议控制块存在,但状态为关闭,服务端不调用connect()或listen()。该分段丢弃,但不返回RST。客户端会尝试重新建立连接请求。
建立新的socket当处于监听状态的socket收到该分段时,会建立一个子socket,同时还有socket{},tcpcb{}和pub{}建立。这时如果有错误发生,会通过标志位来拆除相应的socket和释放内存,TCP连接失败。如果缓存队列处于填满状态,TCP认为有错误发生,所有的后续连接请求会被拒绝。这里可以看出SYN Flood攻击是如何起作用的。
丢弃如果该分段中的标志为RST或ACK,或者没有SYN标志,则该分段丢弃。并释放相应的内存。
URG紧急(The urgent pointer)标志有效,紧急标志置位,

4、SEQ

前文所述,以一个包的大小为1400字节为例,那么一次性发送大量数据,就必须分成多个包。(发送一个 10MB 的文件,就需要发送7100多个TCP包)因此为了方便接收的一方能按照顺序还原或者在发生丢包的时候明确丢了哪个包,在发送的时候,TCP 协议会为每个包进行编号(sequence number,简称 SEQ),通常第一个包的编号是一个随机数,而第二个包则会根据指定算法在第一个包的基础上计算得出第二个包的SEQ,假定第一个包的负载长度是100字节,那么可以推算出下一个包的编号应该是101,这样子每个数据包SEQ都可以得到两个编号——自身的编号以及下一个包的编号。接收方由此知道,应该按照什么顺序将它们还原成原始文件。

5、TCP 数据包的组装

在收到 TCP 数据包以后,组装还原工作都是由操作系统内核去完成的。应用程序不会也不能直接处理 TCP 数据包。所以对于应用程序来说,不用关心数据通信的细节,除非线路异常,收到的总是完整的数据。而应用程序只需要按照“约定的格式”进行解析,TCP本身 并没有提供任何机制去表示原始文件的大小,这由应用层的协议来规定。比如我们常用的HTTP 协议就有一个头信息Content-Length,表示信息体的大小。对于操作系统来说只做一件事——就是持续地接收 TCP 数据包,将它们按照顺序组装好,一个包都不少(操作系统不会去处理 TCP 数据包里面的数据)一旦组装好 TCP 数据包,就把它们转交给应用程序,TCP 数据包里面有一个端口(port)参数就是用来指定转交给监听该端口的应用程序。以浏览器为例,浏览器收到组装好的原始数据,就会根据 HTTP 协议的Content-Length字段正确读出一段段的数据,然后解析并进行处理,一次 HTTP 通信会包含多个 TCP 通信

6、ACK

理论上服务器发送数据包越快越好,最好一次性全发出去。但是发得太快或者带宽小、路由器过热、缓存溢出等其他客观因素影响,就有可能丢包。最理想的状态是,在线路允许的情况下达到最高速率。但是如何得知对方线路的理想速率是多少呢?答案就是慢慢试。TCP 协议为了做到效率与可靠性的统一,设计了一个慢启动(slow start)机制。开始的时候,发送得较慢,然后根据丢包的情况进行调整(如果不丢包,就加快发送速度;如果丢包,就降低发送速度)。在Linux 内核里面设定了(常量TCP_INIT_CWND),刚开始通信的时候,发送方一次性发送10个数据包,然后停下来,等待接收方的确认,再继续发送。默认情况下,接收方每收到两个 TCP 数据包,就要发送一个确认消息ACK(全称acknowledgement),其中每个ACK 包含两个信息:

  • 期待要收到下一个数据包的SEQ编号
  • 接收方的接收窗口的剩余容量

发送方接到ACK消息,就得知了这两个信息,再加上自己已经发出的数据包的最新编号,就会推测出接收方大概的接收速度,从而降低或增加发送速率。这被称为"发送窗口",这个窗口的大小是可变的。

由于 TCP 通信是双向的,所以双方都需要发送 ACK。因为两方的窗口大小,很可能是不一样的。而且 ACK 只是很简单的几个字段,通常与数据合并在一个数据包里面发送。

ps:下一篇总结下TCP的三次握手和“四”次挥手。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

CrazyMo_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值