TCP 和 UDP 的区别
- TCP 是面向连接的协议,需要在数据传输前建立连接;UDP 是无连接的,不需要建立连接。
- TCP 提供可靠的数据传输,保证数据包的顺序和完整性;UDP 不保证数据包的顺序或完整性。
- TCP 具有拥塞控制机制,可以根据网络状况调整数据传输速率;UDP 没有拥塞控制,发送速率通常固定。
- TCP 通过滑动窗口机制进行流量控制,避免接收方处理不过来;UDP 没有流量控制。
- TCP 能够检测并重传丢失或损坏的数据包;UDP 不提供错误恢复机制。
- TCP 有复杂的报文头部,包含序列号、确认号等信息;UDP 的报文头部相对简单。
- 由于 TCP 的连接建立、数据校验和重传机制,其性能开销通常比 UDP 大;UDP 由于简单,性能开销小。
- 适用场景:TCP 适用于需要可靠传输的应用,如网页浏览、文件传输等;UDP 适用于对实时性要求高的应用,如语音通话、视频会议等。
TCP 连接如何确保可靠性
TCP 通过差错控制(序列号、确认应答、数据校验)、超时重传、流量控制、拥塞控制等机制,确保了数据传输的可靠性和效率。
- 序列号:每个 TCP 段都有一个序列号,确保数据包的顺序正确。
- 数据校验:TCP 使用校验和来检测数据在传输过程中是否出现错误,如果检测到错误,接收方会丢弃该数据包,并等待重传。
- 确认应答:接收方发送 ACK 确认收到的数据,如果发送方在一定时间内没有收到确认,会重新发送数据。
- 超时重传:发送方设置一个定时器,如果在定时器超时之前没有收到确认,发送方会重传数据。
- 流量控制:TCP 通过滑动窗口机制进行流量控制,确保接收方能够处理发送方的数据量。
- 拥塞控制:TCP 通过算法如慢启动、拥塞避免、快重传和快恢复等,来控制数据的发送速率,防止网络拥塞。
TCP 拥塞控制是怎么实现的
TCP 拥塞控制可以在网络出现拥塞时动态地调整数据传输的速率,以防止网络过载。TCP 拥塞控制的主要机制包括以下几个方面:
- 慢启动(Slow Start): 初始阶段,TCP 发送方会以较小的发送窗口开始传输数据。随着每次成功收到确认的数据,发送方逐渐增加发送窗口的大小,实现指数级的增长,这称为慢启动。这有助于在网络刚开始传输时谨慎地逐步增加速率,以避免引发拥塞。
- 拥塞避免(Congestion Avoidance): 一旦达到一定的阈值(通常是慢启动阈值),TCP 发送方就会进入拥塞避免阶段。在拥塞避免阶段,发送方以线性增加的方式增加发送窗口的大小,而不再是指数级的增长。这有助于控制发送速率,以避免引起网络拥塞。
- 快速重传(Fast Retransmit): 如果发送方连续收到相同的确认,它会认为发生了数据包的丢失,并会快速重传未确认的数据包,而不必等待超时。这有助于更快地恢复由于拥塞引起的数据包丢失。
- 快速恢复(Fast Recovery): 在发生快速重传后,TCP 进入快速恢复阶段。在这个阶段,发送方不会回到慢启动阶段,而是将慢启动阈值设置为当前窗口的一半,并将拥塞窗口大小设置为慢启动阈值加上已确认但未被快速重传的数据块的数量。这有助于更快地从拥塞中恢复。
TCP 流量控制是怎么实现的
流量控制就是让发送方发送速率不要过快,让接收方来得及接收。利用滑动窗口机制就可以实施流量控制,主要方法就是动态调整发送方和接收方之间数据传输速率。
- 滑动窗口大小: 在 TCP 通信中,每个 TCP 报文段都包含一个窗口字段,该字段指示发送方可以发送多少字节的数据而不等待确认。这个窗口大小是动态调整的。
- 接收方窗口大小: 接收方通过 TCP 报文中的窗口字段告诉发送方自己当前的可接收窗口大小。这是接收方缓冲区中还有多少可用空间。
- 流量控制的目标: 流量控制的目标是确保发送方不要发送超过接收方缓冲区容量的数据。如果接收方的缓冲区快满了,它会减小窗口大小,通知发送方暂停发送,以防止溢出。
- 动态调整: 发送方会根据接收方的窗口大小动态调整发送数据的速率。如果接收方的窗口大小增加,发送方可以加速发送数据。如果窗口大小减小,发送方将减缓发送数据的速率。
- 确认机制: 接收方会定期发送确认(ACK)报文,告知发送方已成功接收数据。这也与流量控制密切相关,因为接收方可以通过 ACK 报文中的窗口字段来通知发送方它的当前窗口大小。
TCP 建立连接三次握手的过程,只使用两次握手行不行
- 第一次握手:客户端向服务器发送一个 SYN (同步序列编号)报文,请求建立连接,客户端进入 SYN_SENT 状态。
- 第二次握手:服务器收到 SYN 报文后,如果同意建立连接,则会发送一个 SYN-ACK (同步确认)报文作为响应,同时进入 SYN_RCVD 状态。
- 第三次握手:客户端收到服务器的 SYN-ACK 报文后,会发送一个ACK (确认)报文作为最终响应,之后客户端和服务器都进入 ESTABLISHED 状态,连接建立成功。
通过三次握手,客户端和服务器都能够确认对方的接收和发送能力。第一次握手确认了客户端到服务器的通道是开放的;第二次握手确认了服务器到客户端的通道是开放的;第三次握手则确认了客户端接收到服务器的确认,从而确保了双方的通道都是可用的。
而如果仅使用两次握手,服务器可能无法确定客户端的接收能力是否正常,比如客户端可能已经关闭了连接,但之前发送的连接请求报文在网络上延迟到达了服务器,服务器就会主动去建立一个连接,但是客户端接收不到,导致资源的浪费。
TCP 释放连接四次挥手的过程,只使用三次挥手行不行
- 第一次挥手:客户端发送一个 FIN 报文给服务端,表示自己要断开数据传送,报文中会指定一个序列号 (seq=x)。然后,客户端进入 FIN-WAIT-1 状态。
- 第二次挥手:服务端收到 FIN 报文后,回复 ACK 报文给客户端,且把客户端的序列号值 +1,作为 ACK 报文的序列号 (seq=x+1)。然后,服务端进入 CLOSE-WAIT(seq=x+1) 状态,客户端进入 FIN-WAIT-2 状态。
- 第三次挥手:服务端也要断开连接时,发送 FIN 报文给客户端,且指定一个序列号 (seq=y+1),随后服务端进入 LAST-ACK 状态。
- 第四次挥手:客户端收到 FIN 报文后,发出 ACK 报文进行应答,并把服务端的序列号值 +1 作为 ACK 报文序列号 (seq=y+2)。此时客户端进入 TIME-WAIT 状态。服务端在收到客户端的 ACK 报文后进入 CLOSE 状态。如果客户端等待 2MSL 没有收到回复,才关闭连接。
TCP 是全双工通信,可以双向传输数据。任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。 当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后才会完全关闭 TCP 连接。因此两次挥手可以释放一端到另一端的 TCP 连接,完全释放连接一共需要四次挥手。
只有通过四次挥手,才可以确保双方都能接收到对方的最后一个数据段的确认,主动关闭方在发送完最后一个 ACK 后进入 TIME-WAIT 状态,这是为了确保被动关闭方接收到最终的 ACK,如果被动关闭方没有接收到,它可以重发 FIN 报文,主动关闭方可以再次发送 ACK 。
而如果使用三次挥手,被动关闭方可能在发送最后一个数据段后立即关闭连接,而主动关闭方可能还没有接收到这个数据段的确认。
从输入 URL 到页面展示到底发生了什么事?
- 输入网址,解析 URL 信息,准备发送 HTTP 请求。
- 检查浏览器缓存里是否有缓存该资源,如果有直接返回;如果没有进入下一步网络请求。
- DNS 域名解析:网络请求前,进行 DNS 解析,以获取请求域名的 IP 地址。如果请求协议是 HTTPS,那么还需要建立 TLS 连接。DNS 解析时会按本地浏览器缓存 → 本地 Host 文件 → 路由器缓存 → DNS 服务器 → 根 DNS 服务器的顺序查询域名对应 IP,直到找到为止。
- TCP 三次握手建立连接:浏览器与服务器 IP 建立 TCP 连接。
- 客户端发送 HTTP 请求:连接建立后,浏览器端会构建请求行、请求头等信息,并把和该域名相关的 Cookie 等数据附加到请求头中,向服务器构建请求信息。如果是 HTTPS 的话,还涉及到 HTTPS 的加解密流程。
- 服务器处理请求并返回 HTTP 资源:服务器接收到请求信息,根据请求生成响应数据。
- TCP 四次挥手断开连接:浏览器与服务器 IP 断开 TCP 连接。
- 浏览器解析响应并渲染页面:
- 浏览器解析响应头。若响应头状态码为 301、302,会重定向到新地址;若响应数据类型是字节流类型,一般会将请求提交给下载管理器;若是 HTML 类型,会进入下一部渲染流程。
- 浏览器解析 HTML 文件,创建 DOM 树,解析 CSS 进行样式计算,然后将 CSS 和 DOM 合并,构建渲染树;最后布局和绘制渲染树,完成页面展示。
HTTP 请求报文和响应报文是怎样的,有哪些常见的字段?
HTTP 报文分为请求报文和响应报文。
(1)请求报文
请求报文主要由请求行、请求头、空行、请求体构成。 请求行包括如下字段:
- 方法(Method):指定要执行的操作,如 GET、POST、PUT、DELETE 等。
- 资源路径(Resource Path):请求的资源的URI(统一资源标识符)。
- HTTP 版本(HTTP Version):使用的HTTP协议版本,如 HTTP/1.1 或 HTTP/2.0。
请求头的字段较多,常使用的包含以下几个:
- Host:请求的服务器的域名。
- Accept:客户端能够处理的媒体类型。
- Accept-Encoding:客户端能够解码的内容编码。
- Authorization:用于认证的凭证信息,比如 token 数据。
- Content-Length:请求体的长度。
- Content-Type:请求体的媒体类型。
- Cookie:存储在客户端的 cookie 数据。
- If-None-Match:资源的 ETag 值,用于缓存控制。
- Connection:管理连接的选项,如 keep-alive。
空行是请求头部和请求主体之间的空行,用于分隔请求头部和请求主体。而请求体通常用于 POST 和 PUT 请求,包含发送给服务器的数据。
(2)响应报文
HTTP 响应报文是服务器向客户端返回的数据格式,用于传达服务器对客户端请求的处理结果以及相关的数据。一个标准的 HTTP 响应报文通常包含状态行、响应头、空行、响应体。
状态行包含 HTTP 版本、状态码和状态消息。例如:HTTP/1.1 200 OK。
响应头部也是以键值对的形式提供的额外信息,类似于请求头部,用于告知客户端有关响应的详细信息。一些常见的响应头部字段包括:
- Content-Type:指定响应主体的媒体类型。
- Content-Length:指定响应主体的长度(字节数)。
- Server:指定服务器的信息。
- Expires: 响应的过期时间,之后内容被认为是过时的。
- ETag: 响应体的实体标签,用于缓存和条件请求。
- Last-Modified: 资源最后被修改的日期和时间。
- Location:在重定向时指定新的资源位置。
- Set-Cookie:在响应中设置 Cookie。
- Access-Control-Allow-Origin: 跨源资源共享(CORS)策略,指示哪些域可以访问资源。
空行(Empty Line)在响应头和响应体之间,表示响应头的结束。而响应体是服务端实际传输的数据,可以是文本、HTML 页面、图片、视频等,也可能为空。
HTTP 有哪些请求方式?
- GET:请求指定的资源。
- POST:向指定资源提交数据进行处理请求(例如表单提交)。
- PUT:更新指定资源。
- DELETE:删除指定资源。
- HEAD:获取报文首部,不返回报文主体。
- OPTIONS:查询服务器支持的请求方法。
- PATCH:对资源进行部分更新。
HTTP 中常见的状态码有哪些?
- 200:表示客户端请求成功。
- 201:创建了新资源。
- 204 :无内容,服务器成功处理请求,但未返回任何内容。
- 301:永久重定向。
- 302: 临时重定向。
- 304:请求的内容没有修改过,所以服务器返回此响应时,不会返回网页内容,而是使用缓存。
- 401:请求需要身份验证。
- 403:请求的对应资源禁止被访问。
- 404:服务器无法找到对应资源。
- 500:服务器内部错误。
- 503: 服务不可用。
什么是强缓存和协商缓存
强缓存和协商缓存是 HTTP 缓存机制的两种类型,它们用于减少服务器的负担和提高网页加载速度。
- 强缓存:客户端在没有向服务器发送请求的情况下,直接从本地缓存中获取资源。
- Expires 强缓存:设置一个强缓存时间,此时间范围内,从内存中读取缓存并返回。但是因为 Expires 判断强缓存过期的机制是获取本地时间戳,与之前拿到的资源文件中的 Expires 字段的时间做比较来判断是否需要对服务器发起请求。这里有一个巨大的漏洞:“如果我本地时间不准咋办?”所以目前已经被废弃了。
- Cache-Control 强缓存:目前使用的强缓存是通过 HTTP 响应头中的 Cache-Control 字段实现,通过 max-age 来告诉浏览器在指定时间内可以直接使用缓存数据,无需再次请求。
- 协商缓存:当强缓存失效时,浏览器会发送请求到服务器,通过 ETag 或 Last-Modified 等 HTTP 响应头与服务器进行验证,以确定资源是否被修改。如果资源未修改,服务器返回 304 Not Modified 状态码,告知浏览器使用本地缓存;如果资源已修改,则返回新的资源,浏览器更新本地缓存。这种方式需要与服务器通信,但可以确保用户总是获取最新的内容。
基于 Last-Modified 的协商缓存
- Last-Modified 是资源的最后修改时间,服务器在响应头部中返回。
- 当客户端读取到 Last-modified 的时候,会在下次的请求标头中携带一个字段:If-Modified-Since,而这个请求头中的 If-Modified-Since 就是服务器第一次修改时候给他的时间
- 服务器比较请求中的 If-Modified-Since 值与当前资源的 Last-Modified 值,如果比对的结果是没有变化,表示资源未发生变化,返回状态码 304 Not Modified。如果比对的结果说资源已经更新了,就会给浏览器正常返回资源,返回 200。
但是这样的协商缓存有两个缺点:
- 因为是更改文件修改时间来判断的,所以在文件内容本身不修改的情况下,依然有可能更新文件修改时间(比如修改文件名再改回来),这样,就有可能文件内容明明没有修改,但是缓存依然失效了。
- 当文件在极短时间内完成修改的时候(比如几百毫秒)。因为文件修改时间记录的最小单位是秒,所以,如果文件在几百毫秒内完成修改的话,文件修改时间不会改变,这样,即使文件内容修改了,依然不会返回新的文件。
基于 ETag 的协商缓存:将原先协商缓存的比较时间戳的形式修改成了比较文件指纹(根据文件内容计算出的唯一哈希值)。
- ETag 是服务器为资源生成的唯一标识符(文件指纹),可以是根据文件内容计算出的哈希值,服务端将其和资源一起放回给客户端。
- 客户端在请求头部的 If-None-Match 字段中携带上次响应的 ETag 值。
- 服务器比较请求中的 If-None-Match 值与当前资源的 ETag 值,如果匹配,表示资源未发生变化,返回状态码 304 Not Modified。如果两个文件指纹不吻合,则说明文件被更改,那么将新的文件指纹重新存储到响应头的 ETag 中并返回给客户端。
HTTP1.0 和 HTTP1.1 的区别
- 持久连接:HTTP/1.1 默认支持持久连接,允许在一个 TCP 连接上发送多个 HTTP 请求和响应,减少了连接建立和关闭的开销。而 HTTP/1.0 默认为短连接,每次请求都需要建立一个TCP连接,并通过 Connection: keep-alive 头来实现持久连接。
- 管道化:HTTP/1.1 支持管道化(不是默认开启),允许客户端在第一个请求的响应到达之前发送多个请求,这可以减少等待时间,提高效率。HTTP/1.0 不支持管道化。
- 缓存控制:HTTP1.0 主要使用 If-Modified-Since/Expires 来做为缓存判断的标准,而HTTP1.1 则引入了更多的缓存控制策略例如 Etag/If-None-Match 等更多可供选择的缓存头来控制缓存策略。
- 错误处理:HTTP/1.1 增加了一些新的 HTTP 状态码,如 100 Continue,用于增强错误处理和请求的中间响应。
- Host 头:HTTP/1.1 引入了 Host 头,允许客户端指定请求的主机名,这使得在同一台服务器上托管多个域名成为可能。HTTP/1.0 没有这个头字段。
- 带宽优化 :HTTP1.0 中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能, 而 HTTP1.1 则在请求头引入了 range 头域,它允许只请求资源的某个部分,即返回码是 206(Partial Content)。
HTTP2.0 与 HTTP1.1 的区别
- 二进制协议:HTTP/2.0 采用二进制格式传输数据,而非HTTP/1.1 的文本格式,使得解析更高效,减少了解析时间。
- 多路复用:HTTP/2.0 支持多路复用,允许在单个 TCP 连接上并行交错发送多个请求和响应,解决了HTTP/1.1 中的队头阻塞问题。
- 头部压缩:HTTP/2.0 引入了 HPACK 压缩算法,对请求和响应的头部信息进行压缩,减少了冗余头部信息的传输,提高了传输效率。
- 服务器推送:HTTP/2.0 允许服务器主动推送资源给客户端,而不需要客户端明确请求,这可以减少页面加载时间。
- 优先级和依赖:HTTP/2.0 允许客户端为请求设置优先级,并表达请求之间的依赖关系,资源加载更加有序。
HTTP 3.0 是否了解
HTTP/3 是 HTTP 协议的最新版本,它基于 QUIC 协议,具有以下特点:
- 无队头阻塞:QUIC 在传输层使用 UDP 协议来传输数据。一个连接上的多个 stream 之间没有依赖,如果一个 stream 丢了一个 UDP 包,不会影响后面的 stream,不存在队头阻塞问题。
- 零 RTT 连接建立:QUIC 允许在首次连接时进行零往返时间连接建立,从而减少了连接延迟,加快了页面加载速度。
- 连接迁移:QUIC 允许在网络切换(如从 Wi-Fi 到移动网络)时,将连接迁移到新的 IP 地址,从而减少连接的中断时间。
- 向前纠错机制:每个数据包除了它本身的内容之外,还包括了部分其他数据包的数据,因此少量的丢包可以通过其他包的冗余数据直接组装而无需重传。向前纠错牺牲了每个数据包可以发送数据的上限,但是减少了因为丢包导致的数据重传。
- 安全性:HTTP/3 默认使用 TLS 加密,确保了数据传输的安全性。
HTTPS 和 HTTP 有哪些区别
- 加密层:HTTPS 在 HTTP 的基础上增加了 SSL/TLS 协议作为加密层,确保数据传输的安全性。而 HTTP 数据传输是明文的,容易受到攻击。
- HTTP 连接建立相对简单, TCP 三次握手之后便可进行 HTTP 的报文传输。而 HTTPS 在 TCP 三次握手之后,还需进行 SSL/TLS 的握手过程,才可进入加密报文传输。
- 端口:HTTPS 通常使用端口 443 ,而 HTTP 使用端口 80。
- HTTPS 协议需要向 CA 申请数字证书,来保证服务器的身份是可信的。
HTTPS 的工作原理(HTTPS 建立连接的过程)
1. 浏览器将支持的加密算法信息发给服务器。
2. 服务器选择一套浏览器支持的加密算法,以证书的形式回发给浏览器,证书中包含服务器的公钥。
3. 客户端(SSL/TLS)解析证书验证证书合法性,生成对称加密的密钥,我们将该密钥称之为 client key,即客户端密钥,用服务器的公钥对客户端密钥进行非对称加密。
4. 客户端会发起 HTTPS 中的第二个 HTTP 请求,将加密之后的客户端对称密钥发送给服务器。
5. 服务器接收到客户端发来的密文之后,会用自己的私钥对其进行非对称解密,解密之后的明文就是客户端密钥,然后用客户端密钥对数据进行对称加密,这样数据就变成了密文。
6. 服务器将加密后的密文发送给客户端。
7. 客户端收到服务器发送来的密文,用客户端密钥对其进行对称解密,得到服务器发送的数据。这样 HTTPS 中的第二个 HTTP 请求结束,整个 HTTPS 传输完成。
HTTP 的 Keep-Alive 是什么,和 TCP 的 Keepalive 是一个东西吗?
HTTP 的 Keep-Alive,是由应用层实现的,称为 HTTP 长连接。每次请求都要经历这样的过程:建立 TCP连接 HTTP请求资源 → 响应资源 → 释放连接,这就是 HTTP 短连接,但是这样每次建立连接都只能请求一次资源,所以 HTTP 的 Keep-Alive 实现了使用同一个 TCP 连接来发送和接收多个 HTTP 请求/应答,避免了连接建立和释放的开销,就是 HTTP 长连接。通过设置 HTTP 头 Connection: keep-alive 来实现。
TCP 的 Keepalive,是由 TCP 层(内核态)实现的,称为 TCP 保活机制,是一种用于在 TCP 连接上检测空闲连接状态的机制。
当 TCP 连接建立后,如果一段时间内没有任何数据传输,TCP Keepalive 会发送探测包来检查连接是否仍然有效。
DNS 查询过程
DNS 用来将主机名和域名转换为 IP 地址, 其查询过程一般通过以下步骤:
- 本地 DNS 缓存检查:首先查询本地 DNS 缓存,如果缓存中有对应的IP地址,则直接返回结果。
- 如果本地缓存中没有,则会向本地的 DNS 服务器(通常由你的互联网服务提供商(ISP)提供, 比如中国移动)发送一个 DNS 查询请求。
- 如果本地 DNS 解析器有该域名的 IP 地址,就会直接返回,如果没有缓存该域名的解析记录,它会向根 DNS 服务器发出查询请求。根 DNS 服务器并不负责解析域名,但它能告诉本地 DNS 解析器应该向哪个顶级域(.com/.net/.org)的 DNS 服务器继续查询。
- 本地 DNS 解析器接着向指定的顶级域名 DNS 服务器发出查询请求。顶级域 DNS 服务器也不负责具体的域名解析,但它能告诉本地 DNS 解析器应该前往哪个权威 DNS 服务器查询下一步的信息。
- 本地 DNS 解析器最后向权威 DNS 服务器发送查询请求。 权威 DNS 服务器是负责存储特定域名和 IP 地址映射的服务器。当权威 DNS 服务器收到查询请求时,它会查找 “example.com” 域名对应的 IP 地址,并将结果返回给本地 DNS 解析器。
- 本地 DNS 解析器将收到的 IP 地址返回给浏览器,并且还会将域名解析结果缓存在本地,以便下次访问时更快地响应。
- 浏览器发起连接: 本地 DNS 解析器已经将 IP 地址返回给您的计算机,您的浏览器可以使用该 IP 地址与目标服务器建立连接,开始获取网页内容。
Cookie 和 Session 是什么?有什么区别?
Cookie 和 Session 都用于管理用户的状态和身份,Cookie 通过在客户端记录信息确定用户身份,Session 通过在服务器端记录信息确定用户身份。
- Cookie:服务器会将一个或多个 Cookie 发送到用户浏览器,然后浏览器将这些 Cookie 存储在本地。服务器在接收到来自客户端浏览器的请求之后,就能够通过分析存放于请求头的 Cookie 得到客户端特有的信息,从而动态生成与该客户端相对应的内容。
- Session:客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是 Session。Session 主要用于维护用户登录状态、存储用户的临时数据和上下文信息等。服务器为每个用户分配一个唯一的 Session ID,通常存储在 Cookie 中。
Cookie 和 Session 的区别:
- 存储位置:Cookie 数据存储在用户的浏览器中,而 Session 数据存储在服务器上。
- 数据容量:Cookie 存储容量较小,一般为几 KB。Session 存储容量较大,通常没有固定限制,取决于服务器的配置和资源。
- 安全性:由于 Cookie 存储在用户浏览器中,因此可以被用户读取和篡改。相比之下,Session 数据存储在服务器上,更难被用户访问和修改。
- 生命周期:Cookie可以设置过期时间,Session 依赖于会话的持续时间或用户活动。
- 传输方式:Cookie 在每次 HTTP 请求中都会被自动发送到服务器,而 Session ID 通常通过 Cookie 或 URL 参数传递。