TCP、UDP、Socket、HTTP 网络编程面试题
Category(分类): NET Protocol Status: 优
本文尽量保留原文的面试题结构、比喻和代码示例,并结合 RFC 9293、RFC 768、RFC 8085、RFC 9110、RFC 9112、RFC 9113、RFC 9114、RFC 8446 以及现代浏览器实现进行修订。文中“TCP/IP 四层模型”和“OSI 七层模型”是帮助理解的抽象模型,实际协议和实现并不总能严格放进某一层。
什么是网络编程
- 网络编程的本质是不同计算设备之间的数据交换。数据传递本身并不只是“把一个设备中的数据发送给另一个设备”,还涉及地址发现、路由、连接管理、数据边界、可靠性、拥塞控制、安全和应用协议解析。常见网络程序采用请求/响应方式:一个程序发送请求,另一个程序返回响应。发起连接或请求的一方通常称为客户端(Client),等待连接或提供服务的一方通常称为服务器(Server)。
- 客户端可以按需启动,服务器一般持续运行并监听端口,但这不是绝对规则。P2P、WebSocket、消息队列和服务间通信中,一台设备或一个进程也可能同时具有客户端和服务器的角色。
- 例如打电话时,首先拨号的一方类似客户端,等待接听的一方类似服务器。连接建立后,双方都可以发送数据,TCP 连接本身是双向的;客户端和服务器只是当前交互中的角色,不代表某一方永远只能发送或接收。
网络编程中两个主要的问题
- 如何准确地定位网络上的一个或多个主机、接口和服务;
- 找到目标后,如何以合适的可靠性、吞吐量、时延和安全性传输数据。
- IP 层主要负责寻址和分组转发,但 IP 地址并不一定唯一对应一台物理主机:一台主机可以有多个接口和地址,多个主机也可能通过 NAT 共享公网地址。
- 传输层提供端到端的传输抽象。TCP 提供可靠、有序的字节流;UDP 提供尽量简单的无连接数据报服务,不保证送达、顺序或不重复。QUIC 运行在 UDP 之上,但在用户态补充了可靠流、拥塞控制、TLS 保护和连接迁移等能力。
- 网络编程通常通过 Socket API 使用 TCP、UDP、Unix domain socket 或其他协议。应用程序仍然需要自己定义消息格式、消息边界、超时、重试、鉴权和错误处理。
- 目前常见的网络模型包括客户端/服务器(C/S)、浏览器/服务器(B/S)、发布/订阅、P2P 和微服务模型。服务器一般作为长期运行的进程监听端口,但也可以采用事件驱动、线程池、协程、无服务器函数等实现,而不一定“每来一个客户端就启动一个进程”。
网络协议是什么
在计算机网络中要有条理地交换数据,就必须遵守事先约定的规则,例如地址格式、消息结构、编码方式、是否需要确认、错误如何处理和连接如何关闭。这些规则被称为网络协议。
协议不仅规定“发送什么”,也规定接收方如何解释数据。双方即使都能发送字节,如果没有共同的协议和 framing(消息边界)约定,也无法可靠地还原业务消息。
为什么要对网络协议分层
- 降低问题复杂度:将寻址、路由、可靠传输、加密和应用语义拆分为相对独立的问题;
- 便于替换技术:某一层的技术发生变化时,只要保持对上层的接口和语义,其他层可以少受影响,例如 HTTP/3 将 HTTP 语义映射到 QUIC;
- 便于实现和维护:每层有相对清晰的职责和测试边界;
- 促进标准化:不同厂商可以围绕统一的层间接口和协议规范实现互操作。
分层是模型,不表示真实报文一定严格只经过某一层,也不表示所有协议都能无争议地归入某一层。TLS、QUIC、Socket API 等都可能跨越教科书中的层次边界。
计算机网络体系结构

OSI 参考模型
- OSI(Open Systems Interconnection,开放系统互联)参考模型由 ISO 制定,用于描述网络通信的七层框架:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
- OSI 是参考模型,不是“所有公司都必须逐层实现”的工程协议栈。现实中的 TCP/IP 协议族并没有严格实现独立的会话层和表示层,TLS、RPC、序列化等功能可能分布在不同位置。

TCP/IP 参考模型
常见的 TCP/IP 四层模型为:
- 应用层:为应用提供协议和数据格式,例如 HTTP、DNS、SMTP、SSH、FTP。HTTPS 可以理解为 HTTP 与 TLS 的组合;
- 传输层:提供进程到进程的传输抽象,TCP、UDP、SCTP、QUIC(从应用使用角度常被视为传输协议)都与这一层有关。端口号用于区分主机上的服务或进程;
- 网际层/网络层:IP 负责跨网络寻址和分组转发;ICMP 用于差错报告和诊断;
- 网络接口层/链路层:处理局域网链路上的帧、介质访问和链路地址。物理传输有时另列为物理层,因此也常见 TCP/IP 五层模型。
原文将“数据链路层”描述成同时包含硬件、软件和物理线路,这在入门模型中可以理解,但严格划分时应区分链路层和物理层。
1 TCP / UDP
1.1 什么是 TCP/IP、TCP 和 UDP
TCP/IP 不是一个单独的协议,而是以 IP 为核心的一组协议族。TCP(Transmission Control Protocol)是其中的传输层协议;UDP(User Datagram Protocol)也是传输层协议。
- TCP 是面向连接、可靠、有序的字节流协议。它通过序列号、确认、重传、校验和、接收窗口和拥塞控制等机制,把应用写入的字节流尽量按序交付给对端应用。TCP 不保留应用层消息边界,连接异常中断时,尚未被确认的数据仍可能丢失。
- UDP 是无连接的数据报协议。每次发送一个 UDP 数据报,接收端按数据报边界接收;UDP 不保证送达、不保证顺序、不保证不重复,也不提供像 TCP 那样的拥塞控制和连接状态。UDP 具有校验和机制,不能简单说“内容正确性完全不可能保证”,但应用不能把校验和当作可靠传输机制。
- UDP 本身简单,不等于应用一定不可靠。DNS、音视频协议和 QUIC 都可以在 UDP 之上增加重试、序号、校验、拥塞控制或可靠流;反过来,使用 TCP 也不代表业务一定成功,因为连接可能在应用确认前中断。
1.2 TCP 与 UDP 的区别
- TCP 是面向连接的;UDP 不需要建立 TCP 式连接;
- TCP 提供可靠、有序的字节流;UDP 提供尽力而为的数据报;
- TCP 的数据没有消息边界,接收方需要通过长度字段、分隔符、固定长度或关闭连接等方式自行组帧;UDP 一个数据报对应一次接收操作的消息边界,但超过路径或接收能力仍可能丢弃或分片;
- TCP 通常支持一对一的连接。UDP 可以一对一、一对多、多对一或多对多,是否允许广播/组播还取决于网络和 Socket 配置;
- TCP 首部最小 20 字节,UDP 首部 8 字节,但实际传输开销还包括 IP、链路层、TLS、QUIC 或应用层开销,不能只比较这两个数字;
- TCP 维护连接状态、发送/接收窗口和拥塞控制状态;UDP 协议本身维护的状态很少,应用或内核仍可能维护发送队列、NAT 映射等状态;
- TCP 面向连接并不等于“永远长连接”,连接可以超时、被关闭或被 RST;UDP 无连接也不等于不能持续发送多个数据报。
TCP 通信可以类比打电话:双方先建立连接,再持续交流。UDP 通信可以类比广播:发送方发出一条消息,不要求接收方先建立会话,也不自动知道对方是否收到。
1.3 TCP 和 UDP 的应用场景
- 需要可靠、有序传输且不希望应用自己实现重传和拥塞控制时,常使用 TCP,例如传统 HTTP/1.1、HTTP/2、SSH、FTP 控制连接、SMTP、数据库连接;
- 对时延、实时性或组播更敏感,能够容忍少量丢包,或希望自行设计传输机制时,可以使用 UDP,例如实时音视频、在线游戏、DNS 查询、NTP;
- “实时就一定用 UDP、其他都用 TCP”不是绝对规则。WebRTC 使用 UDP 为主但有 TCP/TLS 回退;QUIC/HTTP/3 使用 UDP 却提供可靠流;实时应用也可能根据数据类型同时使用可靠和不可靠传输。
1.4 形容一下 TCP 和 UDP
TCP 通信可看作打电话
李三:喂,是王五吗?
王五:哎,您谁啊?
李三:我是李三,我想给你说点事儿,你现在方便吗?
王五:哦,我现在方便,你说吧。
(连接建立,接下来传输应用数据)
这个比喻用于说明连接建立和双向通信,但 TCP 并不理解“句子”或“通话内容”,它只传输有序字节流。
UDP 通信可看作学校广播
播音室:喂喂喂!全体操场集合!
广播发出后,发送方不会从 UDP 协议本身得到“所有人都听到了”的保证;应用若需要确认,必须自行设计反馈。
1.5 运行在 TCP 或 UDP 上的应用层协议
常见使用 TCP 的协议
- HTTP/1.1、HTTP/2:现代实现通常运行在 TCP 之上;HTTP/3 改为运行在 QUIC 之上;
- HTTPS:HTTP over TLS,不是与 HTTP 完全无关的新应用语义;TLS 可以运行在 TCP 或 QUIC 等传输之上;
- FTP:控制连接通常使用 TCP,数据连接也使用 TCP;
- POP3、SMTP、IMAP:邮件收取和传输协议,通常通过 TLS 或 STARTTLS 保护;
- Telnet:基于 TCP 的远程终端协议,但明文传输,现代环境通常使用 SSH 代替;
- SSH:基于 TCP 的加密远程登录和隧道协议;
- 传统数据库协议:多数使用 TCP,但具体取决于数据库和驱动实现。
常见使用 UDP 的协议
- DHCP:IPv4 中客户端通常使用 UDP 68,服务器使用 UDP 67;
- NTP:通常使用 UDP 123;
- DNS 查询:通常优先使用 UDP,响应过大、区域传送或特定场景会使用 TCP;DNS 也可以通过 TLS(DoT)或 HTTPS(DoH)传输;
- QUIC:运行在 UDP 之上,为 HTTP/3 等应用提供可靠流、拥塞控制、TLS 1.3 集成和连接迁移;
- 实时媒体协议:许多 RTP/RTCP 部署使用 UDP,但可靠性和拥塞控制由上层或配套协议负责。
既可以使用 TCP 也可以使用 UDP 的协议
- DNS:并非只能使用 UDP;
- ECHO:RFC 862 定义的诊断协议,可以使用 TCP 或 UDP,但现代公网通常禁用;
- SNMP:传统上主要使用 UDP,也有 TCP、TLS、DTLS 等传输映射;
- 部分自定义应用协议:可以根据需求选择不同传输,不应仅凭协议名称推断。
ARP 不属于运行在 TCP 或 UDP 之上的应用层协议,它直接服务于 IPv4 局域网中的下一跳链路寻址,应单独理解。
什么是 ARP 协议(Address Resolution Protocol)
- ARP 用于在 IPv4 局域网中把一个 IPv4 地址解析成对应的链路层地址(例如以太网 MAC 地址)。它不是把 IP 地址映射成抽象的“物理地址”,也不跨路由器查询远端主机。
- 每个主机通常维护 ARP 缓存。当源主机要发送 IPv4 数据包时,会先判断目的 IPv4 地址是否属于本地子网:
- 如果目的主机在同一局域网,主机查询或广播 ARP 请求,获取目的主机的链路地址;
- 如果目的主机在远端网络,主机解析的是默认网关或下一跳路由器的链路地址,然后把 IP 数据包交给路由器转发。
- ARP 请求通常以链路层广播发送;目标主机发现请求中的目标 IP 是自己后,返回 ARP 响应。缓存项会过期,不能假设映射永久有效。
- IPv6 不使用 ARP,而使用 ICMPv6 Neighbor Discovery(邻居发现协议)。
- ARP 没有内置强身份认证,局域网中存在 ARP 欺骗风险,企业网络可以结合交换机防护、静态绑定、动态 ARP 检测和加密协议降低风险。
什么是 NAT(Network Address Translation,网络地址转换)
NAT 由网络设备修改经过的数据包中的地址,很多家庭路由器还会同时转换端口,因此常见的是 NAPT/PAT。它主要用于缓解 IPv4 地址不足、实现内网地址复用以及控制入站连接。
- 静态 NAT:内外地址之间固定映射;
- 动态 NAT:从地址池中分配外部地址;
- PAT/NAPT:多个内网地址通过不同端口共享一个或少量公网 IPv4 地址;
- 端口映射/端口转发:允许外部连接映射到内网服务。
NAT 会破坏端到端可达性,使入站连接、P2P、VoIP 和某些协议需要 NAT 穿透、STUN、TURN、UPnP 或端口映射。NAT 不是完整的防火墙,也不是加密安全机制;IPv6 通常不需要用 NAT 解决地址不足,但仍可能部署防火墙或其他地址转换方案。
从输入网址到获得页面的过程
传统教材常把过程简化成“DNS → TCP 三次握手 → HTTP 请求 → 关闭 TCP”。现代浏览器还会使用缓存、Service Worker、代理、连接池、TLS、HTTP/2 多路复用和 HTTP/3/QUIC,因此实际过程可能不同。
- 解析 URL:确定 scheme、主机、端口、路径和查询参数;浏览器还可能受到 HSTS、Service Worker、扩展、代理和缓存影响;
- 查找地址:浏览器和操作系统可能先查自身缓存、Hosts 文件和本地 DNS stub,再向配置的递归 DNS 解析器查询。递归解析器可能从缓存、权威 DNS、根服务器、顶级域服务器获得 A/AAAA 等记录;
- 选择连接:根据协议协商结果选择 HTTP/1.1、HTTP/2 或 HTTP/3。HTTP/1.1/2 常见路径是 TCP(HTTPS 还需要 TLS),HTTP/3 是 QUIC(QUIC 集成 TLS 1.3);已有可复用连接时不一定重新握手;
- 发送 HTTP 请求:请求可能经过正向代理、CDN、负载均衡和反向代理。HTTP/2/3 可以在同一连接上复用多个 Stream;
- 服务端处理和返回响应:服务端根据方法、目标 URI、首部、认证信息和请求内容处理请求,返回状态码、响应首部和可选内容。发生重定向时,浏览器可能继续请求新的 URI;
- 解析和渲染:浏览器解析 HTML、CSS、JavaScript 和图片等资源,并根据缓存策略、优先级和安全策略请求子资源;
- 连接继续复用或关闭:HTTP/1.1 持久连接、HTTP/2 连接和 HTTP/3 连接都可能继续服务后续请求,不能把“服务器关闭 TCP 连接”当作每个 HTTP 请求的必经步骤。
1.6 TCP 的三次握手
1.6.1 什么是 TCP 的三次握手
TCP 建立连接的过程通常称为三次握手。它不是简单的“确认服务器开机”,而是让双方交换初始序列号、确认对方的控制报文已经到达,并建立双方的连接状态。

1.6.2 三次握手的具体细节
设客户端初始序列号为 x,服务端初始序列号为 y:
- 第一次握手:客户端发送
SYN=1, SEQ=x,表示请求建立连接并声明自己的初始序列号,客户端进入SYN-SENT; - 第二次握手:服务端收到客户端 SYN 后,发送
SYN=1, ACK=1, SEQ=y, ACK=x+1。这同时确认了客户端的 SYN,并声明服务端自己的初始序列号,服务端进入SYN-RECEIVED; - 第三次握手:客户端收到并验证服务端的 SYN+ACK 后,发送
ACK=1, ACK=y+1。客户端进入ESTABLISHED;服务端收到这个 ACK 后也进入ESTABLISHED,随后双方可以发送应用数据。
简化表示:
客户端 服务端
SYN, SEQ=x ────────>
<──────── SYN+ACK, SEQ=y, ACK=x+1
ACK= y+1 ────────>
SYN 会占用一个序列号,因此确认号要加 1。TCP 序列号和确认号是 32 位序号空间中的值,会发生回绕,实际判断还结合窗口和时间戳等机制。
三次握手分别确认了什么
- 第一次握手到达服务端,服务端确认自己可以接收客户端发来的 SYN,但此时服务端还不知道客户端是否能收到服务端的报文;
- 第二次握手到达客户端,客户端可以确认服务端发出了 SYN+ACK,并能接收服务端报文;
- 第三次握手到达服务端后,服务端才确认客户端收到了自己的 SYN+ACK,并且客户端到服务端的反向路径可用。
因此三次握手能够在正常情况下确认双方的收发路径和初始序列号。它不能保证应用程序一定存活,也不能保证后续业务处理成功。
1.6.3 用现实理解三次握手
客户端:喂,是服务端吗?(SYN)
服务端:是我,我听到了,你能听到我吗?(SYN + ACK)
客户端:听到了,我也确认收到你的消息。(ACK)
1.6.4 建立连接可以两次握手吗?为什么?
正常 TCP 连接不能用两次握手替代三次握手。原因包括:
- 旧网络中可能存在延迟到达的重复 SYN。若服务端收到后仅凭一次响应就建立连接,而客户端其实没有发起当前连接,服务端可能分配资源并长期等待,形成半开连接;
- 两次握手不能让服务端确认客户端收到自己的 SYN+ACK;
- 三次握手让双方能够完成初始序列号的双向确认,并避免把过期的连接请求误认为新连接。
两次消息在某些抽象协议中可能足够,但 TCP 需要面对旧报文、双向可达性和序列空间等问题,所以正常建立过程至少需要三次控制报文。实际抓包中第三次 ACK 可能和第一段应用数据合并,但逻辑上的 ACK 仍然存在。
1.6.5 可以采用四次握手吗?为什么?
可以发送额外的确认或控制报文,但没有必要把正常 TCP 建连设计成四次。三次握手已经完成必要的状态同步,额外报文只会增加时延和开销。某些 TCP 扩展、代理或应用协议可能在握手后继续交换自己的协商消息,那属于应用或扩展层,不是 TCP 必需的第四次握手。
1.6.6 第三次握手中,如果客户端的 ACK 未送达服务器,会怎样?
- 服务端没有收到第三次 ACK 时,通常仍停留在
SYN-RECEIVED,并按照实现配置的重传计时器和退避策略重发 SYN+ACK。重传间隔、次数和最终超时时间不是固定的“每 3 秒、五次”,会因操作系统、网络和配置而变化; - 如果客户端再次发送一个包含可接受 ACK 的报文,服务端可能据此完成状态转换;
- 如果服务端最终放弃半开连接,客户端之后发送的报文可能收到 RST 或其他错误;
- 客户端通常已经进入
ESTABLISHED,它可能重传 ACK,或在发送数据时携带 ACK。是否能恢复取决于服务端当前状态和网络情况。
1.6.7 如果已经建立连接,但客户端出现故障怎么办?
TCP 可以通过重传超时发现未确认数据失败,也可以使用可选的 TCP keep-alive 探测空闲连接。但 keep-alive 不是所有实现都默认开启,RFC 规定的是能力和建议,并不是所有系统都使用“空闲 2 小时、每 75 秒探测、连续 10 次失败”这一固定组合。
现代服务通常还会使用应用层心跳、请求超时、连接池健康检查和负载均衡探活。TCP keep-alive 只能说明传输层对端是否仍有响应,不能证明应用服务健康或业务请求已经处理成功。
1.6.8 初始序列号是什么?
TCP 连接的一方会选择一个 32 位初始序列号(Initial Sequence Number,ISN)。它不是为了给每条应用消息编号,而是为连接中的字节流建立序号空间,并降低旧连接报文干扰新连接的风险。
例如发送方从 1000 开始,若发送 1000 字节,则这些字节占用相应的序号区间。对端返回 ACK=2001 时,通常表示序号小于 2001 的数据已经按序收到,下一步期望序号为 2001。ACK 反映的是 TCP 接收和确认状态,不等于应用程序已经读取或业务已经持久化。
SYN 和 FIN 各自占用一个序列号;纯 ACK 不占用序列号。TCP 是字节流,不能把一次 send()、一个应用对象或一个 TCP Segment 当成天然的业务消息边界。
1.7 TCP 的四次挥手
1.7.1 什么是 TCP 的四次挥手
TCP 是全双工连接,两个方向可以独立关闭。通常一方先发送 FIN 关闭自己的发送方向,另一方先 ACK,再在自己没有更多数据时发送 FIN,因此常见的是四个控制报文。实际情况可能是三段、四段或更多段:ACK 和 FIN 可以合并,也可能出现同时关闭和重传。

1.7.2 四次挥手的具体细节
以下以客户端主动关闭为例:
- 第一次挥手:客户端发送
FIN(通常也带 ACK),表示客户端不再发送数据,进入FIN-WAIT-1; - 第二次挥手:服务端收到 FIN 后发送 ACK,确认号为客户端 FIN 序号加 1,进入
CLOSE-WAIT。此时服务端仍可以继续向客户端发送尚未完成的数据; - 第三次挥手:服务端应用完成发送后,TCP 发送 FIN,表示服务端发送方向也关闭,服务端进入
LAST-ACK; - 第四次挥手:客户端收到服务端 FIN 后发送 ACK,进入
TIME-WAIT;服务端收到 ACK 后通常进入CLOSED。客户端等待约 2×MSL 后释放连接状态。
客户端 服务端
FIN, SEQ=u ───────────────────────>
<─────────────────────── ACK=u+1
<─────────────────────── FIN, SEQ=v
ACK=v+1 ───────────────────────>
TIME-WAIT CLOSED
FIN 表示发送 FIN 的一方在该方向没有更多数据,不等于双方立即停止接收。第二次挥手的 ACK 只是确认收到了 FIN,不代表服务端已经同意立即关闭或服务端数据已经发送完毕。
1.7.3 用现实理解 TCP 的四次挥手
客户端:我这边说完了,不再发送了。(第一次挥手)
服务端:收到,我知道你不再发送了。(第二次挥手)
服务端:我这边剩余内容也发送完了,我也不再发送了。(第三次挥手)
客户端:收到你关闭发送方向的消息。(第四次挥手)
“四次”是典型的独立半关闭过程,不是任何断开都严格产生四个 TCP Segment。RST 异常中止连接时,通常不会走完整的 FIN 挥手。
1.7.4 为什么不能把服务器的 ACK 和 FIN 合并成三次挥手?CLOSE_WAIT 有什么意义?
如果服务端收到客户端 FIN 时已经没有待发送数据,ACK 和 FIN 可以在同一个报文中发送,实际抓包可能只有三段。不能要求所有场景都合并,是因为服务端收到客户端 FIN 时可能还有数据未发送完:
- 服务端先 ACK,进入
CLOSE-WAIT; - 服务端继续发送剩余数据;
- 应用关闭 Socket 后,TCP 再发送 FIN,进入
LAST-ACK。
CLOSE-WAIT 说明对端已经关闭了它的发送方向,而本地应用还没有关闭自己的发送方向。服务端长期处于 CLOSE_WAIT,通常意味着应用没有及时关闭连接或存在资源泄漏。
1.7.5 如果第二次挥手时服务器的 ACK 没有送达客户端,会怎样?
客户端没有收到对自己 FIN 的确认,会根据 TCP 重传计时器重传 FIN,而不是按照固定时间简单重发。服务端收到重复 FIN 后通常再次发送 ACK。只要服务端仍保留连接状态,关闭流程可以继续;若服务端已经超时清理,客户端可能收到 RST。
1.7.6 客户端 TIME-WAIT 状态的意义
主动关闭的一方进入 TIME-WAIT,主要有两个目的:
- 确保最后一个 ACK 有机会重传:如果服务端没有收到最后 ACK,会重传 FIN;客户端在 TIME-WAIT 中可以再次确认;
- 防止旧连接的延迟报文干扰新连接:等待旧报文在网络中消失,避免相同四元组快速复用时产生歧义。
RFC 9293 要求主动关闭方等待 2×MSL,但现代系统可能根据时间戳和实现策略优化连接复用。TIME-WAIT 多并不自动表示网络异常,短连接过多、主动关闭角色、端口范围和连接复用策略都可能造成大量 TIME-WAIT。
2 Socket
2.1 什么是 Socket
Socket 是操作系统向应用提供的网络通信接口和抽象对象。它允许应用通过统一 API 使用 TCP、UDP、Unix domain socket、SCTP、原始套接字等不同协议,具体能力取决于操作系统和 Socket 类型。
- TCP 连接的一个端点可以用本地地址、端口、远端地址、远端端口和协议共同描述;“一个 Socket 由一个 IP 地址和端口唯一确定”是入门时期的简化说法,不足以描述 TCP 连接。服务器监听 Socket 和已建立连接的 Socket 也不是同一个对象;
- Java 中
java.net.Socket主要表示 TCP 客户端连接,ServerSocket用于监听 TCP 连接,DatagramSocket用于 UDP 数据报; - Socket API 偏底层,HTTP 客户端、数据库驱动、WebSocket 库和 RPC 框架通常在其上实现协议和连接管理;
- TCP Socket 的连接可以长时间保持,但“Socket 连接就是长连接”不准确。是否保持连接由 TCP 状态、应用协议、超时、连接池和对端行为共同决定;HTTP/1.1、HTTP/2 也可以复用持久连接。

2.2 Socket 属于网络的哪个层面
Socket 不是 OSI 或 TCP/IP 模型中的一个独立网络协议层,而是应用程序与操作系统网络协议栈之间的编程接口。它把 bind、listen、accept、connect、send、recv、shutdown 等操作抽象出来,让应用不必直接操作 IP、TCP 报文和网卡。

把 Socket 称为“应用层与 TCP/IP 协议族之间的中间软件抽象层”可以作为入门理解,但它并不只支持 TCP/IP,也不负责自动定义业务消息格式。
2.3 Socket 通讯的过程
基于 TCP
服务器端通常执行:
- 创建监听 Socket;
bind绑定本地地址和端口;listen进入监听状态;accept等待客户端连接;- 对每个已建立连接使用独立的连接 Socket 收发字节流;
- 完成协议交互后
shutdown或close。
客户端通常执行:
- 创建 Socket;
connect连接服务端地址和端口;- 按应用协议发送和读取数据;
- 关闭或复用连接。
一个阻塞式 BIO 服务器可以为每个连接创建线程,也可以一次只处理一个连接。生产环境通常使用线程池、NIO、异步 IO 或事件循环。
基于 UDP
UDP 服务端通常创建并绑定 DatagramSocket,通过 receive 接收一个个数据报;客户端创建 Socket 后通过 send 把数据报发送到目标地址。UDP 不需要 TCP 式的三次握手,但应用可以使用 connect 记录默认对端并过滤来源,这不等于建立了 TCP 可靠连接。
UDP 发送成功通常只代表数据交给了本机协议栈,不代表目标主机已经接收或应用已经处理。需要可靠性时,应在应用层设计序号、确认、超时、重传、去重、拥塞控制和安全校验。
2.4 TCP 协议 Socket 代码示例
先运行服务端,再运行客户端。示例使用换行作为应用层消息分隔符,演示了 TCP 字节流必须自行定义消息边界。它是单线程 BIO 示例,不适合直接用于高并发生产环境。
服务端
package com.test.io;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class BIOServer {
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(8000)) {
System.out.println("服务端启动成功,监听端口为 8000...");
while (true) {
try (Socket socket = server.accept();
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8))) {
System.out.println("客户连接成功:" + socket.getRemoteSocketAddress());
String line;
while ((line = in.readLine()) != null) {
System.out.println("收到:" + line);
out.write("hello!\n");
out.flush();
}
}
}
}
}
}
客户端
package com.test.io;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
public class Client01 {
public static void main(String[] args) throws IOException {
try (Socket socket = new Socket("127.0.0.1", 8000);
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8));
Scanner scanner = new Scanner(System.in, StandardCharsets.UTF_8)) {
System.out.println("TCP 连接成功,请输入内容:");
while (scanner.hasNextLine()) {
out.write(scanner.nextLine());
out.write('\n');
out.flush();
System.out.println("服务端响应:" + in.readLine());
}
}
}
}
原示例存在一个重要问题:服务端使用 read() 循环等待客户端关闭输出流,客户端却一直保持连接并继续发送,因此服务端可能一直阻塞,根本执行不到返回 hello! 的代码。修订后使用换行定义消息边界,并在每次请求后返回一行响应。
如果实际协议传输 JSON、文件或二进制内容,应使用长度前缀、固定头部、明确分隔符或其他 framing 方案;不能依赖一次 write 对应一次 read。


2.5 UDP 协议 Socket 代码示例
UDP 示例只演示一次数据报发送和接收。发送端打印“发送成功”只表示本机 API 接受了数据,不代表服务端一定收到。实际应用应处理丢包、乱序、重复、超时和消息大小限制。
服务端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.nio.charset.StandardCharsets;
public class Server1 {
public static void main(String[] args) {
byte[] buffer = new byte[1024];
try (DatagramSocket socket = new DatagramSocket(8888)) {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
System.out.println("等待 UDP 数据报...");
socket.receive(packet);
String message = new String(
packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8);
System.out.println("收到 " + packet.getAddress() + ":" + packet.getPort() + ":" + message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
public class Client1 {
public static void main(String[] args) {
byte[] data = "UDP 协议的 Socket 请求,可能丢失".getBytes(StandardCharsets.UTF_8);
try (DatagramSocket socket = new DatagramSocket()) {
InetAddress address = InetAddress.getByName("127.0.0.1");
DatagramPacket packet = new DatagramPacket(data, data.length, address, 8888);
socket.send(packet);
System.out.println("UDP 数据报已交给本机协议栈");
} catch (IOException e) {
e.printStackTrace();
}
}
}
原文中的 ODP 是笔误,应为 UDP;new String 和 getBytes 使用默认字符集也可能在不同机器上产生乱码,因此示例明确使用 UTF-8。

2.6 Socket 的常用 Java 类
| 类名 | 协议/用途 | 作用 |
|---|---|---|
Socket | TCP | 客户端 TCP 连接,也可表示服务端 accept 得到的已连接 Socket;负责字节流读写和连接状态管理 |
ServerSocket | TCP | 绑定并监听服务端口,accept 返回一个独立的 TCP 连接 Socket |
DatagramSocket | UDP | 发送和接收 UDP 数据报 |
DatagramPacket | UDP | 表示待发送或已接收的数据报及其地址、端口和有效长度 |
InetAddress | IP 地址 | 表示 IPv4/IPv6 地址并提供 DNS 解析相关方法,不包含端口 |
InetSocketAddress | IP/主机名 + 端口 | 封装 Socket 连接或绑定所需的地址和端口,不依赖某一种应用协议 |
3 HTTP
什么是 HTTP 协议
HTTP(Hypertext Transfer Protocol,超文本传输协议)是应用层协议,定义了客户端和服务器之间传输请求、响应、表示和控制信息的语义。它可以承载 HTML、JSON、图片、音频、视频和任意二进制内容。
HTTP 的可靠性、顺序性和安全性不是 HTTP 语义单独提供的:HTTP/1.1 和 HTTP/2 常运行在 TCP 上,HTTP/3 运行在 QUIC 上,HTTPS 则在 HTTP 与传输之间使用 TLS 保护。HTTP 不是“可靠传输文字、图片、音频、视频的协议”这么简单。

Socket 和 HTTP 的区别及应用场景
- Socket 是操作系统提供的通信 API/抽象对象;HTTP 是运行在传输协议之上的应用层协议。二者不是同一层次的概念;
- TCP Socket 可以承载 HTTP,也可以承载自定义二进制协议、数据库协议或 WebSocket 握手后的数据;
- HTTP/1.1 支持持久连接和连接复用;HTTP/2 在一条 TCP 连接上多路复用多个 Stream;HTTP/3 在 QUIC 连接上多路复用,因此“HTTP 是短连接、Socket 是长连接”是过时的简化说法;
- HTTP 适合 Web、API、文件传输、服务间调用;自定义 Socket 协议适合需要特殊消息格式、低延迟或长时间双向交互的场景,但应用必须自行处理 framing、安全、鉴权、心跳和重连;
- 网络游戏、直播、在线协作等实时场景可以使用 UDP、QUIC、WebSocket、WebRTC 或 TCP,具体选择取决于可靠性、延迟、浏览器能力和部署环境。
什么是 HTTP 请求体
HTTP 请求消息可以包含请求目标、首部和可选内容。HTTP/1.1 文本形式通常是:
POST /users HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Length: 15
{"name":"Tom"}
“请求体由请求行、请求头、请求数据组成”不准确:请求行和请求首部属于请求消息的起始行/首部部分,body 是可选内容,三者不能都称为请求体。
- POST、PUT、PATCH 常使用请求体,但并非强制;
- GET 请求在 HTTP 语义中没有通用的请求体语义,客户端通常不应发送 GET content,除非目标服务明确规定;不能笼统说所有 GET 都绝对没有请求体;
- HTTP/2、HTTP/3 不使用文本请求行和空行,而是用伪首部字段、HEADERS 帧和 DATA 帧表达相同语义;
- body 的格式由
Content-Type等首部描述,长度和边界由具体 HTTP 版本的 framing 规则确定。


HTTP 响应报文有哪些
HTTP 响应是在请求之后返回的消息,但请求不必包含请求体。HTTP/1.1 响应通常包含:
- 状态行,例如
HTTP/1.1 200 OK; - 响应首部字段,例如
Content-Type、Content-Length、Cache-Control; - 空行;
- 可选响应内容。
HTTP/2/3 则使用 :status 等伪首部和二进制帧,不再使用文本状态行和空行。204、304、HEAD 响应等场景不能按普通响应返回内容。

HTTP 和 HTTPS 的区别
HTTPS 可以理解为 HTTP over TLS:
- HTTP 默认端口通常是 80,HTTPS 默认端口通常是 443,但端口可以由部署配置改变;
http://不使用 TLS 时,内容可能被窃听或篡改;- HTTPS 使用 TLS 提供机密性、完整性和服务器身份认证;
- 证书不一定需要付费,Let’s Encrypt 等 CA 可以签发免费证书;
- 现代证书的公钥不一定是 RSA,也可以是 ECDSA 等算法;
- HTTP/1.1 和 HTTP/2 常通过 TCP + TLS 运行,HTTP/3 通过 QUIC 使用 TLS 1.3;
- HTTPS 不能修复服务器漏洞、弱密码、错误授权或恶意业务逻辑,也不能自动证明网站内容可信。
HTTPS 工作原理
原文将现代 HTTPS 描述成“客户端生成随机数,再用服务器 RSA 公钥加密,服务端用私钥解密”,这是早期 TLS RSA 密钥传输的说法,不适用于现代主流 TLS 1.2 ECDHE 和 TLS 1.3。现代流程大致是:
- 客户端发送 ClientHello,包含支持的 TLS 版本、密码套件、随机数、SNI、ALPN 和 ECDHE key share 等扩展;
- 服务端选择参数并发送 ServerHello,随后发送自己的证书链和
CertificateVerify签名; - 客户端验证证书链是否锚定到信任 CA,检查有效期、用途以及证书 SAN 是否匹配目标主机名;
- 双方通过 ECDHE 等临时密钥交换计算共享秘密,不是把一个“随机数”直接用 RSA 加密传输;
- TLS 使用 HKDF 等密钥派生机制生成握手密钥和应用数据密钥;
- 服务端的证书私钥用于签名认证,客户端使用证书公钥验证签名。签名不是简单的“用私钥加密摘要”;
- 双方通过 Finished 校验握手 transcript 后,用协商出的对称 AEAD 密钥加密应用数据。
TLS 1.3 典型完整握手通常为 1-RTT,并删除了 RSA 静态密钥传输和许多旧式弱算法。RSA 仍可作为证书签名算法使用,但不再作为 TLS 1.3 的 RSA 密钥传输方式。MD5 和 SHA-1 不应作为现代 TLS 认证或签名方案使用。
一次完整的 HTTP 请求经历哪些步骤
原文的“建立 TCP、发送请求行/请求头、服务器回送状态行、发送数据、关闭 TCP”可以作为 HTTP/1.1 首次访问的历史简化流程,但现代 HTTP 还要考虑连接复用和 HTTP/2/3。较完整的过程是:
- 解析 URL、代理配置、缓存和安全策略;
- 查询 DNS,得到一个或多个 A/AAAA 地址;
- 选择或复用 TCP/TLS 连接,或者建立 QUIC 连接;
- 发送 HTTP 请求。HTTP/1.1 使用文本起始行和首部,HTTP/2/3 使用 HEADERS/DATA 等帧;
- 服务端、CDN、代理和网关处理请求,返回状态码、首部和内容;
- 浏览器依据缓存、CORS、Cookie、CSP 和 MIME 类型处理响应,并请求页面子资源;
- 连接可能留在连接池中继续复用,也可能因超时、服务器策略、错误或用户操作而关闭。
HTTP/1.1 的典型报文仍可以写成:
GET /sample/hello.html HTTP/1.1
Host: example.com
Accept: text/html
首部结束的空行是 HTTP/1.1 文本消息的 framing 形式;HTTP/2 和 HTTP/3 不发送这个文本空行。
常用 HTTP 状态码分类
HTTP 状态码表示请求的处理结果,但状态码不等于业务结果:
- 1xx:信息响应,例如
100 Continue、101 Switching Protocols、103 Early Hints; - 2xx:成功,例如
200 OK、201 Created、202 Accepted、204 No Content、206 Partial Content; - 3xx:重定向或缓存,例如
301、302、303、304、307、308; - 4xx:当前服务器不能满足请求,例如
400、401、403、404、405、409、413、429; - 5xx:服务器、代理或网关处理失败,例如
500、501、502、503、504。
原文表格中的几处说法需要更新:
303 See Other要求客户端使用 GET(HEAD 情况除外)访问另一个 URI;307 Temporary Redirect与302的历史行为不同,要求保留原请求方法和请求内容,并不是“强制使用 POST”;304用于条件 GET/HEAD 的缓存验证结果,不是普通重定向;401 Unauthorized通常需要认证并配合WWW-Authenticate,403是已理解但拒绝服务;503表示暂时不可用,可能配合Retry-After,不能只解释为“服务器正忙”。
HTTP 协议有哪些请求方法
| 方法 | 主要语义 | 常见性质 |
|---|---|---|
| GET | 获取目标资源的当前表示 | safe、幂等,通常可缓存 |
| HEAD | 获取类似 GET 的首部,不返回内容 | safe、幂等 |
| POST | 对目标资源进行资源特定处理 | 默认非 safe、非幂等 |
| PUT | 创建或替换目标资源的表示 | 幂等,通常不 safe |
| PATCH | 对目标资源做部分修改 | 是否幂等取决于具体设计 |
| DELETE | 请求删除目标资源 | 幂等语义,但不代表资源一定存在 |
| OPTIONS | 查询目标资源或服务器支持的通信选项 | safe、幂等 |
| CONNECT | 建立到目标的隧道,常用于代理 | 由代理场景定义 |
| TRACE | 回显请求用于诊断 | 生产环境常禁用 |
PUT 不是“只能传输文件”,它的语义是用请求内容创建或替换目标资源的表示;PATCH 也不是简单地“把文档内容取代”,而是部分修改。方法是否允许请求体、是否缓存、是否幂等,都应参考具体方法规范和资源接口定义。
GET 方法与 POST 方法的区别
- GET 的主要语义是获取资源;POST 的语义是让目标资源根据请求内容执行资源特定处理;
- GET 通常把查询参数放在 URI 中,POST 通常把数据放在请求体中,但这不是安全边界,也不是绝对的语法限制;
- URL 没有一个由 HTTP 统一规定的“最大长度”。浏览器、代理、服务器和网关各有实现限制,过长 URI 可能得到
414 URI Too Long;POST 也会受到请求体大小限制; - GET 是 safe 和幂等方法,POST 默认不是。幂等表示重复请求的预期服务器效果相同,不表示响应内容、日志或计费行为一定完全相同;
- GET 响应通常更容易被缓存,POST 响应在显式满足缓存规则时也可能缓存;
- GET 参数可能出现在历史记录、日志、书签、Referer 等位置,但 POST 放在请求体并不等于安全。两者都应使用 HTTPS,敏感数据不应通过 URL 传递;
- GET 不应依赖请求体的通用语义,POST 可以有 body,但真正是否接受、如何解析由接口定义决定。
“GET 数据量小、POST 可以传大文件”是工程经验,不是 HTTP 方法本身的严格限制。实际限制来自 URI、首部、请求体、代理和服务器配置。
HTTP 版本的对比
HTTP/1.0
- 早期 HTTP/1.0 默认每个请求完成后关闭连接,持久连接常通过非标准或扩展的
Connection: keep-alive实现; - HTTP 语义本身是无状态的,服务器是否保存会话状态由 Cookie、Session、Token 等应用机制决定;
- 每个请求建立连接会增加 TCP/TLS 握手和慢启动开销。
HTTP/1.1
- 持久连接成为默认行为,只要双方没有明确关闭,就可以在同一 TCP 连接上发送多个请求/响应;
- 支持
Host、分块传输、Range 请求、条件请求和更完善的缓存控制; - 管线化允许客户端先发送多个请求,但响应仍需按顺序返回,容易出现应用层队头阻塞,浏览器实际支持有限,后来通常使用多个连接缓解;
- HTTP/1.1 的文本报文并不意味着一次
send()就是一个请求,TCP 仍然是字节流,接收方必须按 HTTP framing 解析。
HTTP/2(RFC 9113)
- 使用二进制帧;
- 使用 HPACK 压缩首部;
- 在同一 TCP 连接上通过多个 Stream 多路复用;
- 支持 Stream 级和连接级流量控制;
- 旧的 Stream 优先级机制已经被 RFC 9113 弃用,现代实现可以参考 RFC 9218 的 HTTP Priority;
- Server Push 仍存在于协议规范历史中,但主流浏览器已经禁用或移除实际支持,现代项目通常使用缓存、preload 和 103 Early Hints 代替;
- 多路复用解决了部分 HTTP 层队头阻塞,但所有 Stream 共享 TCP,丢包仍可能阻塞整条 HTTP/2 连接。
HTTP/3(RFC 9114)
- 基于 QUIC,而不是 TCP;
- 使用 QUIC 的独立 Stream,减少 TCP 层跨请求队头阻塞;
- 使用 QPACK 压缩首部;
- QUIC 集成 TLS 1.3,并支持连接迁移、快速建立和更灵活的流控制;
- 使用 UDP 不代表天然不可靠,QUIC 在 UDP 之上提供可靠、有序的 Stream;
- 单个 Stream 内仍然有顺序要求,丢包仍可能影响该 Stream。
什么是对称加密与非对称加密
- 对称加密使用同一个密钥加密和解密,速度通常较快,适合大量应用数据。主要困难是通信双方如何安全地取得共享密钥;
- 非对称密码使用公钥和私钥。公钥可以公开,私钥需要保护。它可以用于加密/解密,也可以用于数字签名/验证,但“签名”与“公钥加密”是两个不同用途;
- 非对称算法通常比对称算法计算成本高,因此 TLS 常先通过证书和 ECDHE 完成身份认证及密钥协商,再使用对称 AEAD 算法保护应用数据;
- 公钥本身不能自动证明属于某个域名。HTTPS 依靠 CA 签发的 X.509 证书、主机名校验、有效期和密钥用途建立信任链。
Cookie 和 Session 对 HTTP 有什么用
HTTP 的通用语义是无状态的,它不会自动记住上一次请求是谁。但 Web 应用需要登录态、购物车和用户偏好,因此可以使用 Cookie、Session、Token 等机制保存和传递状态。
什么是 Cookie
Cookie 是用户代理保存的一小段键值数据,不应只理解为服务器保存在浏览器上的普通文件。服务器通过 Set-Cookie 设置属性,浏览器根据 Domain、Path、Secure、HttpOnly、SameSite 和过期时间决定是否保存及发送:
Set-Cookie: session_id=abc; Path=/; Secure; HttpOnly; SameSite=Lax
后续请求可能携带:
Cookie: session_id=abc
- Cookie 的实际大小、数量和总首部限制因浏览器和服务器而异,常见实现约把单个 Cookie 限制在 4KB 左右,但不是所有环境都严格相同;
Secure通常要求仅通过 HTTPS 发送;HttpOnly阻止 JavaScript 通过document.cookie读取,但不能阻止 XSS 利用当前登录态发起请求;SameSite用于控制跨站请求携带 Cookie,现代浏览器对未设置该属性的 Cookie 通常按 Lax 兼容策略处理;- Cookie 不是天然加密存储,敏感数据应放在安全设计的会话标识中,并结合服务端失效、轮换和权限检查。
什么是 Session
Session 不是 HTTP 标准中的一个状态码或传输协议,而是应用层会话管理方式。常见设计是:
- 服务端创建一份会话状态,生成随机不可预测的 Session ID;
- 通过 Cookie、URL、请求首部或请求体把 Session ID 交给客户端;
- 客户端后续请求带回 ID;
- 服务端根据 ID 从内存、数据库、Redis 或其他存储中读取会话状态。
Session 也可以采用客户端保存的签名/加密令牌,不一定总是“服务端分配一块储存空间”。Session ID 被盗用时同样会造成会话劫持,服务端仍应使用 HTTPS、HttpOnly、Secure、SameSite、轮换和注销机制。
Cookie 与 Session 的区别
- Cookie 数据由用户代理保存,Session 状态通常由服务端或服务端可验证的令牌保存;
- Cookie 会随匹配请求自动发送,可能增加带宽开销;Session ID 通常也通过 Cookie 传递,因此二者可以配合使用;
- Cookie 的大小、数量受浏览器和首部限制;Session 的存储容量取决于服务端设计,但服务端也要考虑内存、数据库和集群一致性;
- “Cookie 安全性差、Session 一定安全”不是绝对结论。Cookie 内容可能只是随机 ID,Session ID 泄露同样危险;真正的安全性取决于 HTTPS、随机性、访问控制、过期、CSRF 防护和服务端实现;
- 原文末尾重复出现的对称加密、Cookie 和 Session 段落属于重复内容,已合并保留一份。
参考资料
- RFC 9293:Transmission Control Protocol
- RFC 768:User Datagram Protocol
- RFC 8085:UDP Usage Guidelines
- RFC 9110:HTTP Semantics
- RFC 9112:HTTP/1.1
- RFC 9113:HTTP/2
- RFC 9114:HTTP/3
- RFC 8446:TLS 1.3
- RFC 826:ARP
- RFC 6888:NAT considerations
- IANA Service Name and Transport Protocol Port Number Registry
- 原文参考:TCP、UDP、Socket、HTTP 网络编程面试题