C#通过NetworkStream传输图片出错求助
解决C# NetworkStream传输大数据(图片)丢失/损坏问题
核心问题分析
你的代码存在两个关键错误,直接导致图片传输损坏:
- 服务器写入流时误用缓冲区总长度:
stream.Read()返回的read变量是本次实际读取的字节数,最后一次读取时缓冲区大概率无法填满,此时写入data.Length会把多余的空字节写入MemoryStream,导致图片文件大小异常、损坏。 - 客户端未正确通知服务器数据发送完毕:发送完数据直接关闭TcpClient,可能导致服务器还未读取完所有数据就收到连接断开信号,
stream.Read()提前返回0,造成数据丢失。
快速修复方案
修正服务器代码
将写入MemoryStream的逻辑改为使用实际读取的字节数,同时优化循环逻辑避免无限等待:
// get image var ms = new MemoryStream(); int total = 0; int read = -1; // 将缓冲区声明移到循环外,避免重复创建对象 byte[] data = new byte[client.ReceiveBufferSize]; do { if (!stream.CanRead) { break; // 无法读取时直接退出循环,避免无限等待 } Console.WriteLine("get"); read = stream.Read(data, 0, data.Length); if (read > 0) { // 关键:使用实际读取的字节数写入,而非缓冲区总长度 ms.Write(data, 0, read); total += read; } } while (read > 0);
修正客户端代码
发送完数据后,先刷新流并通知服务器数据发送完毕,再关闭连接:
try { var stream = tcpClient.GetStream(); if (stream.CanWrite) { stream.Write(image, 0, image.Length); stream.Flush(); // 确保所有数据都写入网络流 // 通知服务器:客户端已完成发送,不会再发送数据 tcpClient.Client.Shutdown(SocketShutdown.Send); } } catch (Exception e) { Console.Error.WriteLine($"send error, {e.Message}"); } finally { tcpClient.Close(); }
更可靠的进阶方案:基于长度的传输
TCP是无边界的流协议,依赖连接关闭判断数据结束的方式不够可靠(比如连续传输多个文件时无法区分边界)。更专业的做法是先发送数据长度,再发送实际数据,让服务器精准知道需要接收多少字节:
客户端代码(带长度校验)
private static void SendData(byte[] image) { Console.WriteLine($"image byte size is {image.Length}"); File.WriteAllBytes("/Users/tim/Downloads/test_send.jpg", image); var tcpClient = new TcpClient(); try { tcpClient.Connect("127.0.0.1", 20000); var stream = tcpClient.GetStream(); // 使用BinaryWriter简化长度和数据的写入 using (var writer = new BinaryWriter(stream)) { // 先发送图片字节数组的长度(Int32类型,占4字节) writer.Write(image.Length); // 再发送图片数据 writer.Write(image); writer.Flush(); } // 通知服务器发送完毕 tcpClient.Client.Shutdown(SocketShutdown.Send); } catch (Exception e) { Console.Error.WriteLine($"TCP connect or send error, {e.Message}"); } finally { tcpClient.Close(); } }
服务器代码(带长度校验)
static void Main(string[] args) { int Port = 20000; TcpListener? server = null; try { server = new TcpListener(IPAddress.Any, Port); server.Start(); var client = server.AcceptTcpClient(); var stream = client.GetStream(); // 先读取图片数据的总长度 int imageLength; using (var reader = new BinaryReader(stream)) { imageLength = reader.ReadInt32(); } // 循环读取直到接收完所有数据 var ms = new MemoryStream(); int totalRead = 0; byte[] buffer = new byte[client.ReceiveBufferSize]; while (totalRead < imageLength) { // 计算本次最多能读取的字节数(避免超过剩余需要接收的长度) int readSize = Math.Min(buffer.Length, imageLength - totalRead); int read = stream.Read(buffer, 0, readSize); if (read == 0) { throw new IOException("连接意外中断,未接收完整图片数据"); } ms.Write(buffer, 0, read); totalRead += read; Console.WriteLine($"已接收 {totalRead}/{imageLength} 字节"); } Console.WriteLine("get image finish"); Console.WriteLine($"total is {totalRead}"); byte[] image = ms.ToArray(); Console.WriteLine($"the byte array size is {image.Length}"); File.WriteAllBytes("/Users/tim/Downloads/test_received.jpg", image); Console.WriteLine("saved"); } catch (Exception e) { Console.WriteLine($"Error: {e.Message}"); } finally { server?.Stop(); } }
效果验证
使用上述方案后,服务器接收的字节数组长度会和客户端发送的完全一致,图片不会再出现损坏或丢失的情况。
内容的提问来源于stack exchange,提问作者Tim
相关产品推荐
相关产品推荐

