<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>TCP和UDP on Last Stand</title><link>https://amatsuzero.github.io/LastStand/tags/tcp%E5%92%8Cudp/</link><description>Recent content in TCP和UDP on Last Stand</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Wed, 06 Aug 2025 00:00:00 +0800</lastBuildDate><atom:link href="https://amatsuzero.github.io/LastStand/tags/tcp%E5%92%8Cudp/index.xml" rel="self" type="application/rss+xml"/><item><title>TCP和UDP</title><link>https://amatsuzero.github.io/LastStand/posts/frontend/network/tcp-udp/</link><pubDate>Mon, 28 Jul 2025 00:00:00 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/frontend/network/tcp-udp/</guid><description>&lt;h2 id="tcpip-五层模型"&gt;TCP/IP 五层模型&lt;/h2&gt;
&lt;p&gt;先看图，为网络模型分层，以及一个完整http请求在五层模型中的完整工作流程&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://amatsuzero.github.io/LastStand/posts/frontend/network/tcp-udp/image-01.webp"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;应用层&lt;/code&gt;：最高层，提供特定于应用程序的协议，运行在该层的协议有HTTP、FTP、SSH、WebScoket等&lt;/li&gt;
&lt;li&gt;&lt;code&gt;传输层&lt;/code&gt;：为两个主机进程通信提供通用的数据传输协议，如TCP、UDP&lt;/li&gt;
&lt;li&gt;&lt;code&gt;网络层&lt;/code&gt;：负责寻址和路由功能，将数据包发送到特定的计算机，主要协议是IP协议，路由器就是在这一层&lt;/li&gt;
&lt;li&gt;&lt;code&gt;链路层&lt;/code&gt;：负责将二进制数据包和网络信号相互转换，交换机、网卡就是在这一层&lt;/li&gt;
&lt;li&gt;&lt;code&gt;物理层&lt;/code&gt;：主要有接收器、发送器、中继器、光纤电缆等&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;网络协议通过分层来明确每一层的工作职责，通过定义明确的接口来协同工作，第一层都可以使用下面各层的功能，而不用担心各层是怎么实现的。就好像我们开发封装组件一样，每一个组件各自负责各自的事，互不干扰，也提高了复用度，如上图文件基本传输过程，也就是&lt;code&gt;http分层工作流程&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;主机A发起请求，数据发送前会被分为许多片段，称为数据包，然后使用http协议将数据包封装，并加上&lt;code&gt;请求头&lt;/code&gt;，传给下一层&lt;/li&gt;
&lt;li&gt;传输层拿到数据，为每个数据包分配一个端口号，用来确定目标计算机的哪一个应用程序，然后使用TCP协议进行处理，加上&lt;code&gt;TCP头&lt;/code&gt;或&lt;code&gt;UDP头&lt;/code&gt;，通过TCP协议传给下一层&lt;/li&gt;
&lt;li&gt;网络层拿到数据后为每个数据包添加目标计算机的IP地址，并决定传给什么路由或接收的主机，再封装传给下一层&lt;/li&gt;
&lt;li&gt;链路层将数据转译成电子信号，进一步封装成&lt;code&gt;数据帧&lt;/code&gt;，传给物理层&lt;/li&gt;
&lt;li&gt;物理层通过电缆传送给主机B这边的链路层&lt;/li&gt;
&lt;li&gt;主机B的链路层拿到数据后，检查每个包中的目标地址并确定将其发送到哪里，如果不是发给自己的就丢弃，然后根据数据确定协议类型，再传给网络层的IP协议模块&lt;/li&gt;
&lt;li&gt;网络层接收到后拆开获取&lt;code&gt;IP头&lt;/code&gt;，判断首部接收的IP地址匹配，然后根据&lt;code&gt;头部协议类型&lt;/code&gt;，转发TCP或UDP等&lt;/li&gt;
&lt;li&gt;传输层TCP收到后会&lt;code&gt;计算校验&lt;/code&gt;，判断数据的&lt;code&gt;完整性&lt;/code&gt;，然后处理数据包顺序接收的逻辑，最后根据&lt;code&gt;端口&lt;/code&gt;确定要转发给应用层的哪个程序&lt;/li&gt;
&lt;li&gt;最终应用层接到数据之后，根据http协议&lt;code&gt;解析&lt;/code&gt;数据&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里只展开一下网络层的 IP 和传输层的 TCP 、UDP&lt;/p&gt;
&lt;h2 id="ip"&gt;IP&lt;/h2&gt;
&lt;p&gt;如果是在&lt;code&gt;局域网&lt;/code&gt;内都是用&lt;code&gt;MAC地址通信&lt;/code&gt;，局域网之外，就得用IP了。MAC就像是身份证，IP就像是住址。所有TCP、UDP、ICMP等数据都是以IP数据报格式进行传输&lt;/p&gt;
&lt;p&gt;IP协议本身不支持发往目的地址失败的IP数据包，也没有提供直接的方式获取诊断信息，比如发送途中经过哪些路由器，以及往返时间，而这就由&lt;code&gt;ICMP协议&lt;/code&gt;来专门负责&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ICMP并不为IP网络提供可靠性，只用于反馈各种故障和配置信息，丢包不会触发ICMP&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我们常用的ping就是用ICMP查询报文。不过ping使用ICMP协议会直接跳过了传输层，所以ping程序是没有端口号的&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;IP协议的特点是：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;IP协议是不可靠的传输协议&lt;/code&gt;。如果 ICMP协议出现传输异常，IP都会丢弃数据包并可能会响应一个ICMP差错消息给发送端，而&lt;strong&gt;任何要求可靠性必须由上层如TCP协议来提供&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IP协议是无连接的&lt;/code&gt;。就是不维护任何关于后续数据的状态信息，每个数据独立。表现在：可以不按发送顺序接收，不用维护连接状态，免去了维护复制的链路状态信息(TCP会讲到)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="udp"&gt;UDP&lt;/h2&gt;
&lt;h3 id="udp的特点"&gt;UDP的特点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;无连接&lt;/code&gt;不需要握手和挥手就可以直接发送数据。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;不可靠性&lt;/code&gt;：就是一个传递数据的搬运工，来一个包就发一个。不会备份，也不关心对方是否正确收到，传输顺序也无法保证。所以就只能由应用层来保证可靠，因为网络层也是不可靠的&lt;br&gt;&lt;/li&gt;
&lt;li&gt;在发送端应用层将数据传给传输层的UDP，它只&lt;strong&gt;加一个UDP头标识&lt;/strong&gt;(UDP协议)，就直接发给网络层了。&lt;/li&gt;
&lt;li&gt;接收端在网络层将数据发给传输层，传输层UDP&lt;strong&gt;只去掉IP报文头&lt;/strong&gt;就传给应用层了。&lt;/li&gt;
&lt;li&gt;其他什么都不会管，不过这也&lt;code&gt;减少开销和发送数据之前的延迟&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;支持广播&lt;/code&gt;：有单播，多播，广播的功能，不只支持一对一传输方式，还支持一对多，多对多的方式&lt;/li&gt;
&lt;li&gt;&lt;code&gt;首部开销小&lt;/code&gt;：8个字节（&lt;code&gt;源端口号&lt;/code&gt;(非必填)、&lt;code&gt;目的端口号&lt;/code&gt;、&lt;code&gt;UDP长度&lt;/code&gt;(数据报的整个长度)、&lt;code&gt;UDP检查和&lt;/code&gt;(检测UDP数据报是否有错或者目的端口找不到对应的进程，&lt;code&gt;各2字节&lt;/code&gt;），因为它要求不高而且实现的功能没有那么多，所以首部字段不多，而&lt;code&gt;TCP有20个字节&lt;/code&gt;。它的数据是可以为0的，所以它最少可以是8个字节&lt;/li&gt;
&lt;li&gt;&lt;code&gt;是面向报文的&lt;/code&gt;：适合一次性传输少量数据，因为应用层给UDP多长的报文都会照样发送，即一次发送一个完整的报文，即不合并也不拆分。如果报文太长的话，UDP完整的装进来交给网络层的话，网络层就要分片了，因为传给链路层的话它有一个MTU的要求，所以网络层就要分片，这会给网络层的效率造成影响&lt;/li&gt;
&lt;li&gt;&lt;code&gt;无拥塞控制&lt;/code&gt;：适合实时应用，因为它会一直以恒定的速度发送数据，即使网络条件不好，也不会对发送速率进行调整。这就导致在网络不好的情况下就有可能丢包，但优点也明显，在某些实时性要求高的场景比如说聊天、在线视频、网络语音电话等使用UDP而不是TCP，比如打微信电话出现偶尔断续不是太大问题。当然拥塞太严重也有一些补救措施比如向前纠错或者重传&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="udp-为什么不可靠"&gt;UDP 为什么不可靠&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;传输数据之前&lt;code&gt;不需要先建立连接&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;不保证消息交付&lt;/code&gt;，远程主机的传输层在接收到UDP报文后，不需要确认&lt;/li&gt;
&lt;li&gt;&lt;code&gt;不保证将会顺序&lt;/code&gt;，不设置包序号、不重排、不会发生队首阻塞&lt;/li&gt;
&lt;li&gt;&lt;code&gt;不进行拥塞控制&lt;/code&gt;，没有内置反馈机制，不重传、无超时&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="tcp"&gt;TCP&lt;/h2&gt;
&lt;p&gt;这是我们平时用的最多的协议，特别是前后端 TCP给应用程序提供了一种与UDP完全不同的服务 TCP是面向连接的可靠的服务，面向连接指TCP的两个应用程序必须在它们可交换数据之前，通过相互联系来建立一个TCP连接 TCP提供了一种字节流抽象概念给应用程序：不会自动插入记录标志或者消息边界，如发送端分别发10字节和30字节，接收端可能会以两个20字节的方式读入&lt;/p&gt;
&lt;h3 id="tcp的特点"&gt;TCP的特点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;是面向连接的&lt;/code&gt;，通信之前双方必须要先建立连接&lt;/li&gt;
&lt;li&gt;&lt;code&gt;只支持单播&lt;/code&gt;，就是点对点的传输，一条TCP连接只能有两个端点&lt;/li&gt;
&lt;li&gt;&lt;code&gt;提供可靠交付的服务&lt;/code&gt;，有完整性校验、数据不会丢失，会丢包重传、且会按顺序到达&lt;/li&gt;
&lt;li&gt;&lt;code&gt;是面向字节流的&lt;/code&gt;。不像UDP那样一个个报文独立传输，而是在不保留报文边界的情况下以字节流方式进行传输&lt;/li&gt;
&lt;li&gt;&lt;code&gt;提供拥塞控制&lt;/code&gt;，当网络出现拥塞的情况，有流量控制，能够减少传输数据的速度和数量，缓解拥塞，保证稳定&lt;/li&gt;
&lt;li&gt;&lt;code&gt;提供全双工通信和可靠通信&lt;/code&gt;，指的是发送方和接收方可以&lt;code&gt;同时发送数据&lt;/code&gt;也可以&lt;code&gt;同时接收数据&lt;/code&gt;。因为两边都会设置有&lt;strong&gt;发送缓存&lt;/strong&gt;和&lt;strong&gt;接收缓存&lt;/strong&gt;&lt;br&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;发送缓存&lt;/code&gt;就是发送缓存的队列里面有准备发送的数据和已经发送但是还没有收到来自接收方确认的数据，如果没有收到确认还要重发所以不能扔掉，将可能会被重传，因为TCP需要保证可靠传输&lt;/li&gt;
&lt;li&gt;&lt;code&gt;接收缓存&lt;/code&gt;就是按序到达但是还没有被接收应用程序读取的数据和没按序到达的数据，需要顺序排好了，接收方才能逐一接收数据&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="为什么说-tcp-是可靠的"&gt;为什么说 TCP 是可靠的&lt;/h4&gt;
&lt;p&gt;因为接收方收到数据后会发送一个ACK确认应答消息，这样发送方就知道自己的数据被对方接收了，如果一直没有收到ACK一定时间后就会重发。因此就算数据没有发到接收方，或者接收方的ACK数据包丢失也有重传机制，确保双方最终可以通过重传也能正确收到消息&lt;/p&gt;
&lt;h4 id="重传机制"&gt;重传机制&lt;/h4&gt;
&lt;p&gt;由于TCP的下层网络层可能出现丢失、重复或乱序的情况，TCP协议需要提供可靠数据传输服务。&lt;/p&gt;</description></item></channel></rss>