c#软件开发学习笔记--网络通讯、TCP、Socket

网络通讯 / 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 连接的三个阶段

  1. 建立连接:三次握手
  2. 数据传输:全双工通信
  3. 释放连接:四次挥手

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#)

  1. 创建一端:使用 UdpClient配合 IPEndPoint
  2. 发送数据:udpClient.Send()
  3. 启动另一端:使用 UdpClient
  4. 接收数据:Task.Run() + udpClient.Receive()

4. TCP 与 UDP 对比

对比维度TCPUDP
连接方式面向连接无连接
系统资源较多较少
程序结构较复杂较简单
传输模式流模式数据报模式
数据正确性保证可能丢包
数据顺序保证不保证
可靠性
适用场景大数据量、持续连接小数据量、高突发、高频率

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 简易聊天室服务器

该项目在基础服务端之上扩展了多客户端管理广播/私发能力,是综合应用案例。

核心设计

  1. 客户端管理:使用字典 + 列表双重存储

    // 字典:键为客户端终端,值为客户端 Socket
    Dictionary<EndPoint, Socket> clients = new Dictionary<EndPoint, Socket>();
    // 列表:作为下拉框数据源
    List<Client> clients2 = new List<Client>();
    
  2. 启动/关闭服务器

    /// <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();
    }
    
  3. 广播 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));
        }
    }
    
  4. 自定义断开协议:服务器与客户端事先约定,发送 "n1" 字符串作为断开标识,比依赖默认 Socket 异常更可控。


7. 关键 API 速查表

7.1 核心类

命名空间用途
SocketSystem.Net.Sockets底层套接字,支持 TCP/UDP
TcpListenerSystem.Net.SocketsTCP 服务器封装
TcpClientSystem.Net.SocketsTCP 客户端封装
UdpClientSystem.Net.SocketsUDP 通讯封装
IPEndPointSystem.Net终端对象(IP + 端口)
IPAddressSystem.NetIP 地址
DnsSystem.Net域名解析
CancellationTokenSourceSystem.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 线程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值