进程和线程区别、管道和套接字、共享变量、TCP三次握手,是否可以少一次握手、子进程和主进程区别和API——Nodejs
首先讲了进程和线程区别
然后讲解 管道和套接字,它是进程间通信的方式
接着讲解共享变量 ,它是线程间通信
最后讲解TCP三次握手,因为套接字使用了TCP协议
一、线程和进程的区别
线程(Thread)和进程(Process)是操作系统中并发执行的两个基本单位。它们都可以执行任务,但作用范围和资源管理方式不同。
🧩 一张对比表快速理解
比较项 | 进程(Process) | 线程(Thread) |
---|---|---|
基本定义 | 系统资源分配的最小单位 | CPU调度的最小单位(属于进程) |
是否独立 | 相互独立,拥有各自内存空间 | 同一进程下线程共享内存空间 |
资源占用 | 占用资源较多(独立地址空间) | 占用资源少(共享进程资源) |
通信方式 | 进程间通信(IPC)复杂(如管道、socket) | 线程间通信简单(共享变量) |
创建销毁代价 | 创建和销毁开销大 | 开销相对小 |
崩溃影响 | 一个进程崩溃不会影响其他进程 | 一个线程崩溃可能影响整个进程 |
举例 | Chrome每个标签页一个进程 | Chrome标签页中的多个脚本线程 |
🔧 举个例子说明
想象你开了一家餐厅:
-
一个进程就像一个餐厅,彼此独立。
-
一个线程就像餐厅里的服务员(共享厨房、菜单、收银台)。
你开的多个餐厅之间(进程)是互不影响的,但一个餐厅中的多个服务员(线程)可以一起服务,但他们共享资源,配合更紧密。
📦 编程角度理解
-
在 Java、Python、Node.js 中,我们可以通过
thread
,child_process
,worker_threads
创建线程或进程。
Node.js 中示例
// 创建子进程
const { fork } = require('child_process');
fork('child.js');// 创建线程(Node.js 10+)
const { Worker } = require('worker_threads');
new Worker('./worker.js');
📌 总结记忆口诀
“进程是房子,线程是房间”
一个进程里可以有多个线程,但线程不能脱离进程单独存在。
如果你在学 Node.js、Java 或 Python 并发模型,我可以分别帮你分析线程/进程的用法和适用场景。需要我进一步展开哪门语言下的实践案例?
二、进程间通信中管道是什么
在操作系统中,管道(Pipe)是一种常见的进程间通信(IPC, Inter-Process Communication)机制,允许一个进程把数据发送给另一个进程,就像水流通过水管一样。
🧱 一句话解释
管道就是一个连接两个进程的通信通道,一个进程写入数据,另一个进程读取数据。
🧠 管道的原理
-
管道本质上是一个内核缓冲区(FIFO 队列)。
-
写端写入数据 → 内核缓冲 → 读端读取数据。
-
是半双工通信(数据只能单向流动)。
📦 管道的类型
类型 | 说明 |
---|---|
匿名管道 | 父子进程间通信(只能在具有亲缘关系的进程间使用) |
命名管道 FIFO | 允许无亲缘关系的进程通信,有名称(文件路径),位于文件系统中 |
🔧 举例(Node.js)
匿名管道(spawn 使用标准输入输出)
const { spawn } = require('child_process');const child = spawn('node', ['-e', `process.stdin.pipe(process.stdout)`]);child.stdin.write('Hello from parent!\n');
child.stdin.end();
child.stdout.on('data', data => {console.log(`Child says: ${data}`);
});
命名管道(Linux Bash 示例)
# 创建命名管道
mkfifo mypipe# 一个终端:向管道写入
echo "Hello" > mypipe# 另一个终端:从管道读取
cat < mypipe
🚦 特点和限制
-
✅ 进程间数据共享,快速。
-
✅ 管道通信不需要借助中间文件。
-
❌ 匿名管道只能用于父子进程。
-
❌ 半双工通信(除非建立两个管道)。
-
❌ 不能跨网络使用(仅限本地进程)。
✅ 常见应用场景
-
Shell 中的命令管道(
ls | grep txt
) -
父子进程间传递数据(如 Node.js 中子进程通信)
-
轻量级、无锁通信场景
💬 总结口诀
匿名管道亲缘深,命名管道通陌生;写端数据流向读,半双工最常见。
如果你想知道在 Java、C/C++、Python 中怎么实现管道通信,也可以举例说明。是否需要我给出跨语言的管道实现?
三、进程间通信socket是什么
在操作系统中,Socket(套接字)是最强大、最灵活的进程间通信(IPC)机制之一,它不仅能实现本地通信,还能支持网络通信(跨主机)。
🧠 一句话解释
Socket 就是一种通信端点,用于两个进程之间发送和接收数据,无论这两个进程是在同一台机器上,还是在不同主机上。
🏗️ 工作原理
Socket 通信的基本模型是 客户端-服务器(Client-Server)模型:
-
服务器进程监听某个地址(IP + 端口)。
-
客户端进程主动连接这个地址。
-
双方建立连接后就可以双向通信(全双工)。
📦 套接字类型(按通信范围)
类型 | 范围 | 示例说明 |
---|---|---|
本地套接字 | 同一台机器上通信 | Unix Domain Socket (文件路径) |
网络套接字 | 跨主机通信 | TCP/IP、UDP 套接字 |
🔧 Node.js 中的 Socket 通信示例
1. 本地通信(UNIX Socket)
// server.js
const net = require('net');
const server = net.createServer((socket) => {socket.on('data', (data) => {console.log('Server received:', data.toString());});
});
server.listen('/tmp/mysocket'); // 本地 socket 文件
// client.js
const net = require('net');
const client = net.createConnection('/tmp/mysocket', () => {client.write('Hello from client');
});
2. 网络通信(TCP Socket)
// tcp-server.js
const net = require('net');
const server = net.createServer((socket) => {console.log('Client connected');socket.on('data', (data) => {console.log('Server received:', data.toString());socket.write('pong');});
});
server.listen(3000);
// tcp-client.js
const net = require('net');
const client = net.createConnection(3000, 'localhost', () => {client.write('ping');
});
client.on('data', (data) => {console.log('Client received:', data.toString());
});
🚦 特点
特性 | 描述 |
---|---|
📡 通用性强 | 本地通信、远程通信都可用 |
🔁 全双工 | 可以双向同时读写 |
📁 文件描述符 | 在类 UNIX 系统中 Socket 是一种特殊的文件描述符 |
🔐 可配加密 | 可以集成 TLS/SSL 实现安全通信(HTTPS、WSS 等) |
🧱 适合复杂通信 | 比如多客户端并发、长连接、大数据传输等 |
🆚 与其他通信方式对比
通信方式 | 是否跨主机 | 是否双向 | 应用难度 | 性能 | 常见用途 |
---|---|---|---|---|---|
管道 pipe | ❌ 本地 | 🚫 半双工 | 简单 | 高 | 父子进程简单数据传输 |
消息队列 | ❌ 本地 | ✅ | 中 | 中 | 多进程异步通信 |
Socket | ✅ 本地+远程 | ✅ 全双工 | ✅ 复杂 | 中-高 | 服务端、网络通信 |
共享内存 | ❌ 本地 | ✅ | 复杂 | ⚡ 极快 | 高性能数据交换 |
🧪 Socket 常用场景
-
本地服务与客户端通信(如数据库客户端)
-
网络服务(Web Server、Chat Server)
-
微服务间通信(配合 TCP/HTTP)
-
实时推送(WebSocket)
✅ 总结口诀
Socket 是个洞,进程互打通。可远程可本地,网络通信最通用。
如果你想继续了解:
-
UDP socket
和TCP socket
区别 -
WebSocket
和原始socket
的差异 -
socket.io
使用实例(前后端实时通信)
可以告诉我,我可以一并讲清楚。
四、TCP三次握手
我们从原理到实战场景全面讲清楚 TCP 三次握手(Three-way Handshake),并且深入探讨你提到的重点问题:“是否可以省略第三次握手?”
🔍 一、什么是 TCP 三次握手?
三次握手是建立 TCP 连接时客户端和服务器之间必须进行的 三步通信流程,确保双方都具备发送和接收能力,才能正式开始数据传输。
🧠 二、三次握手详解(图 + 文)
lua
复制编辑
客户端 服务端 | ------ SYN -------> | (第一次握手:客户端发起请求) | <----- SYN-ACK -----| (第二次握手:服务端确认并回应) | ------ ACK ------->| (第三次握手:客户端确认)
✅ 第一次握手(客户端 → 服务端)
-
客户端发送一个带
SYN
标志的数据包。 -
表示:“我要连接你,并且我能发送数据了。”
-
此时客户端进入
SYN_SENT
状态。
✅ 第二次握手(服务端 → 客户端)
-
服务端收到
SYN
后,确认客户端能发送。 -
回复一个带
SYN+ACK
标志的包。 -
表示:“我知道你要来,我也可以发送数据。”
-
服务端进入
SYN_RECEIVED
状态。
✅ 第三次握手(客户端 → 服务端)
-
客户端收到
SYN+ACK
后,再发一个ACK
。 -
表示:“我知道你也准备好了,我们开始吧!”
-
此时双方进入
ESTABLISHED
状态,连接建立成功。
📌 三次握手的目的总结
目的 | 举例 |
---|---|
✅ 确保客户端能发送 | 第一次握手 |
✅ 确保服务端能接收并能发送 | 第二次握手 |
✅ 确保客户端能接收 | 第三次握手 |
❓是否可以省略第三次握手?
不能省略。
原因:
-
如果省略,服务端无法确认客户端“收到了”自己的 SYN+ACK。
-
网络不稳定或中间设备重发,可能导致服务端误以为连接成功,出现半连接(SYN Flood 漏洞)。
-
TCP 协议设计就是为了防止这种“单向认为建立连接”的问题。
🧪 示例场景:省略第三次握手会怎么样?
-
客户端发了 SYN,服务端发了 SYN+ACK,但客户端没有发 ACK 就宕机了。
-
如果不等待第三次握手:
-
服务端误以为连接建立。
-
客户端其实已经没了。
-
服务端一直占用连接资源,白白浪费(资源泄漏)。
-
🧱 补充:SYN Flood 攻击
攻击者连续伪造 IP 发送 SYN 包,但不发送第三次 ACK。
-
服务端进入
SYN_RECEIVED
,等待 ACK 却等不到,连接被挂起。 -
持续攻击会让服务端连接数爆满,无法为正常用户服务。
解决方案:
-
使用 SYN Cookie 技术。
-
连接队列限制。
✅ 总结口诀
三次握手不多余,确认彼此都OK。缺一不可防欺骗,少了连接不安全。
如果你还想进一步了解:
-
四次挥手断开连接机制
-
TCP 状态转换图(如
TIME_WAIT
,CLOSE_WAIT
) -
和 HTTP 的握手区别(如 HTTP/1.1 vs HTTP/2)
我可以继续为你展开。
五、子进程和主进程区别和API
在 Node.js 中,子进程(child process)用于执行一个独立的任务或运行另一个程序,与主进程并行执行、互不干扰。这是 Node 在单线程模型中实现多任务处理(并发/并行)的常用手段。
🧠 一、主进程 vs 子进程 的区别
对比项 | 主进程 | 子进程 |
---|---|---|
启动方式 | node 直接启动 | 主进程通过 child_process 模块创建 |
内存空间 | 独立进程,拥有自己的内存空间 | 也是独立的,和主进程不共享内存(需显式通信) |
执行上下文 | 当前运行的 Node.js 文件和上下文 | 可运行任意 Node.js 文件或外部命令 |
是否共享事件循环 | ❌ 不共享事件循环 | 每个子进程有自己的事件循环 |
通信方式 | - | 通过 IPC(进程间通信)或标准输入输出 |
错误影响 | 主进程出错影响全局 | 子进程崩溃不影响主进程,反之亦然 |
🚀 二、创建子进程的 4 个核心 API(child_process
模块)
你需要通过:
const child_process = require('child_process');
进行调用。
1️⃣ exec(command[, options], callback)
-
用于执行一个命令行命令,适合处理结果较小的任务。
-
会将子进程的输出(stdout 和 stderr)缓存在内存中,传给回调函数。
const { exec } = require('child_process');exec('ls -la', (err, stdout, stderr) => {if (err) {console.error('错误:', err);return;}console.log('标准输出:', stdout);
});
2️⃣ execFile(file[, args][, options][, callback])
-
直接执行一个可执行文件(比如
.exe
,.sh
,.py
),不会通过 shell 解析。 -
安全性更高、性能更好,适用于直接调用程序文件。
const { execFile } = require('child_process');execFile('node', ['--version'], (err, stdout) => {if (err) throw err;console.log('Node 版本是:', stdout);
});
3️⃣ spawn(command[, args][, options])
-
更底层,适合**处理大数据流(持续输出)**的子进程。
-
返回一个 ChildProcess 对象,你可以用
stdout.on('data')
来获取数据。
const { spawn } = require('child_process');const child = spawn('ping', ['baidu.com']);child.stdout.on('data', (data) => {console.log(`输出:${data}`);
});child.stderr.on('data', (data) => {console.error(`错误:${data}`);
});child.on('close', (code) => {console.log(`子进程退出码:${code}`);
});
4️⃣ fork(modulePath[, args][, options])
✅ 专为 Node.js 脚本间的多进程通信而设计,默认通过 IPC 通信(
process.send()
和message
事件)。
// parent.js
const { fork } = require('child_process');
const child = fork('./child.js');child.send({ msg: 'Hello from parent!' });child.on('message', (msg) => {console.log('父进程接收:', msg);
});
// child.js
process.on('message', (msg) => {console.log('子进程接收:', msg);process.send({ reply: 'Got it!' });
});
✅ 三、适用场景对比总结
API | 适合场景 | 是否支持通信 | 是否缓冲输出 |
---|---|---|---|
exec | 简单命令 + 小输出结果 | ❌ | ✅(内存缓存) |
execFile | 运行本地程序、脚本 | ❌ | ✅ |
spawn | 处理持续输出(如视频转码) | ❌ | ❌(流式处理) |
fork | Node 之间多进程通信 | ✅ | ❌ |
🔧 四、如何终止子进程?
使用:
child.kill(); // 默认发送 SIGTERM 信号
📦 五、补充推荐阅读
-
官方文档:https://nodejs.org/api/child_process.html
-
Node.js进程模型分析文章
-
Node.js高级编程书籍中的“多进程架构”
需要我结合实战:比如用子进程处理图片压缩、日志分析、CPU 密集计算等例子吗?或者再带你写一个完整的 fork 通信项目也可以。