背景
TCP 握手和 HTTPS 握手分别属于 OSI 网络模型中的不同层次,并且在网络通信中扮演着重要的角色。TCP(传输控制协议)是 OSI 模型中的传输层协议
,负责提供可靠的数据传输。TCP 握手是 TCP 协议在建立连接时的一部分。HTTPS 握手属于应用层
(Application Layer),HTTPS(安全的超文本传输协议)是一个基于 HTTP 协议的加密通信协议,用于在客户端和服务器之间建立安全的通信通道。
一、TCP 握手过程(三次握手):

过程
-
第一次握手(SYN):客户端发送一个带有 SYN(同步)标志的 TCP 数据包给服务器,请求建立连接。此时客户端进入 SYN_SENT 状态。
-
第二次握手(SYN + ACK):服务器收到客户端的请求后,发送一个带有 SYN 和 ACK(确认)标志的 TCP 数据包作为响应。此时服务器进入 SYN_RCVD 状态。
-
第三次握手(ACK):客户端收到服务器的响应后,发送一个带有 ACK 标志的 TCP 数据包给服务器,确认连接建立。此时客户端和服务器都进入 ESTABLISHED 状态,可以开始传输数据。
注意: 这里的握手次数指的是建立连接所需的握手次数。关闭连接时,还需要进行四次挥手过程来正常关闭连接。
示例
下面是一个使用Node.js的示例代码,演示了TCP客户端和服务器之间的握手过程:
js
// TCP服务器代码
const net = require('net');
const server = net.createServer((socket) => {
socket.on('data', (data) => {
console.log('Received data:', data.toString());
});
socket.on('end', () => {
console.log('Connection closed by client');
});
});
server.listen(3000, () => {
console.log('TCP server listening on port 3000');
});
// TCP客户端代码
const net = require('net');
const client = net.createConnection({ port: 3000 }, () => {
console.log('Connected to TCP server');
client.write('Hello server!');
});
client.on('data', (data) => {
console.log('Received data:', data.toString());
});
client.on('end', () => {
console.log('Connection closed by server');
});
二、HTTPS 握手过程(四次握手):
HTTPS握手过程在建立安全连接之前需要进行TCP握手来建立基本的网络连接。HTTPS是在HTTP协议上添加了TLS/SSL加密层的协议。TLS/SSL协议需要在TCP连接建立之后才能进行握手过程,确保通信双方建立了安全的加密通道。

过程
-
第一次握手:客户端向服务器发送一个加密套件列表和一个随机数,请求建立连接。
-
第二次握手:服务器选择一个加密套件和一个随机数,并发送服务器证书给客户端。
-
第三次握手:客户端验证服务器证书的合法性,并生成一个用于对称加密的密钥,并将密钥加密后发送给服务器。
-
第四次握手:服务器解密客户端发送的密钥,并使用该密钥进行加密和解密通信。
在 HTTPS 握手过程中,客户端和服务器之间进行了四次握手,确保建立了安全的加密通道。完成握手后,客户端和服务器可以开始进行加密通信。
示例
下面是一个使用Node.js的示例代码,演示了HTTPS客户端和服务器之间的握手过程:
js
// HTTPS服务器代码
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt'),
};
const server = https.createServer(options, (req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello, HTTPS!');
});
server.listen(443, () => {
console.log('HTTPS server listening on port 443');
});
// HTTPS客户端代码
const https = require('https');
const options = {
hostname: 'localhost',
port: 443,
path: '/',
method: 'GET',
};
const req = https.request(options, (res) => {
console.log(`Server responded with status code: ${res.statusCode}`);
res.on('data', (data) => {
console.log('Received data:', data.toString());
});
});
req.on('error', (error) => {
console.error('Request error:', error);
});
req.end();
三、对称加密与非对称加密
因为前面提到加密与解密,所以这里作为补充温故下这两个知识点
1. 对称加密

对称加密使用相同的密钥
(称为密钥)来进行加密和解密数据。发送方
使用密钥将明文数据加密,接收
方使用相同的密钥解密数据。对称加密算法的优势在于处理速度快,适用于大量数据的加密和解密。常见的对称加密算法有AES(高级加密标准)和DES(数据加密标准)等。
下面是一个使用 Node.js 进行对称加密的示例代码:
javascript
const crypto = require('crypto');
// 定义密钥和明文数据
const key = 'SecretKey12345678';
const plaintext = 'Sensitive data';
// 创建加密器对象
const cipher = crypto.createCipher('aes-256-cbc', key);
// 加密数据
let encrypted = cipher.update(plaintext, 'utf8', 'hex');
encrypted += cipher.final('hex');
console.log('Encrypted data:', encrypted);
// 创建解密器对象
const decipher = crypto.createDecipher('aes-256-cbc', key);
// 解密数据
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
console.log('Decrypted data:', decrypted);
2. 非对称加密

非对称加密使用一对密钥
,分别是公钥
和私钥
。发送方使用接收方的公钥加密数据,接收方使用自己的私钥解密数据。非对称加密算法的优势在于提供了更高的安全性,可用于密钥交换和数字签名等场景。常见的非对称加密算法有RSA和ECC等。
下面是一个使用 Node.js 进行非对称加密的示例代码:
javascript
const crypto = require('crypto');
// 生成密钥对
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
});
// 定义明文数据
const plaintext = 'Sensitive data';
// 使用公钥加密数据
const encrypted = crypto.publicEncrypt(publicKey, Buffer.from(plaintext));
console.log('Encrypted data:', encrypted.toString('base64'));
// 使用私钥解密数据
const decrypted = crypto.privateDecrypt(privateKey, encrypted);
console.log('Decrypted data:', decrypted.toString());
注意
HTTPS(安全的超文本传输协议)使用对称加密和非对称加密的混合方式来实现通信的安全性。对称加密算法用于加密和解密实际的数据传输,而非对称加密算法用于安全地交换对称加密所需的密钥。
写在最后
其实TCP 握手和 HTTPS 握手是软件开发者基本都会了解并且在面试中经常会被问到的问题,本文旨在温故而知新,学习的过程是一个反复确认和实践的过程,也是一个需要被记录和可视化的过程,共勉~