网络通讯 / Socket / TCP 学习总结
1. 网络协议体系结构
1.1 OSI 七层模型
OSI(Open System Interconnect) 即开放式系统互联,由 ISO(国际标准化组织)于 1985 年研究提出,定义了网络互连的七层框架。
| 层级 | 名称 | 主要功能 | 常见协议 |
|---|---|---|---|
| 7 | 应用层 | 为用户提供应用接口与网络服务 | HTTP、HTTPS、FTP、SMTP、DNS、Telnet |
| 6 | 表示层 | 数据格式转换、加密压缩 | ASCII、UTF-8、Unicode、SSL/TLS、JPEG、DES |
| 5 | 会话层 | 建立、管理、终止通信会话 | ADSP、RPC、SQL |
| 4 | 传输层 | 端到端可靠数据传输,差错/流量控制 | TCP、UDP、SPX |
| 3 | 网络层 | IP 寻址、路由选择 | IP、ICMP、ARP、RARP、IGMP |
| 2 | 数据链路层 | 成帧、MAC 地址访问、差错检测 | HDLC、PPP、VLAN、Wi-Fi(802.11) |
| 1 | 物理层 | 比特流与电信号/光信号互换 | UART、RS-232/485、RJ-45、V.35 |
数据传输流程(以服务器向客户端发送数据为例):
- 发送端:应用层 → 表示层 → 会话层 → 传输层 → 网络层 → 数据链路层 → 物理层
- 物理层通过物理介质以比特流方式传送到对端物理层
- 接收端:物理层 → 数据链路层 → 网络层 → 传输层 → 会话层 → 表示层 → 应用层
1.2 TCP/IP 五层模型
TCP/IP 协议参考 OSI 体系结构,将其简化为五层。相比七层模型,结构更简单、效率更高、成本更低,在实际应用中更为普及。
2. TCP 协议详解
2.1 基本概念
TCP(Transmission Control Protocol)传输控制协议,是 OSI 七层框架中传输层的协议,是 TCP/IP 协议簇中的重要协议。
TCP 是一种:
- 面向连接的
- 可靠的
- 基于字节流的
- 传输层通信协议
2.2 TCP 特点
| 特性 | 说明 |
|---|---|
| 有序 | 保证数据按顺序到达 |
| 安全 | 通过连接机制保障通信安全 |
| 可靠 | 通过确认、重传机制保证送达 |
| 不易丢包 | 流量控制 + 拥塞控制 |
| 延时性高 | 建立连接与确认带来额外延迟 |
主要应用场景:聊天室、数据传输等需要可靠通信的场景。
2.3 TCP 连接的三个阶段
- 建立连接:三次握手
- 数据传输:全双工通信
- 释放连接:四次挥手
2.4 三次握手(建立连接)
目的:在客户端与服务器之间建立一个可信的连接通道。
| 步骤 | 方向 | 报文内容 | 状态变化 |
|---|---|---|---|
| 第一次握手 | 客户端 → 服务器 | SYN=1, seq=100(请求建立连接) | 客户端进入 SYN_SENT |
| 第二次握手 | 服务器 → 客户端 | SYN=1, ACK=1, seq=300, ack=101(确认请求并回应) | 服务器进入 SYN_RECV |
| 第三次握手 | 客户端 → 服务器 | ACK=1, seq=101, ack=301(确认收到响应) | 双方进入 ESTABLISHED |
通俗理解:客户端发两次、服务器响应一次,双方共发送三次报文以建立信任。
延伸问题:为什么必须是三次握手?两次不行吗?
因为需要考虑连接丢包问题。若没有第三次握手,客户端一直未收到服务器响应时,服务器却已建立连接,会造成资源浪费与状态不一致。
2.5 四次挥手(释放连接)
目的:关闭一个已建立的 TCP 连接。
| 步骤 | 方向 | 报文内容 | 含义 |
|---|---|---|---|
| 第一次挥手 | 客户端 → 服务器 | FIN 报文 | 客户端数据发送完毕,请求释放连接 |
| 第二次挥手 | 服务器 → 客户端 | ACK 确认报文 | 服务器确认收到客户端的 FIN |
| 第三次挥手 | 服务器 → 客户端 | FIN 报文 | 服务器数据发送完毕,请求释放连接 |
| 第四次挥手 | 客户端 → 服务器 | ACK 确认报文 | 客户端确认收到服务器的 FIN |
2.6 报文标识位含义
- SYN:标识客户端意图。SYN=1 表示请求连接;SYN=2 表示请求数据;SYN=3 表示释放连接
- ACK:标识服务器是否收到请求。ACK=1 且 ack=客户端 seq+1 表示已收到
- FIN:发送端完成发送任务
- RST:连接出现错误,必须释放连接并重建
- PSH:接收方应尽快将数据交给应用层
- URG:紧急数据,需尽快传送
2.7 TCP 报文头部结构
TCP 报文头部共 32 字节,其中 20 字节为固定首部。
| 字段 | 长度 | 说明 |
|---|---|---|
| 源端口 | 2 字节 | 与 IP 头源 IP 共同标识发送方 |
| 目的端口 | 2 字节 | 与 IP 头目的 IP 共同标识接收方 |
| 序号 (seq) | 4 字节 | 本报文段第一个字节的序号(32 位无符号,到达 2^32-1 后从 0 开始) |
| 确认序号 (ack) | 4 字节 | 期望收到对方下一个报文段的第一个字节序号(ACK=1 时有效) |
| 数据偏移 | 4 比特 | 表示首部长度,单位 4 字节,首部最大 60 字节 |
| 保留 | 6 比特 | 供以后使用,置 0 |
| 标志位 | 6 比特 | URG / ACK / PSH / RST / SYN / FIN |
| 窗口 | 2 字节 | 滑动窗口,用于流量控制,最大 65535 字节 |
| 检验和 | - | 覆盖整个 TCP 报文段(首部+数据),收发端必须计算验证 |
2.8 TCP 连接的组成
一个 TCP 连接由一个四元组构成:
{ 源IP, 源端口, 目的IP, 目的端口 }
- 服务器身份:IP 地址 + 端口
- 客户端身份:IP 地址 + 端口
- 一个 IP + 一个端口 = 一个 socket 插口
3. UDP 协议详解
3.1 基本概念
UDP(User Datagram Protocol)用户数据报协议,是一种:
- 无连接的
- 不可靠的
- 面向数据报的
- 传输层通信协议
3.2 UDP 特点
- 无需建立连接即可通信
- 不使用拥塞控制
- 不保证可靠交付,最大努力交付
- 容易造成数据丢失,易读到脏数据
- 适合小数据量、高突发性、高频率的网络通信
3.3 UDP 通讯过程(C#)
- 创建一端:使用
UdpClient,配合IPEndPoint类 - 发送数据:
udpClient.Send() - 启动另一端:使用
UdpClient - 接收数据:
Task.Run()+udpClient.Receive()
4. TCP 与 UDP 对比
| 对比维度 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接 | 无连接 |
| 系统资源 | 较多 | 较少 |
| 程序结构 | 较复杂 | 较简单 |
| 传输模式 | 流模式 | 数据报模式 |
| 数据正确性 | 保证 | 可能丢包 |
| 数据顺序 | 保证 | 不保证 |
| 可靠性 | 高 | 低 |
| 适用场景 | 大数据量、持续连接 | 小数据量、高突发、高频率 |
5. Socket 套接字
5.1 什么是 Socket
Socket 即套接字,本质是一个"外壳",是对网络中不同主机上应用程序之间进行双向通信端点的抽象。
5.2 Socket 与 TCP/UDP 的关系
Socket 是套接字,本质是对 TCP 和 UDP 做一层封装,让开发者无需关注协议底层原理即可使用。
- Socket 是应用层与 TCP/IP 协议簇通信的中间软件抽象层
- 将传输层封装后,方便应用层调用
- TCP 和 UDP 是协议,而 Socket 是一种技术框架
5.3 C# 中的 Socket 封装层次
应用层代码
↓
TcpListener / TcpClient / UdpClient ← .NET Framework 推荐使用的封装类
↓
Socket 类 ← 底层 Socket 实现,可同时支持 TCP/UDP
↓
TCP / UDP 协议
5.4 Socket 通信要点
- 客户端:可发送也可接收,发送和接收都使用客户端实例
- 服务器:可发送也可接收,但接收和发送并非服务器"自身"完成,而是通过
Accept()获取连接的客户端实例,由该客户端实例代为收发
6. C# Socket 编程实践
6.1 基础服务端
服务端核心 5 步流程:
// 1. 创建服务器对象:指定 IPv4、流式传输、TCP 协议
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 2. 绑定 IP 和端口
IPAddress address = IPAddress.Parse(ipStr);
IPEndPoint endPoint = new IPEndPoint(address, port);
server.Bind(endPoint);
// 3. 开启监听(参数为容纳客户端数量)
server.Listen(100);
// 4. 接收客户端连接请求(阻塞,返回客户端 Socket)
Socket client = server.Accept();
// 5. 接收客户端发来的数据
byte[] buffer = new byte[client.Available];
int length = client.Receive(buffer);
多客户端接收实现:使用 Task.Run + CancellationTokenSource 双层嵌套,外层循环接收新客户端连接,内层循环持续读取已连接客户端的数据。
/// <summary>
/// 接收多个客户端连接,并为每个客户端单独开线程读取数据
/// </summary>
public void AcceptDataMul()
{
cts = new CancellationTokenSource();
Task.Run(() =>
{
while (!cts.IsCancellationRequested)
{
// 外层:循环接收新客户端连接
Socket client = server.Accept();
if (client != null && client.Connected)
{
// 内层:为每个客户端开启独立线程读取数据
cts2 = new CancellationTokenSource();
Task.Run(() =>
{
while (!cts2.IsCancellationRequested)
{
byte[] buffer = new byte[client.Available];
int length = client.Receive(buffer);
// 通过 Invoke 回到 UI 线程更新控件
Invoke(new Action(() =>
{
richTextBox1.AppendText(Encoding.UTF8.GetString(buffer));
}));
}
}, cts2.Token);
}
}
}, cts.Token);
}
6.2 基础客户端
客户端核心流程:
// 1. 创建客户端实例
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 2. 创建终端(服务器的 IP 和端口)
IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse("192.168.219.12"), 63323);
// 3. 异步连接服务器(避免阻塞 UI 线程)
await client.ConnectAsync(endpoint);
// 4. 发送数据
byte[] data = Encoding.UTF8.GetBytes(textBox1.Text);
client.Send(data);
// 5. 异常时优雅关闭
if (client.Connected) client.Shutdown(SocketShutdown.Both);
client.Close();
client.Dispose();
6.3 简易聊天室服务器
该项目在基础服务端之上扩展了多客户端管理与广播/私发能力,是综合应用案例。
核心设计:
-
客户端管理:使用字典 + 列表双重存储
// 字典:键为客户端终端,值为客户端 Socket Dictionary<EndPoint, Socket> clients = new Dictionary<EndPoint, Socket>(); // 列表:作为下拉框数据源 List<Client> clients2 = new List<Client>(); -
启动/关闭服务器:
/// <summary> /// 打开服务器:创建 Socket → 绑定 → 监听 /// </summary> public void StartServer() { socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socketServer.Bind(new IPEndPoint(IPAddress.Parse(txtIP.Text), int.Parse(txtPort.Text))); socketServer.Listen(1000); } /// <summary> /// 关闭服务器:先向所有客户端发送 "n1" 断开信号,再断开连接 /// </summary> public void CloseServer() { foreach (var item in clients2) { item.Socket.Send(Encoding.UTF8.GetBytes("n1")); // 自定义断开标识 item.Socket.Disconnect(false); } cts1.Cancel(); socketServer.Close(); } -
广播 vs 私发:
if (checkBox1.Checked) // 广播:遍历所有客户端发送 { foreach (var item in clients2) { item.Socket.Send(Encoding.UTF8.GetBytes("服务器说:" + textBox1.Text)); } } else // 私发:从下拉框选中特定客户端 { Socket s1 = (Socket)comboBox1.SelectedValue; if (s1 != null && s1.Connected) { s1.Send(Encoding.UTF8.GetBytes("服务器说:" + textBox1.Text)); } } -
自定义断开协议:服务器与客户端事先约定,发送
"n1"字符串作为断开标识,比依赖默认 Socket 异常更可控。
7. 关键 API 速查表
7.1 核心类
| 类 | 命名空间 | 用途 |
|---|---|---|
Socket | System.Net.Sockets | 底层套接字,支持 TCP/UDP |
TcpListener | System.Net.Sockets | TCP 服务器封装 |
TcpClient | System.Net.Sockets | TCP 客户端封装 |
UdpClient | System.Net.Sockets | UDP 通讯封装 |
IPEndPoint | System.Net | 终端对象(IP + 端口) |
IPAddress | System.Net | IP 地址 |
Dns | System.Net | 域名解析 |
CancellationTokenSource | System.Threading | 取消异步任务 |
7.2 Socket 关键方法
| 方法 | 说明 | 所属 |
|---|---|---|
new Socket(...) | 创建 Socket,需指定地址族/类型/协议 | 服务端 & 客户端 |
Bind(IPEndPoint) | 绑定 IP 和端口 | 服务端 |
Listen(int) | 开始监听,参数为等待队列最大长度 | 服务端 |
Accept() | 阻塞等待客户端连接,返回客户端 Socket | 服务端 |
Receive(byte[]) | 接收数据,返回字节数 | 服务端 & 客户端 |
Send(byte[]) | 发送数据,返回发送字节数 | 服务端 & 客户端 |
Connect(IPEndPoint) | 同步连接服务器 | 客户端 |
ConnectAsync(IPEndPoint) | 异步连接服务器(推荐) | 客户端 |
Shutdown(SocketShutdown.Both) | 禁用 Socket 的发送和接收 | 通用 |
Close() | 关闭 Socket | 通用 |
Dispose() | 释放资源 | 通用 |
7.3 AddressFamily / SocketType / ProtocolType 常用枚举
// 地址族
AddressFamily.InterNetwork // IPv4
AddressFamily.InterNetworkV6 // IPv6
// Socket 类型
SocketType.Stream // 流式传输(TCP 用)
SocketType.Dgram // 数据报传输(UDP 用)
// 协议类型
ProtocolType.Tcp // TCP 协议
ProtocolType.Udp // UDP 协议
7.4 获取本机 IP
IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
foreach (IPAddress item in host.AddressList)
{
if (item.AddressFamily == AddressFamily.InterNetwork) // 筛选 IPv4
{
ipStr = item.ToString();
}
}
8. 面试高频考点
Q1:三次握手为什么不是两次?
两次握手无法处理丢包问题。若客户端的请求丢失,服务器已建立连接会浪费资源;若服务器响应丢失,客户端无法确认连接是否建立。第三次握手让客户端确认服务器响应,建立双向信任通道。
Q2:三次握手与四次挥手的区别?
| 维度 | 三次握手 | 四次挥手 |
|---|---|---|
| 目的 | 建立连接 | 关闭连接 |
| 报文数 | 3 个 | 4 个 |
| 发起方 | 客户端 | 客户端 |
| 原因 | 双方需要互相同步初始序列号 | TCP 全双工,两个方向需独立关闭 |
Q3:TCP 与 UDP 如何选择?
- 需要可靠传输、文件传输、聊天室 → TCP
- 实时性要求高、可容忍丢包、视频流/DNS/广播 → UDP
Q4:Socket 是协议吗?
不是。Socket 不是协议,而是对 TCP/UDP 协议的封装,是应用层与 TCP/IP 协议簇通信的中间软件抽象层,提供编程接口。
Q5:TCP 报文头部有哪些关键字段?
源端口、目的端口、序号(seq)、确认序号(ack)、数据偏移、标志位(URG/ACK/PSH/RST/SYN/FIN)、窗口、检验和。
Q6:什么是滑动窗口?
TCP 通过滑动窗口机制进行流量控制,窗口大小表示接收端可提供的缓冲区大小(最大 65535 字节),协调通信双方的工作节奏,防止快速发送方淹没慢速接收方。
学习要点总结
| 主题 | 核心结论 |
|---|---|
| 协议体系 | OSI 七层 / TCP-IP 五层,TCP/UDP 都在传输层 |
| TCP | 面向连接、可靠、字节流;三次握手建连 + 四次挥手释放 |
| UDP | 无连接、不可靠、数据报;适合高频小数据 |
| Socket | 不是协议,是 TCP/UDP 的封装层,提供编程接口 |
| C# 实践 | 服务端:Bind → Listen → Accept → Receive;客户端:Connect → Send |
| 多客户端 | 使用 Task.Run + CancellationTokenSource 双层嵌套 |
| UI 更新 | 子线程更新控件必须通过 Invoke 回到 UI 线程 |


被折叠的 条评论
为什么被折叠?



