通过TCP发送Zip文件至客户端后文件损坏/无效的问题排查
二进制文件(如Zip)TCP传输问题排查与解决
你的问题核心在于客户端用文本处理方式(StreamWriter+Encoding.ASCII)读取并写入二进制的Zip文件:
- 普通文本文件(.txt/.cs/.csv)的内容大多在ASCII编码范围内,用ASCII转换不会破坏数据;但Zip文件是二进制格式,包含大量超过ASCII范围(0x80及以上)的字节,
Encoding.ASCII会把这些字节强制替换成0x3F(问号),导致文件内容隐性损坏,哈希值自然不同——哪怕文件大小看起来一致。
正确传输二进制文件的注意事项
- 全程使用二进制流处理,绝对避免任何文本编码转换操作
- TCP是流协议,不能靠
Available判断数据是否就绪,也不能用“最后一包长度是否为1024”作为结束条件(存在刚好整包的情况),最可靠的方式是先发送文件总长度,客户端根据长度精准接收对应字节数 - 写入文件时用
FileStream而非StreamWriter,前者专门处理二进制数据 - 异步操作必须加
await等待完成,否则会出现流程错乱
修正后的服务器代码
using System.Net.Sockets; using System.Net; using System.IO.Compression; string filename = @"C:\BabyServerZip\ServerFiles.zip"; // 重新生成Zip文件 File.Delete(filename); ZipFile.CreateFromDirectory(@"C:\BabyServerSend", filename); IPHostEntry ipHostInfo = Dns.GetHostEntry("localhost"); IPAddress ipAddress = ipHostInfo.AddressList[0]; IPEndPoint endpoint = new(ipAddress, 58008); using Socket listener = new(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); listener.Bind(endpoint); listener.Listen(); // 等待客户端连接 using var handler = await listener.AcceptAsync(); // 先发送文件长度(用4字节int标识) byte[] fileBytes = File.ReadAllBytes(filename); int fileLength = fileBytes.Length; byte[] lengthBuffer = BitConverter.GetBytes(fileLength); await handler.SendAsync(lengthBuffer, SocketFlags.None); // 发送文件内容 await handler.SendAsync(fileBytes, SocketFlags.None);
修正后的客户端代码
using System.Net; using System.Net.Sockets; string outputPath = @"C:\BabyClientReceive\sent.zip"; IPHostEntry ipHostInfo = Dns.GetHostEntry("localhost"); IPAddress serverIP = ipHostInfo.AddressList[0]; IPEndPoint clientEnd = new(serverIP, 58008); using Socket clientSocket = new(clientEnd.AddressFamily, SocketType.Stream, ProtocolType.Tcp); // 等待连接完成 await clientSocket.ConnectAsync(clientEnd); // 先接收文件长度 byte[] lengthBuffer = new byte[4]; int receivedLength = await clientSocket.ReceiveAsync(lengthBuffer, SocketFlags.None); if (receivedLength != 4) { Console.WriteLine("接收文件长度失败"); return; } int fileLength = BitConverter.ToInt32(lengthBuffer, 0); // 接收文件内容 using FileStream fs = new FileStream(outputPath, FileMode.Create); int totalReceived = 0; byte[] buffer = new byte[1024]; while (totalReceived < fileLength) { int received = await clientSocket.ReceiveAsync(buffer, SocketFlags.None); if (received == 0) break; // 连接断开 await fs.WriteAsync(buffer.AsMemory(0, received)); totalReceived += received; } Console.WriteLine($"文件接收完成,共接收{totalReceived}字节");
关键修正点说明
- 服务器先发送文件总长度,客户端据此精准接收,彻底解决流数据的边界判断问题
- 客户端用
FileStream直接写入二进制字节,完全跳过文本编码转换,保证数据原样保存 - 所有异步操作添加
await,确保流程执行顺序正确 - 使用
using语句自动释放Socket和文件流资源,避免内存泄漏
内容的提问来源于stack exchange,提问作者Critical_Thought_404
相关产品推荐
相关产品推荐

