介绍
HMAC(Hash-based Message Authentication Code是一种基于哈希函数的消息认证码(MAC
)算法,用于验证数据的完整性和真实性。它通过结合哈希算法与密钥来生成一个认证码,可以防止数据在传输过程中被篡改。HMAC
广泛应用于网络协议(如 TLS
、IPSec
等)、数字签名以及 API
认证等场景中。
在这篇文章中,我们将深入了解 HMAC
的原理,并通过具体的计算过程来说明其如何工作。
1 | launchctl load /System/Library/LaunchAgents/com.apple.sharingd.plist |
1 | launchctl unload /System/Library/LaunchAgents/com.apple.sharingd.plist |
1 | defaults write /Library/Preferences/com.apple.Bluetooth.plist ControllerPowerState 0 |
1 | for (DRDevice *device in [DRDevice devices]) { |
使用 Disk Arbitration framework
Disk Arbitration framework
是一个基于 Core Foundation
的低级框架。会在磁盘出现和消失时通知您的应用程序,并让您的应用程序影响该过程。借助 Disk Arbitration
,我们可以:
Git
提交历史中的 author
等信息1 | git commit --amend --author="newname<newmail>" |
在 Git
中,若想选择性保留冲突文件的原始版本(当前分支的版本)或合并来的版本(被 cherry-pick
的提交版本),可以通过以下命令实现:
1 | # 针对特定冲突文件(例如你的 xxx.cpp) |
1 | # 针对特定冲突文件 |
–ours:代表当前分支的版本(即你在执行 cherry-pick
时的本地原始状态)。
–theirs:代表被 cherry-pick
的提交的版本(即你要合并进来的修改)。
如果多个文件冲突,可以批量操作:
1 | # 保留所有文件的当前分支版本 |
(操作后仍需 git add
并 git cherry-pick --continue
)
如果中途想彻底放弃整个 cherry-pick
,仍可用:
1 | git cherry-pick --abort |
在网络通信中,HTTP
代理是一个常见的工具,用于在客户端和服务器之间中转HTTP请求和响应。它可以用于多种目的,包括提升安全性、管理网络流量、提高访问速度等。
代理服务器的定义:代理服务器(Proxy Server
)是一种位于客户端和目标服务器之间的中间服务器,用于转发客户端的请求并将服务器的响应返回给客户端。
HTTP
协议基础:HTTP
(Hypertext Transfer Protocol
)是用于分布式、协作式和超媒体信息系统的应用层协议,是万维网数据通信的基础。
请求流程:
客户端发送 HTTP
请求到代理服务器。
代理服务器接收请求并将其转发给目标服务器。
目标服务器处理请求并返回响应给代理服务器。
代理服务器将响应返回给客户端
响应流程:
与请求流程类似,代理服务器在请求和响应过程中充当中间人角色。
数据传输和处理:
代理服务器可以对传输的数据进行缓存、过滤和修改。
1 | +--------+ HTTP Request +-----------+ HTTP Request +-----------+ |
HTTP
客户端向代理发送请求报文,代理服务器需要正确地处理请求和连接(例如正确处理 Connection: keep-alive
),同时向服务器发送请求,并将收到的响应转发给客户端。 这种代理扮演的是「中间人
」角色,对于连接到它的客户端来说,它是服务端;对于要连接的服务端来说,它是客户端。它就负责在两端之间来回传送 HTTP
报文。
1 | ➜ ~ curl -vvv http://baidu.com |
1 | ➜ ~ curl -vvv -x http://127.0.0.1:8080 http://www.baidu.com |
Connection
,以及 Connection
定义的其它 Header
,只是对上个节点和当前节点之间的连接进行说明,必须在报文转给下个节点之前删除,否则可能会引发后面要提到的问题。其它不能传递的 Header
还有Prxoy-Authenticate
、Proxy-Connection
、Transfer-Encoding
和 Upgrade
。
「close」
表示操作完成后需要关闭当前连接;Connection
还允许任何字符串作为它的值,如「my-connection」
,用来存放自定义的连接说明。HTTP/1.0
默认不支持持久连接,很多 HTTP/1.0
的浏览器和服务器使用「Keep-Alive
」这个自定义说明来协商持久连接:浏览器在请求头里加上 Connection: Keep-Alive
,服务端返回同样的内容,这个连接就会被保持供后续使用。对于 HTTP/1.1
,Connection: Keep-Alive
已经失去意义了,因为 HTTP/1.1
除了显式地将 Connection
指定为 close
,默认都是持久连接。
有了上面的背景知识,我们来看问题。互联网上,存在着大量简陋并过时的代理服务器在继续工作,它们很可能无法理解 Connection
——无论是请求报文还是响应报文中的 Connection
。而代理服务器在遇到不认识的 Header
时,往往都会选择继续转发。大部分情况下这样做是对的,很多使用 HTTP
协议的应用软件扩展了 HTTP
头部,如果代理不传输扩展字段,这些软件将无法工作。
如果浏览器对这样的代理发送了 Connection: Keep-Alive
,那么结果会变得很复杂。这个 Header
会被不理解它的代理原封不动的转给服务端,如果服务器也不能理解就还好,能理解就彻底杯具了。服务器并不知道 Keep-Alive
是由代理错误地转发而来,它会认为代理希望建立持久连接,服务端同意之后也返回一个 Keep-Alive
。同样,响应中的 Keep-Alive
也会被代理原样返给浏览器,同时代理还会傻等服务器关闭连接——实际上,服务端已经按照 Keep-Alive
指示保持了连接,即使数据回传完成,也不会关闭连接。另一方面,浏览器收到 Keep-Alive
之后,会复用之前的连接发送剩下的请求,但代理不认为这个连接上还会有其他请求,请求被忽略。这样,浏览器会一直处于挂起状态,直到连接超时。
这个问题最根本的原因是代理服务器转发了禁止转发的 Header。但是要升级所有老旧的代理也不是件简单的事,所以浏览器厂商和代理实现者协商了一个变通的方案:首先,显式给浏览器设置代理后,浏览器会把请求头中的 Connection
替换为 Proxy-Connetion
。这样,对于老旧的代理,它不认识这个 Header
,会继续发给服务器,服务器也不认识,代理和服务器之间不会建立持久连接(不能正确处理 Connection
的都是 HTTP/1.0
代理),服务器不返回 Keep-Alive
,代理和浏览器之间也不会建立持久连接。而对于新代理,它可以理解 Proxy-Connetion,会用 Connection
取代无意义的 Proxy-Connection
,并将其发送给服务器,以收到预期的效果。
显然,如果浏览器并不知道连接中有老旧代理的存在,或者在老旧代理任意一侧有新代理的情况下,这种方案仍然无济于事。所以有时候服务器也会选择彻底忽略 HTTP/1.0
的 Keep-Alive
特性:对于 HTTP/1.0
请求,从不使用持久连接,也从不返回 Keep-Alive
。
HTTP
客户端通过 CONNECT
方法请求隧道代理创建一条到达任意目的服务器和端口的 TCP
连接,并对客户端和服务器之间的后继数据进行盲转发。详见 RFC 7231 - HTTP/1.1: Semantics and Content
欢迎来到一个充满移植性挑战的世界!在深入分析这两个选项之前,我们首先需要了解 BSD
套接字实现是所有套接字实现的鼻祖。几乎所有其他系统都在某个时间点复制了 BSD
套接字实现,或至少是其接口,然后在此基础上发展演变。当然,BSD
套接字实现本身也在不断演变,因此那些更晚期复制它的系统获得了更早期系统所缺乏的功能。理解 BSD
套接字实现是理解其他所有套接字实现的关键,即使你不打算为 BSD
系统编写代码,也应该了解它。
在深入探讨 SO_REUSEADDR
和 SO_REUSEPORT
之前,你需要了解一些基本知识。一个 TCP/UDP
连接由五个值组成的元组标识:
1 | {<协议>, <源地址>, <源端口>, <目标地址>, <目标端口>} |
任何唯一的这些值组合标识一个连接。因此,没有两个连接可以具有相同的五个值,否则系统将无法区分这些连接。
socket()
函数设置套接字的协议。bind()
函数设置源地址和端口。connect()
函数设置目标地址和端口。UDP
,由于它是无连接协议,可以在不连接的情况下使用。然而,在某些情况下连接它是有益的。在无连接模式下,如果未显式绑定,UDP
套接字通常会在第一次发送数据时由系统自动绑定,因为未绑定的 UDP
套接字无法接收任何(回复)数据。对于未绑定的 TCP 套接字也是如此,它在连接之前会自动绑定。Netfilter
,在 Linux
内核中的一个软件框架,用于管理网络数据包。不仅具有网络地址转换(NAT
)的功能,也具备数据包内容修改、以及数据包过滤等防火墙功能。利用运作于用户空间的应用软件,如 iptables
、nftables
、ebtables
和 arptables
等,来控制 Netfilter
,系统管理者可以管理通过Linux操作系统的各种网络数据包。1990
年代,Netfilter
在 Linux 2.3.15
版时进入Linux内核,正式应用于 Linux 2.4
版。
TCP
快速打开(TCP Fast Open
,简称TFO
)是对计算机网络中传输控制协议(TCP
)连接的一种简化握手手续的拓展,用于提高两端点间连接的打开速度。
它通过握手开始时的 SYN
包中的 TFO cookie
(一个 TCP
选项)来验证一个之前连接过的客户端。如果验证成功,它可以在三次握手最终的 ACK
包收到之前就开始发送数据,这样便跳过了一个绕路的行为,更在传输开始时就降低了延迟。这个加密的 Cookie
被存储在客户端,在一开始的连接时被设定好。然后每当客户端连接时,这个 Cookie
被重复返回。
最显著的优点是可以利用握手去除一个往返 RTT
net.ipv4.tcp_fastopen
是 Linux
内核中的一个配置参数,它用于控制 TCP Fast Open
功能。
具体地,net.ipv4.tcp_fastopen
的值可以是以下几种:
0
:禁用 TCP Fast Open
功能。1
:在客户端启用 TCP Fast Open
功能。2
:在服务器端启用 TCP Fast Open
功能。3
:在客户端和服务器端都启用 TCP Fast Open
功能。通过设置这个参数,可以根据实际需求选择是否启用和在哪一端启用 TCP Fast Open
,从而优化网络性能。