我开发了一个基于 Beancount 的账本托管服务 HostedBeans,欢迎大家来了解纯文本复式记账并试用我的服务。
标签 #计算机网络

笔记:对比TCP与UDP

TCP和UDP是传输层最常用的两种协议,几乎每个应用层协议都要在它们中间选择一个。同样,本文不详述其协议本身,而是着重对比。

显然如果我要是说,TCP是面向连接的可靠传输协议,而UDP无连接且不可靠,那么我这半个月的书算是白读了。

UDP仅仅是在IP的基础上提供了多路分解和多路复用,以及差错校验,十分简单——从UDP报头就能看出。

而TCP提供的功能则要复杂得多,首先为了保证数据能够正确、有序地到达,TCP使用了“流水线”“积累确认”的模型。

TCP会对每个数据报进行编号,同时在每个数据报中,都包含一个“确认序列号”的字段,用来指明上一个从对方正确接收的数据报。一旦接收方在一定时间内没有发送相应的确认序列号,那么发送方就会重传这些数据报。

数据报的发送行为是“流水线”(与之对应的是“逐一确认”)的,即发送方不逐一等待确认,而是一次发送若干个数据报,换句话说,发送和确认的行为是异步的。

而在这里,确认行为是“积累”(与之对应的是“选择重传”)的,也就是说,一旦一个数据报丢失了,那么在这之后的所有数据报都会重传。

TCP还提供有流量控制和拥塞控制的功能。

流量控制即在每个数据报中,都会指明自己的缓冲区还能容纳多少数据,发送方将根据这个值来协调同时存在于“流水线”上的数据量。流量控制保证了,应用层程序不会因为缓冲区不足而无法接收到新数据。当缓冲区满了以后,发送方会暂停发送。

拥塞控制即TCP会根据网络情况,自动调整发送速率。典型策略是每发送成功一个包,即提高一些发送速率,每出现一次重传,即降低一些发送速率。

同时TCP还使用了“慢启动”模型,即建立连接后默认速率是最低的(1MSS/RTT, MSS即最小TCP分段, MTU减去TCP报头), 然后逐渐提升发送速率。

这样可以保证重传行为始终是受控的,不会因为网络不稳定而出现大量重传的数据报而堵塞线路。

TCP往往用于要求数据分毫不差的应用中,比如网页(HTTP), 文件传输(FTP), 电子邮件(SMTP/POP3), 终端(Telnet)等等。

而UDP往往用于可以容忍数据丢失的协议,如流媒体应用, 语音和视频通话等,这些应用即使出现数据丢失,影响也是瞬间的,可以容忍的。

那么为什么这些流媒体应用要选择UDP呢?换言之,TCP在保证可靠传输的同时又牺牲了什么呢?

对于流媒体应用,临时的数据丢失是可以容忍的,但绝不能容忍的是延迟。

为了建立一个TCP连接,TCP需要进行“三次握手”,只有当三次握手完成后,才会正式传输数据。这样一来,建立连接并传输一份数据,至少需要2个RTT(往返延迟), 如果考虑发送端确认的话,那么需要2.5个RTT.

在断开连接时,TCP还需要“四次挥手”,又会消耗掉至少1.5个RTT.

UDP则没有这个顾虑,直接发送数据,只需0.5个RTT.

UDP协议本身保证了,当应用层通过Socket将数据传递给传输层时,UDP会立刻将数据报发送出去。而TCP不提供这个担保,TCP因为提供有流量控制和拥塞控制服务,TCP会自行选择在“合适”的时候再发送数据报。

流媒体应用在需要低延迟的情况下,还需要尽可能的带宽保证。

尤其是因为TCP的拥塞控制服务,注定了TCP不能够尽可能利用带宽,当网络出现堵塞时,TCP的拥塞控制会降低发送速率,让出带宽,直到网络恢复。

而UDP只会简单粗暴地继续发送数据报,很多情况下,TCP都在为UDP让出带宽,这也算是劣币驱逐良币的一种表现,据说有大牛在研究给UDP加上拥塞控制。

总结:那些宁可在UDP上自行实现TCP的部分功能的应用,主要原因在于希望避开TCP的拥塞控制,以便于尽可能地利用带宽。

笔记:对比 HTTP 和 FTP

HTTP和FTP作为互联网架构中相当基本的应用层协议,想必大家对它们都有一些了解,因此,本文不详述其协议本身,而是着重于它们之间的对比。

应该说HTTP和FTP的定位是相当接近的,FTP用于传输文件,而HTTP用于传输本质也是文件的“对象”(不要忘了HTTP还有PUT和DELETE等方法). 它们均基于TCP, C/S架构,均为基于纯文本的“简单”协议,都有SSL版本:HTTPS和FTPS(不是SFTP!).

通常FTP除了文件本身的属性,如体积,修改时间外,没有其他的元信息(meta-data), 而每个HTTP报文则都会携带大量的元信息,即HTTP报头中的键/值对。这估计也是HTTP将文件抽象为“对象”的原因吧。下面是一个简单(大多数浏览器都会产生更多报头)的HTTP请求报头:

GET / HTTP/1.1
Host: jyprince.me
Connection: kepp-alive
User-agent: Mozilla/4.0
Accept-language: zh-cn

之所以我将报头中的键/值对视为“信息”而不是协议本身,是因为它们中的大部分字段都是交由Web服务器或者浏览器来处理的。

当然,也有一些例外,如 Connection: kepp-alive, 这个报头指出客户端希望于服务器建立持久连接。基于持久连接的HTTP, 和FTP一样,都可以认为是一问一答的交互式会话。

但HTTP是无状态的,每一问一答中间,是没有关联的,将多个应答放在同一个TCP连接中仅仅是为了节约建立TCP连接的时间。

而FTP的两个应答之间,服务器是需要维护状态信息的,例如客户端的当前路径,这将直接影响到之后的指令的行为。

HTTP虽然也有手段携带状态信息(如Cookie), 但在我看来,这些属于协议之上承载的信息,并非协议本身。

HTTP报文的数据紧接报头,以一个空行隔开,即HTTP报文中的键/值对(元信息)和数据是一体的。而FTP连接中仅发送控制指令,每当需要传输文件的数据时,会建立另一个连接专用于传输数据。

总结:

  • HTTP在报头中以键/值对携带元信息,FTP没有
  • HTTP无状态,FTP在会话期间维护状态
  • HTTP的元信息与数据一同传输,FTP使用单独的连接传输数据

笔记:互联网电子邮件架构

果然写笔记是最好的读书方式,这里的笔记绝不是对知识点的堆砌,而是要让没读过这书的人有一个大概的了解,或者让读过书的人有新的发现。

最高效的阅读便是从书中为自己寻找论据。

在我的日志中,“笔记”前缀表示我这是现学现卖,对内容没太多把握。

电子邮件的出现要比Web早得多,一直以来都可以算是互联网的核心服务之一。

应该说由于历史遗留问题,目前电子邮件的架构并不完美,但一直都没有合格的替代品出现。

电子邮件使用SMTP协议被发送,SMTP是一个“简单”的,基于纯文本的交互式协议,位于TCP之上。当A向B发送邮件时,A向B在25端口上发起TCP连接,使用SMTP协议向B传输一份邮件,同时指明发信人地址和收信人地址。

这时,A和B就可以称为邮件服务器,通常一个邮件服务器是既可以收信也可以发信的,即同时是SMTP服务器和SMTP客户端。作为一台服务器,需要保持时刻在线,以便随时接收新邮件。显然,用户是不会直接使用服务器的。

在经典的电子邮件架构中,在每个用户的个人计算机上,都有一个本地SMTP代理,它负责在本地收集邮件,并定时发往服务器。引入本地SMTP代理后,A向B发送邮件时,用户A将写好的邮件交给本地SMTP代理(通常是个人计算机上的一个用户程序),本地SMTP代理通过SMTP协议将邮件发送到A的邮件服务器,A的邮件服务器再将邮件通过SMTP发送到B的邮件服务器。

本地SMTP代理和邮件服务器都会维护一个邮件队列,逐个发送邮件,当邮件无法送达时会进行几次重试,仍无法发送时会退回发信人。

邮件已经到达了B的邮件服务器,再来考虑B如何收信。B的个人计算机并不能保证时刻在线,也可能没有固定的IP地址,所以邮件服务器很难将新邮件发送到他的个人计算机上。

可以想象,SMTP是一个“推送”协议,当有新信息时,由发信人向收信人推送信息。而在从邮件服务器检查新邮件时,我们需要的可能是一种“拉取”协议,由收信人定时拉取新信息。

于是有了POP3, 它同样是在TCP之上的,基于纯文本的“简单”交互式协议。个人计算机上的POP3代理,定时使用POP3协议从邮件服务器检查新邮件,同时将新邮件下载到个人计算机。

除POP3以外还有功能更加复杂的IMAP协议,IMAP协议在POP3的基础上还支持同步阅读状态,搜索,建立文件夹对邮件进行分类等等功能。

SMTP代理和POP3(IMAP)代理分别代表了收信和发信,它们通常是一体的,称为邮件客户端,如Outlook, Thunderbird.

一封邮件除了正文之外,还可能有很多元信息,如收信人,发信人,日期,标题,收信来源等。这些键/值对信息以邮件报头的形式存在于一封邮件中,非常类似于HTTP中的报头。在早期的SMTP协议中,仅在邮件中支持ASCII字符,无法使用非ASCII字符(如中文),也无法使用格式文本和嵌入附件。于是出现了MIME标准,通过将邮件正文(base64)编码,并添加特殊的报头信息,即可实现使用非ASCII字符,使用HTML, 嵌入图片和附件等功能。

形如 m@jybox.net 的电子邮件地址指明了该邮箱位于 jybox.net 这台服务器上,对应的用户名是 m, 显然我们可以通过DNS查询到jybox.net所对应的IP, 但电子邮件服务器并非由通常的A记录指定,而是有专门的MX记录。发信人通过向DNS查询邮件地址中域名的MX记录来找到收信人的邮件服务器。

我们再来考虑安全性问题,SMTP是可选验证的。在本地SMTP代理向邮件服务器提交邮件时,通常是需要验证用户名和密码的,因为邮件服务器只希望向它的用户而不是所有人提供邮件转发服务。而在邮件服务器间发送的邮件则无需验证,因为你需要接收来自所有人的邮件。

至于邮件拉取协议(POP3, IMAP)显然也需要验证,因为一个人只应当能够查看自己邮箱里的邮件。

至此,还没有任何方式来鉴定一封邮件的真伪,任何人都可以在向收信人邮件服务器发送邮件时伪造发信人地址。为此,你可以通过DNS在一个域名的TXT记录中查询到一份被称为SPF的记录,该记录指明了经过认证的,用于该域名的发信服务器的地址,若发信人不是这个地址,则可以认为邮件是伪造的。除此之外,还有一些手段可以对SMTP进行加密,签名,工作量证明,但它们不是业界标准的一部分或应用较少,这里不再介绍。

Hotmail是当年最为成功的,基于Web的免费邮件服务,现在几乎所有邮件服务提供商均支持通过Web发送和接收邮件,甚至不再提供SMTP和POP3/IMAP访问服务。

不过随着智能手机的普及,SMTP和POP3/IMAP又回来了。

精子生于 1995 年,英文 ID jysperm.

订阅推送

通过 Telegram Channel 订阅我的博客日志、产品和项目的动态:

王子亭的博客 @ Telegram


通过邮件订阅订阅我的博客日志、产品和项目的动态(历史邮件):

该博客使用基于  Hexo  的  simpleblock  主题。博客内容使用  CC BY-NC-ND  授权发布。最后生成于 2024-04-08.