首页 >程序人生

TCP/IP 系列之初印象

2017-03-16 06:21 编辑: suiling 分类:程序人生 来源:MrPeakTech

作者:MrPeakTech(公众号)

TCP/IP 系列之初印象

如果有人问我推荐技术类书籍,我个人首选一定是【TCP/IP 详解】原版。这种大部头的经典之作里所包含的知识,说是浩如烟海也不为过,每次重新翻起总会有一些新的体悟和收益。经典的好处在于,它描述的一般是通用的计算机问题,可以帮助读者形成通用的计算机思维,而不局限于某一领域或某一场景,常看常新。

当然这本书的厚度也到了让人望而生畏的程度,如果着急短时间内啃完,多半会由于无法完成目标的沮丧半途而废。我在前面的文章中也提到过,获取知识的时候容易陷入「越看越饿」的焦虑感,这种情形下应该放慢节奏,指定长期的阅读计划,放眼于较长时间跨度的收益。书和文章并不是越多越好,看了多少,能消化多少,是个人的事情,没有比较的意义。知识营养,点滴皆是财富,积跬步而至千里方为正途。

一般来说,看一手技术书籍比看技术文章更有意义。但好的技术文章,如果能做到高屋建瓴,提纲挈领,对于初学者入门也是具备价值的。直接啃英文原版,没有交流和正向反馈,最后能否读完十分考验读者的意志力。

我打算写一个 TCP/IP 的系列文章,目的在于以通俗易懂的语言和比喻,让读者能预先形成一个初步的知识大纲,之后再能有针对性的逐个知识模块去攻破。即使最后无法通读【TCP/IP 详解】全本,也能对网络协议栈形成大致完整的框架理解,以后在项目中遇到问题,至少知道如何下手,从哪个方向去深入突破。感兴趣的读者在阅读这个系列文章之时,最好能同时翻阅下【TCP/IP 详解】,疗效更佳。

打算写这个系列的另一个原因,是我在平时阅读英文书籍的过程中,深感中西方思维方式的差别,会让一些原本简单清晰的概念模型变得晦涩费解,一些关键点如果无法攻克,会阻碍进一步学习的兴趣。另外,我会以 iOS 程序员的视角,尽量结合一些实际应用中的具体场景来讲解,不至于太过枯燥,感觉不到学习的意义所在。

这个系列会在后续的时间里,夹杂着其他文章一起写,可能会耗时较长,又或者会一直写下去。我现在感觉公众号越来越像「读者群」,留下来坚持读文章的多半和我「品性相投」,不论我写技术还是写心得都会坚持阅读。于我,写文章是一件乐事,于读者,看文章多少有些收获。一场双方都觉得愉快的聊天岂不是妙事一件。

接下来进入正题。

TCP/IP 初印象

当我们聊 TCP/IP 的时候,一般是在说整个网络协议栈。也就是大家熟知的 OSI 七层模型。我们先抛开七层协议和三次握手这些生硬的知识点,简单聊聊网络协议栈到底是做什么的。

网络协议要解决的问题很简单,打个比方,就是让身处不同地方的两个人,能完成聊天通讯。无论我们谈及哪一方面的网络知识点,最后都不会脱离这个最基本的场景模型。

首先我们需要分清楚流和包的概念,这也是不少初学者容易混淆的问题。当我们以 iPhone 上的 App 客户端,向我们的 Server 发数据的时候,我们可以用 Charles、MITMProxy 这类工具抓包,但我们同时也知道 TCP 是基于流的协议,这二者的区别和联系在哪呢?

0 和 1 是计算机世界的基础粒子,大量的 0 和 1 组合在一起就形成了一个流(Stream),客户端向服务器发送数据的时候,说白了就是一堆 0 和 1 的组合。一次完整的 http 会话是建立在一个 TCP 连接之上,这个 TCP 连接的生命周期内所有发送的数据最后可以看做是一个流。而在这个流里,我们可以按照某种规则把它切割成一个个的包(packet)。比如 TCP 三次握手里就包含了 SYN,SYN+ACK,ACK 三个包,而这三个包,不过是整个 TCP Stream 最开始的部分数据而已。

所以简单来说,一个 TCP 连接里,是既有流的概念,又有包的存在,有些问题场景下会谈论流,另一些则会说起包,端看具体的场景如何。

在开始学习 TCP/IP 之前,最好能在脑中形成一幅 Gif 动态图像,客户端和服务器之间有两根管道,一根上行(从客户端到服务器),一根下行(从服务器到客户端),管道里流动着无数的 0 和 1,有时候管道里是满的,有时候管道里则空空如也,每次发送数据,都会有大量的 0 和 1 从一端涌向另一端。

有了这个基本的抽象理解,就可以进一步填补一些图像细节。

比如 0 和 1 是以什么形式流动的呢?这个在不同时代里,所用的物理硬件不同,原理自然也不同,现在大多用的是光纤,光纤里的 0 和 1 流动可以用下图表示:

QQ截图20170315143849.png

物理层离应用层较远,深入学习的意义无法立竿见影,遇到实在费解的,可以先跳跃式的阅读,但有些基础的细节还是需要明白的。比如,我们知道光速大致是 300000 km/s ,而光在光纤中的物理传播速度大概是光速的 ?,这个知识点可以帮助我们大概估算一个 TCP 连接中的 RTT 值。RTT 即 Round Trip Time,表示一个包从发送方发出,到收到接收方的反馈,一共耗时多少。RTT 值反应的网络延时的状况,在很多场景下分析问题都有意义,比如 TCP 重发机制中的 RTO 就跟 RTT 有关,后面的文章我们会细说。一些知识点往往是环环相扣的,跳过的内容过多,后面学习起来会更费解。

延迟

再说下延迟的概念,延迟和带宽是我们谈及网络状况的两个重要指标。延迟看上去很好理解,延迟对应的英文术语是 Latency,不少人会觉得延迟主要跟通讯双方的物理距离相关,距离越远,自然延迟越高。其实网络延迟严格来说,是由两部分组成的,其一是 Transmission Delay,另一个是 Propagation Delay。这里出现了一个新单词 Delay,Delay 和 Lantency 的含义在中文语境里很难区分,但在英文语境下,二者的使用却是有明显的场景差别的,这也是为什么读英文原版很重要,可以让被描述的问题细节更清晰。

先说 Transmission Delay。计算机世界里的 0 和 1, 最后要能在光纤中传输,需要在 Physical Layer 将数字信号转化为物理信号,这个转化也是存在速度瓶颈的,我们用 Rate (bits/seconds) 来描述这个转化的速度,Rate 表示每一秒钟里,硬件设备能将多少 bits 转化为光信号放入光纤中,那么 Transmission Delay 就可以用如下公式表示:

Transmission Delay = M / Rate

M 表示有多少 bits。

Transmission Delay 描述的是硬件转化信号的延迟,这一步里信号还没有正式进入光纤中传播,这种延迟和通讯双方的物理距离是没有关系的。

再来是 Propagation Delay。这才是大部分人所理解的传播延迟,和距离直接相关。也可以用如下公式表示:

Propagation Delay = L / S

L 表示两地的物理距离,S 表示光纤中的传播速度,即为 ? 光速。

最后我们才是所说的网络延迟:

Latency = Transmission Delay + Propagation Delay

像这种知识细节,如果不去看书,是很难形成准确认知的,这种细节丰满的认知不是快餐式技术文章可得,需要静下心来,花时间慢慢啃,一旦形成之后,就牢固的沉淀在记忆里,这样才算真正掌握了这方面的知识。

管道容量

再说个有意思的知识点。我们经常将网络通道比作自来水管道,这种比喻其实很贴切,网络管道和自来水管道一样,也是存在容量限制的。也就是说,光在光纤中传播的时候,会像自来水一样,填满整个光纤,光纤也存在管道容量的限制。有时候,光纤管道会变得很拥挤,一旦拥挤起来,有些包就要排队等待,延迟就会增加,丢包率也会随之上升,整个网络状况就会变差,这也是为什么 TCP 要做 Flow Control(流控),Flow Control 也是个很经典且有趣的问题,后面会专门写文章介绍。

网络管道的容量也是可以通过上面提到的元素,用公式计算的:

BD = Rate * Propagation Delay

大家可以思考下这个公式为什么会是这样。

好啦,序篇就到这,后面的文章里,我会逐个讲解一些关键且重要的知识点。一同进步吧!


TCP/IP 系列之 Header 篇

这是 TCP/IP 系列的第二篇,阅读目标是建立对网络包结构的初步认识。

上一篇里,我们提到一次完整的 TCP 会话其实是一个字节流,只不过我们可以按照一定的规则来切割这个字节流,从而划分出一个个的网络包。我们经常说抓包分析网络故障,这个抓包里所指的包在不同的语境下其实意义并不相同,首先我们得对包的构成形成具象的认知。

再看七层协议

刚毕业那会,大家都喜欢在简历上写「精通 TCP/IP 协议」,面试被问起时就必答三次握手和 OSI 七层模型,再多就说不出来了。问七层模型是什么就机械式的从上至下背诵一遍,这离精通还差了一本【TCP/IP 详解】。

我在完整的学习过一遍网络协议栈之后,深感所谓的七层模型是偏工业的说法,看到七层协议图之后,其实很难明白这七层是如何一层层互相构成的,更符合大脑感官的是另一种认知形式,是一种洋葱形的结构,层层叠叠互相包裹,可以用下图表示:

QQ截图20170315144027.png

左边是教材上的结构,右边是我所说的洋葱式的结构。如果以一个 HTTP 请求为例,右图中 Application 部分就代表我们用 Charles 抓包时所感知的部分,这一部分要最后转化为光信号,在光纤中传输,还需要经过一层层的转化,这个转化过程说白了,就是在每一层加上一个 header。

  • Application 层(HTTP)的数据在经过传输层(TCP Layer)的时候,会加上 TCP 的 header,成为一个 TCP Segment。

  • 传输层(TCP)的 Segment 在经过网络层(IP Layer)的时候,会加上 IP 的 header,成为一个 IP Packet。

  • 网络层的 IP Packet 在经过链路层(Link Layer)的时候,会加上Link Layer 的 header,成为一个 Frame。

  • 最后 Frame 会在物理层,将数字信号转化为物理信号传输。

这里值得特别注意的是,在每一层,有不同的英文术语来对应包的概念,比如在 TCP 层的包叫做 Segment,在 IP 层的叫做 Packet,在链路层的叫做 Frame,另外和 TCP 位于同一层的 UDP 包我们一般叫做 Datagram,不同协议层里术语并不一样,好处是,在交流的时候,我们选择不同的英文单词就能预先确立是在那一层讨论协议。而 Segment、Packet、Frame、Datagram 等翻译成中文的时候,都是译为「包」,大家说读英文原版资料是不是更好,这些术语我们需要特别记忆,可以对照下图:

QQ截图20170315144106.png

不过有些场景下,我们也会用 Packet 来泛指每一层的包,但是用每一层自己的术语会更准确和专业,这些行话和习惯我们也需要了解。

我们可以用一个公式来表示每一层协议的构成:

Packet = Protocol Header + Payload

每一层的包都可以用这个公式来表示。Payload 指传入这一层的数据内容,比如:

TCP Segment = TCP header + HTTP data

有了这个认知之后,对于每一层协议的学习,最后就落实到每一层 header 的学习上了,学习 TCP 就是研究 TCP header 的构成,header 里的每一个 bit 位都有特别的用处,来实现协议层对于网络传输的控制。这也是为什么我经常会说,所谓的网络协议学习就是 header 学习,这也是本文标题的含义所在。

深入 Header

【TCP/IP 详解】大致有 1000 页,通读的过程会很漫长且枯燥,知道每一层协议都是关于 header 的设计之后,大家其实可以先跳跃式的阅读,先学习感兴趣的部分,有了收获知识的正向反馈之后,在回过头来填补更多的知识细节。比如大家一般都对 TCP 协议比较感兴趣(确实也是最有意思的部分,后面的文章也重点分析),那么可以先跳到第十二章:

Chapter 12 TCP: The Transmission Control Protocol(Preliminaries)

或者第十三章,真正明白所谓的三次握手:

Chapter 13 TCP Connection Management

由于每一层的设计都是独立的,所以先学习传输层并不会有什么障碍,这也是分层架构的意义所在,各层各司其职,互不依赖具体的实现。

我们的学习行为,大致上可以分为两类,理解(理解思想)和记忆(强行记忆)。对于 header 的学习,除了理解 header 每个 bit 的意义之外,还需要一些记忆行为,对于一些关键信息的强制性记忆,有助于我们形成更深刻的认知。我们以 TCP 的 header 为例:

QQ截图20170315144148.png

上图是一个 TCP header,以下是一些需要「死记硬背」的信息点:

一个 TCP Header 一般有 20 个字节,如果启用了 options,header的长度可以达到 60 个字节。上图中每一行是 4 个 bytes,32 个 bits。我先带大家学习下前 5 行,每一行是 4 Bytes,五行刚好是 20 个 bytes。计算机世界中,通常会以 bit,byte,word(4 个 byte)等不同粒度来描述信息,header 的学习一般是以 4 个字节为一个单位来展示的。

  • 第一行,由 Source port 和 Destination port 构成,二者各占 2 个字节,刚好一起占据第一行的 4 个字节。这两个字段分别表示 TCP 连接中的,发送方端口号和接收方的端口号,既然一个 port 只占 2 个 bytes,那么端口值的范围自然就是 0~65535 啦。

  • 第二行,Sequence number,表示发送方的序列号。这个序列号表示的是什么呢?一个 TCP 流是有无数个 0 和 1 构成,这些 0 和 1 以 8 个 bit 为单位,可以分割成一个个的 byte,TCP 是可靠传输协议,每一个 byte 都是有标号的,因为我们需要追踪每个 byte 是否被成功传输了,每个 byte 的标号就是我们这里的 sequence number。假设我们建立 TCP 连接的时候,发一个 sync 包,我们就以 0 标记 sync 包的第一个字节,那么 sync 包中的 Sequence 值就是 0。实际应用中,处于安全考虑,TCP 流的第一个 Sequence number 一般不会是 0,而是一个随机数。Sequence number 占据 4 个字节,也就是 2 的 32 次方,这个数字并不算大,每个包都会用掉一些,如果达到最大值之后,就取余从 0 重新开始。

  • 第三行,Acknowledge number,表示接收方 ack 的序列号。接收方收到发送方一个的 TCP 包之后,取出其中的 sequence number,在下一个接收方自己要发送的包中,设置 ack 比特位为 1,同时设置 acknowledge number 为 sequence number + 1。所以接收方的 acknowledge number 表示的是,接收方期待接收的下一个包起始字节的标号,大家可以仔细理解下这一句话。所以 acknowledge number 和 sequence number 是配对使用的。

  • 第四行,这一行尤其重要,出于篇幅的考虑,其中细节会在后续的文章中讲解。这里简单提下从 CWR 到 FIN 的 8 个 bit,这 8 个 bit 里每一位都是一个标记位,用来标记当前 TCP 包的特殊含义。比如我们所说的三次握手,第一个 sync 包,就是将 SYN 位置为 1。第二个 syn + ack 包就是将 header 的 ACK 和 SYN 位都置为 1。第三个 ack 包即将 ACK 位置 1。剩余的几个 bit 位暂时不展开讲了,大家可以自己看书先学习下。

  • 第五行,这一行只有两个字段,即 Checksum 和 Urgent pointer。checksum 是个通用的计算机概念,做完整性校验之用,在很多协议(IP,UDP,ICMP)中都有应用,这个值有包的发送方去计算,之后由包的接收方取出来校验。Urgent pointer 为两个字节的偏移量,加上当前包的 sequence number,用来标记某一个范围内的 bytes 为特殊用途数据。

怎么样、其实没有多少信息量对不对?这么跟着理解一遍 header 中的每一个 bytes 之后,是不是加深了对 TCP 的理解呢?

同理,学习完 TCP 的 header 之后,大家可以再去把 IP 的 header,frame 的 header 都搜索出来,对照关键字段去理解学习,最后再配合【TCP/IP 详解】一书阅读效果更好。

Tcpdump 实战

上面是理论部分,可能有些枯燥,大家可以在理解之后,使用 tcpdump 抓包实战下,进一步加深理解。我们就来抓包,基于上面 tcp header 的学习,抓下三次握手的包 :)

我们可以用如下命令来抓三次握手的包:

QQ截图20170315144222.png

输出结果为:

  • 18:18:45.687476 IP 192.168.3.7.65284 > 59.37.116.101.https: Flags [S], seq 3942311653, win 65535, options [mss 1460,nop,wscale 5,nop,nop,TS val 589105996 ecr 0,sackOK,eol], length 0

  • 18:18:45.719744 IP 59.37.116.101.https > 192.168.3.7.65284: Flags [S.], seq 303118583, ack 3942311654, win 14280, options [mss 1412,sackOK,TS val 1712930033 ecr 589105996,nop,wscale 8], length 0

  • 18:18:45.719986 IP 192.168.3.7.65284 > 59.37.116.101.https: Flags [.], ack 1, win 4112, options [nop,nop,TS val 589106029 ecr 1712930033], length 0

你能根据上面 tcp header 的学习,理解上面 tcpdump 命令的含义吗?tcpflags 指的是 header 中的哪些位呢?

对于 tcpdump 的使用还不太了解的同学,可以翻阅我之前的一篇介绍文章。

当然 TCP 里还包含着很多有趣的知识点,大家可以先行阅读,后面我会逐步讲解,比如 TCP 的 ARQ 机制,Flow Control 等。

总结

这次新启的 TCP/IP 系列文章对我来说,会是一次耗时费力,旷日持久的旅途。希望能够慢火细熬,徐徐烹制,将这道倾注心力、营养丰盛的网络协议佳肴制作完成,与君共享。

搜索CocoaChina微信公众号:CocoaChina
微信扫一扫
订阅每日移动开发及APP推广热点资讯
公众号:
CocoaChina
我要投稿   收藏文章
上一篇:程序员才是真正的段子手
下一篇:一个程序员要扔掉多少代码,才能成为真正的程序员
我来说两句
发表评论
您还没有登录!请登录注册
所有评论(0

综合评论

相关帖子

sina weixin mail 回到顶部