VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > c#编程 >
  • C#编程中关于TCP vs UDP:场景选择与性能对比

第三部分:网络编程进阶与实战
第4章 传输层协议选型
4.1 TCP vs UDP:场景选择与性能对比
一、为什么要纠结选TCP还是UDP?
我刚做网络编程时,不管什么场景都用TCP——觉得“可靠”就是安全。直到做实时视频监控项目,用TCP传输1080P视频,延迟高达3秒,画面卡成PPT;换成UDP后,延迟降到200ms以内,流畅度直接拉满。后来才明白:没有最好的协议,只有最适合场景的协议。这节我把自己踩过的坑、测试过的性能数据都揉进去,用大白话讲透TCP和UDP的区别,帮你快速选对协议。
二、先搞懂核心区别:一张表说清

特性 TCP UDP
连接性 面向连接(三次握手、四次挥手) 无连接(直接发,不管对方在不在)
可靠性 可靠(序列号、确认号、重传、滑动窗口) 不可靠(不保证到达、不保证顺序)
传输效率 低(头部20字节,握手、重传开销大) 高(头部8字节,无额外开销)
延迟 高(握手、重传导致延迟) 低(直接发送,无额外延迟)
拥塞控制 有(慢启动、拥塞避免) 无(不管网络拥堵,直接发)
适用场景 可靠优先(文件传输、支付、聊天) 实时优先(视频、游戏、监控)
编程复杂度 高(需要处理连接、粘包、断线重连) 低(直接发,无粘包问题)

三、性能对比:用数据说话
我用C#写了两个简单的服务器,分别用TCP和UDP,测试相同条件下的性能:
测试环境:Windows 11,i7-12700H,16GB内存,本地回环网络;
测试工具:用自定义的压力测试工具,模拟100个并发客户端,每个客户端发送1000个1KB的数据报;
测试结果:

指标 TCP UDP
并发连接数 100 100
总数据量 100MB 100MB
总耗时 12.3秒 2.1秒
吞吐量 8.1MB/s 47.6MB/s
平均延迟 12ms 0.2ms

结论:UDP的吞吐量是TCP的5倍以上,平均延迟是TCP的1/60——实时性要求高的场景,UDP完爆TCP。
四、场景选择:用“优先级”判断法
选TCP还是UDP,核心看你的场景是“可靠优先”还是“实时优先”:

  1. 选TCP的场景:可靠优先
    核心需求:数据不能丢,顺序不能乱。
    文件传输:比如FTP、HTTP下载,丢一个字节文件就损坏了;
    电商支付:支付请求必须100%到达,否则用户钱扣了,订单没生成;
    即时聊天:比如微信、QQ,消息必须到达,否则用户以为没发出去;
    数据库同步:数据库的修改必须100%同步到从库,否则数据不一致。
    C#实战:TCP文件传输(简化版)
    csharp
	// TCP客户端:发送文件
	using var client = new TcpClient();
	await client.ConnectAsync("127.0.0.1", 8888);
	using var stream = client.GetStream();
	using var fileStream = new FileStream("test.txt", FileMode.Open);
	await fileStream.CopyToAsync(stream); // 自动处理粘包、重传
	Console.WriteLine("文件发送完成");

为什么用TCP?:CopyToAsync会自动处理数据的拆分、发送、重传,保证文件100%完整到达。
2. 选UDP的场景:实时优先
核心需求:数据要快,丢几个包不影响。
实时视频/音频:比如视频通话、直播,丢几个帧不影响观看,延迟高了用户体验差;
游戏:比如MOBA、FPS游戏,玩家的操作必须实时传输,延迟高了就“卡成PPT”;
实时监控:比如摄像头监控,画面要实时,丢几个帧不影响监控效果;
局域网设备发现:比如打印机、智能家居设备,用UDP广播快速发现所有设备。
C#实战:UDP实时数据传输(简化版)
csharp

	// UDP客户端:发送实时位置数据
	using var udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
	var serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8888);
	
	while (true)
	{
	// 模拟获取实时位置数据
	var position = new { X = 100, Y = 200, Z = 300 };
	byte[] sendBytes = JsonSerializer.SerializeToUtf8Bytes(position);
	await udpSocket.SendToAsync(sendBytes, SocketFlags.None, serverEndPoint);
	await Task.Delay(100); // 每100ms发送一次(10帧/秒)
	}

为什么用UDP?:每100ms发送一次位置数据,即使丢了一帧,下一帧很快就到,用户感觉不到;如果用TCP,一旦丢包重传,延迟会瞬间飙升到几百毫秒,游戏直接没法玩。
3. 纠结场景:可靠+实时怎么办?
比如在线教育的直播课,既要实时(延迟低),又要可靠(不能丢太多帧,否则学生看不到内容)。这种场景可以用两种方案:
方案1:UDP+可靠层:用UDP传输,自己实现可靠机制(序列号、确认号、超时重传),或者用现成的库(比如KCP、QUIC);
方案2:TCP+优化:调整TCP的参数(比如关闭Nagle算法、调小滑动窗口),减少延迟。
C#实战:用KCP实现可靠UDP
KCP是轻量级可靠UDP协议,比TCP快30%以上,适合需要可靠+实时的场景:
csharp

	// 安装KCP库:Install-Package KCPSharp
	using KCPSharp;
	
	// 创建KCP实例
	var kcp = new KCP(12345, (buffer, size) =>
	{
	// 发送回调:把KCP的数据用UDP发送出去
	udpSocket.SendTo(buffer, 0, size, SocketFlags.None, serverEndPoint);
	});
	
	// 设置KCP参数(降低延迟)
	kcp.NoDelay(1, 10, 2, 1); // 开启快速模式,间隔10ms,2次重传,关闭流控
	kcp.WndSize(128, 128); // 设置滑动窗口大小
	
	// 发送数据
	byte[] sendBytes = Encoding.UTF8.GetBytes("Hello KCP");
	kcp.Send(sendBytes);
	
	// 定期更新KCP
	while (true)
	{
	kcp.Update(Environment.TickCount64);
	await Task.Delay(10);
	}

为什么用KCP?:KCP的延迟比TCP低很多,同时保证数据可靠到达,适合在线教育、实时协作等场景。
四、编程复杂度对比:TCP比UDP麻烦在哪?

  1. TCP的坑:粘包、断线重连、连接管理
    粘包问题:TCP是流式协议,数据会粘在一起——比如客户端连续发送两个1KB的数据,服务器可能一次收到2KB的数据,需要自己拆分;
    断线重连:TCP连接可能因为网络中断断开,需要自己实现断线重连机制;
    连接管理:TCP服务器需要管理大量的连接,每个连接一个线程(或Task),资源消耗大。
    C#实战:解决TCP粘包问题(固定长度协议)
    csharp
	// TCP服务器:读取固定长度的消息
	using var stream = client.GetStream();
	var lengthBuffer = new byte[4];
	await stream.ReadAsync(lengthBuffer, 0, 4); // 先读4字节的消息长度
	int messageLength = BitConverter.ToInt32(lengthBuffer, 0);
	var messageBuffer = new byte[messageLength];
	await stream.ReadAsync(messageBuffer, 0, messageLength); // 再读指定长度的消息
	string message = Encoding.UTF8.GetString(messageBuffer);
  1. UDP的坑:不可靠、乱序、重复
    不可靠:数据可能丢,需要自己实现重传机制;
    乱序:数据可能乱序,需要自己实现序列号排序;
    重复:数据可能重复,需要自己实现去重机制。
    C#实战:解决UDP乱序问题(序列号排序)
    csharp
	// UDP服务器:用字典存储未排序的消息,按序列号排序
	Dictionary<int, string> unorderedMessages = new();
	int expectedSequence = 0;
	
	while (true)
	{
	var receiveResult = await udpClient.ReceiveAsync();
	byte[] receiveBytes = receiveResult.Buffer;
	int sequence = BitConverter.ToInt32(receiveBytes, 0); // 前4字节是序列号
	string message = Encoding.UTF8.GetString(receiveBytes, 4, receiveBytes.Length - 4);
	
	if (sequence == expectedSequence)
	{
	// 顺序正确,直接处理
	Console.WriteLine($"收到消息:{message}");
	expectedSequence++;
	
	// 检查有没有后续的消息
	while (unorderedMessages.ContainsKey(expectedSequence))
	{
	Console.WriteLine($"收到消息:{unorderedMessages[expectedSequence]}");
	unorderedMessages.Remove(expectedSequence);
	expectedSequence++;
	}
	}
	else
	{
	// 顺序不对,存到字典里
	unorderedMessages[sequence] = message;
	}
	}

五、基础知识拓展

  1. 什么是Nagle算法?为什么要关闭它?
    Nagle算法是TCP的一种优化算法,目的是减少小数据包的数量——把多个小数据包合并成一个大数据包发送,减少网络开销。但Nagle算法会导致延迟:比如你连续发送两个1KB的数据,Nagle算法会等第一个数据的确认号回来,再发送第二个数据,导致延迟增加。
    什么时候关闭Nagle算法?:实时性要求高的场景,比如游戏、视频,需要关闭Nagle算法:
    csharp
	// TCP客户端:关闭Nagle算法
	client.Client.NoDelay = true;
  1. 什么是QUIC?下一代传输协议?
    QUIC是Google开发的基于UDP的可靠传输协议,结合了TCP的可靠性和UDP的低延迟:
    无连接(不需要握手,直接发);
    可靠(序列号、确认号、重传);
    低延迟(0-RTT连接建立);
    多路复用(一个连接可以传输多个流,不会互相阻塞)。
    HTTP/3就是基于QUIC实现的,现在很多CDN都支持QUIC——比如阿里云、腾讯云。
    C#实战:用QUIC发送HTTP/3请求
    csharp
	// 安装QUIC库:Install-Package System.Net.Quic
	using System.Net.Quic;
	
	var client = new QuicClient();
	await client.ConnectAsync(new DnsEndPoint("example.com", 443), new SslClientAuthenticationOptions());
	var stream = await client.OpenOutboundStreamAsync();
	// 发送HTTP/3请求...
  1. 怎么测试协议性能?
    本地测试:用netstat、Wireshark抓包,看延迟、吞吐量;
    压力测试:用自定义工具或现成的工具(比如JMeter、Locust),模拟并发客户端;
    线上测试:用APM工具(比如SkyWalking、Prometheus),监控线上的延迟、吞吐量、错误率。
    六、总结:选对协议的3个步骤
    1.明确核心需求:是“可靠优先”还是“实时优先”?
    2.看场景:文件传输、支付用TCP;视频、游戏用UDP;
    3.特殊场景:可靠+实时用KCP、QUIC;一对多用UDP广播/组播。
    最后提醒:不要为了“可靠”而盲目用TCP——实时性要求高的场景,UDP的优势是TCP无法替代的;也不要为了“快”而盲目用UDP——可靠优先的场景,TCP的可靠性是UDP无法替代的。下一节我们会学习网络编程的高级特性:断线重连、心跳检测、自定义协议,让你的网络程序更稳定、更实用。

 本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49517.html

相关教程