一篇文章搞懂 Socket:TCP 握手挥手 + 三种 Node.js 实现方案目录一、Socket 基础概念与底层原理1、什么是 Socket2、TCP 三次握手与四次挥手3、TCP 与 UDP 的区别4、Socket 的实际应用场景二、基于 Net 模块的 Socket 编程2.1 ServerSocket.js2.2 ClientSocket.js三、基于 WebSocket 的 Socket 编程3.1 WebSocketServer.js3.2 index.html3.3 WsClient.js四、基于 Socket.io 的 Socket 编程4.1 SocketIoServer.js4.2 index.htmlsocket.io.js五、三种方案对比与核心要点总结一、Socket 基础概念与底层原理1、什么是 SocketSocket(套接字),直译过来就是"插座"的意思——把两台机器之间的网络通信想象成插头和插座,一头插服务端,一头插客户端,通了之后数据就能来回跑了。说白了,Socket 就是对 TCP/IP 协议族的封装。它把三次握手、数据分包、确认应答这些底层的东西全给你处理好了,开发者只需要调connect()、send()、recv()、close()这几个接口就能搞定网络通信,不用关心底层怎么实现的。一个完整的 Socket 通信流程:服务端:创建 Socket → 绑定 IP 和端口(bind)→ 监听(listen)→ 等待客户端连接(accept)→ 收发数据 → 关闭连接客户端:创建 Socket → 连接服务端(connect)→ 收发数据 → 关闭连接在 Node.js 中,net模块就是对 TCP Socket 的封装,写起来比直接用 C 语言的 Socket API 要简单得多。2、TCP 三次握手与四次挥手三次握手(建立连接,面试必问):为什么是三次?简单说——双方都要确认彼此的收发能力没问题。# 第一次(客户端 → 服务端):SYN客户端:嘿,能连你吗?(发送 SYN 包,seq=x)# 第二次(服务端 → 客户端):SYN + ACK服务端:收到,我这边也没问题(发送 SYN+ACK,seq=y,ack=x+1)# 第三次(客户端 → 服务端):ACK客户端:好的,确认了(发送 ACK,ack=y+1)三次走完,TCP 连接建立。之后双方就能在这个连接上收发数据了。四次挥手(断开连接):为什么挥手比握手多一次?因为 TCP 是全双工的——双方都能独立地收发数据。一方说"我没话说了",另一方可能还有东西没讲完,得等它也说完了才能彻底断开。# 第一次(主动方 → 被动方):FIN主动方:我没数据要发了,可以断吗?# 第二次(被动方 → 主动方):ACK被动方:收到,但我还有数据没发完,稍等# 第三次(被动方 → 主动方):FIN被动方:好了,我这边也发完了,断吧# 第四次(主动方 → 被动方):ACK主动方:收到。(等待 2MSL 后彻底关闭,防止最后的 ACK 丢失)注:用 Node.js 的net模块写 Socket 时,不需要手动管这些 SYN/ACK 包——TCP 协议栈已经帮你搞定了。但了解这个过程,对排查网络问题、理解长连接和连接池很有帮助。3、TCP 与 UDP 的区别面试高频考点,记住这张表就行:对比维度TCPUDP连接方式面向连接,需要握手无连接,直接发包可靠性可靠,丢包重传不可靠,发了不管数据顺序保证顺序不保证传输速度相对慢(有确认机制)快(没那么多校验)头部开销20 字节8 字节典型场景网页、文件传输、邮件直播、语音、在线游戏简单记:TCP 稳但慢,UDP 快但不靠谱。Node.js 中,net模块走的是 TCP,dgram模块可以操作 UDP。实际开发中,绝大多数场景用 TCP 就够了,视频直播、实时游戏这类对延迟极度敏感的才会考虑 UDP。4、Socket 的实际应用场景Socket 编程不是只存在于教科书里,日常开发到处都是:即时通讯(IM):微信、QQ 的消息收发,底层就是 TCP 长连接,服务端收到消息推送给目标用户在线游戏:MOBA、吃鸡类游戏需要毫秒级实时同步,通常走 UDP(关键数据走 TCP 保可靠性)远程桌面:向日葵、TeamViewer 这类工具,持续传输屏幕画面和控制指令App 推送:手机 App 保持一条 TCP 长连接收推送IoT 设备通信:智能家居设备(摄像头、门锁等)和服务端之间的通信微服务间通信:两个 Node.js 服务之间直接用net模块通信,比 HTTP 少一层开销后面讲的三种方案——Net 模块、WebSocket、Socket.io,本质上都是 Socket 编程的应用,只是封装层级和适用场景不同。二、基于 Net 模块的 Socket 编程2.1 ServerSocket.jsconstnet=require('net')constserver=newnet.createServer()letclients={}letclientName=0server.on('connection',(client)={client.name=++clientName clients[client.name]=client client.on('data',(msg)={// console.log('客户端传来:' + msg);broadcast(client,msg.toString())})client.on('error',(e)={console.log('client error'+e);client.end()})client.on('close',(data)={deleteclients[client.name]console.log(client.name+' 下线了');})})functionbroadcast(client,msg){for(varkeyinclients){clients[key