C# Socket跨PC文件传输异常排查求助:调试正常运行报错
C# Socket跨PC文件传输调试正常但直接运行异常排查
问题描述
编写的C# Socket跨PC文件传输代码仅在调试单步执行时功能正常,直接运行会随机触发两个异常:
- EXCEPTION A:Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.
- EXCEPTION B:Could not find a part of the path PartialPathToFile
原代码
服务端代码
internal async Task TransferProc() { while (Index < fileSize) { try { FS!.Position = Index; var read = await FS.ReadAsync(file_buffer, 0, file_buffer.Length); await FS.FlushAsync(); using var ms = new MemoryStream(); using var pw = new BinaryWriter(ms); { if(!isFileNameSent) { FS = new FileStream(somefile, FileMode.OpenOrCreate); pw.Write(filename); pw.Write(fileSize); isFileNameSent= true; } pw.Write(file_buffer); Transferred += read; Index += read; await SendData(ms.ToArray()) } } catch (Exception) { throw; } } //Cleanup Code } var SocketWriteStream = _baseSocket.OutputStream.AsStreamForWrite(); var SocketReadStream = _baseSocket.InputStream.AsStreamForRead(); public Task SendData(byte[] data) { lock (lockObj) // necessary as method is called by multiple objects to transfer data. { try { //Send the size of the data. SocketWriteStream!.WriteAsync(BitConverter.GetBytes(data!.Length), 0, 4); //And then the actual data. SocketWriteStream.WriteAsync(data, 0, data.Length); SocketWriteStream.FlushAsync(); } catch (Exception) { throw; } } }
客户端代码
public async Task<bool> StartClient() { while (true) { try { var bytesRead = await SocketReadStream!.ReadAsync(_buffer!, 0, 4); if (bytesRead > 0) { var size = BitConverter.ToInt32(_buffer!, 0); if (size > _buffer.Length) { _buffer = new byte[size]; Debug.WriteLine($"Buffer size {_buffer.Length} and size {size}"); } var read = await SocketReadStream.ReadAsync(_buffer!, 0, size); //EXCEPTION A await SocketReadStream.FlushAsync(); using var pr = new BinaryReader(new MemoryStream(_buffer)); if(!isFileNameRecd) { var fileName = pr.ReadString(); var length = pr.ReadInt64(); var path= Path.Combine(someFolder, fileName) FS = new FileStream(path, FileMode.OpenOrCreate); //EXCEPTION B isFileNameRecd= true; } var bytes = pr.ReadBytes(size); await FS.WriteAsync(bytes, 0, bytes.Length); await FS.FlushAsync(); } else { Debug.WriteLine("Run Closed"); } } catch (Exception e) { Debug.WriteLine($"Socket was closed! due to {e.Message} and {e.InnerException?.Message}"); throw; } } }
问题分析与修复
核心问题1:EXCEPTION A触发原因及修复
- 服务端SendData未await异步操作:原代码中
WriteAsync和FlushAsync没有用await,导致数据发送乱序、不完整,客户端读到错误的数据包长度。 - 客户端未循环读取完整数据包:
ReadAsync不一定能一次读完指定长度的字节,需要循环读取直到拿到完整数据。 - 服务端写入无效数据:原代码写入整个
file_buffer而非实际读取的字节数,会发送缓冲区中的旧数据;客户端读取时未扣除已解析的元数据长度,直接读取size字节会超出流范围。
服务端修正代码
internal async Task TransferProc() { // 提前打开文件流,避免循环中重复覆盖 using var FS = new FileStream(somefile, FileMode.Open, FileAccess.Read); fileSize = new FileInfo(somefile).Length; Index = 0; Transferred = 0; isFileNameSent = false; while (Index < fileSize) { try { FS.Position = Index; var read = await FS.ReadAsync(file_buffer, 0, file_buffer.Length); if (read == 0) break; // 读取到0字节说明文件已读完 using var ms = new MemoryStream(); using var pw = new BinaryWriter(ms); if(!isFileNameSent) { pw.Write(filename); pw.Write(fileSize); isFileNameSent = true; } // 仅写入实际读取的字节数,避免发送无效数据 pw.Write(file_buffer, 0, read); Transferred += read; Index += read; await SendData(ms.ToArray()); } catch (Exception) { throw; } } } public async Task SendData(byte[] data) { lock (lockObj) { try { // await异步操作,确保数据发送完成再释放锁 await SocketWriteStream!.WriteAsync(BitConverter.GetBytes(data!.Length), 0, 4); await SocketWriteStream.WriteAsync(data, 0, data.Length); await SocketWriteStream.FlushAsync(); } catch (Exception) { throw; } } }
客户端修正代码(EXCEPTION A部分)
public async Task<bool> StartClient() { _buffer = _buffer ?? new byte[4096]; isFileNameRecd = false; FileStream? FS = null; long totalReceived = 0; long fileSize = 0; try { while (true) { // 循环读取完整的4字节长度数据 int bytesRead = 0; while (bytesRead < 4) { int read = await SocketReadStream!.ReadAsync(_buffer, bytesRead, 4 - bytesRead); if (read == 0) { Debug.WriteLine("Run Closed"); return false; } bytesRead += read; } var size = BitConverter.ToInt32(_buffer, 0); if (size > _buffer.Length) { _buffer = new byte[size]; } // 循环读取完整的数据包内容 int totalRead = 0; while (totalRead < size) { int read = await SocketReadStream.ReadAsync(_buffer, totalRead, size - totalRead); if (read == 0) { Debug.WriteLine("Run Closed"); return false; } totalRead += read; } using var ms = new MemoryStream(_buffer, 0, size); using var pr = new BinaryReader(ms); if(!isFileNameRecd) { var fileName = pr.ReadString(); fileSize = pr.ReadInt64(); var path = Path.Combine(someFolder, fileName); // 确保目标文件夹存在 Directory.CreateDirectory(Path.GetDirectoryName(path)!); // 检查文件名合法性 if (Path.GetInvalidFileNameChars().Any(c => fileName.Contains(c))) { throw new ArgumentException($"文件名包含非法字符: {fileName}"); } FS = new FileStream(path, FileMode.Create, FileAccess.Write); isFileNameRecd = true; } // 读取剩余的文件数据(扣除已解析的元数据长度) long remaining = ms.Length - ms.Position; var bytes = pr.ReadBytes((int)remaining); await FS!.WriteAsync(bytes, 0, bytes.Length); await FS.FlushAsync(); totalReceived += bytes.Length; // 文件接收完成后重置状态,支持后续文件传输 if (totalReceived >= fileSize) { Debug.WriteLine("文件接收完成"); await FS.DisposeAsync(); FS = null; isFileNameRecd = false; totalReceived = 0; fileSize = 0; } } } catch (Exception e) { Debug.WriteLine($"Socket was closed! due to {e.Message} and {e.InnerException?.Message}"); FS?.Dispose(); throw; } }
核心问题2:EXCEPTION B触发原因及修复
- 目标文件夹不存在:
Path.Combine后的路径所在文件夹可能未创建,导致无法生成文件。 - 文件名包含非法字符:文件名如果包含系统不允许的字符(如
/、\、:等),会导致路径无效。
修复已包含在上述客户端修正代码中:
- 用
Directory.CreateDirectory(Path.GetDirectoryName(path)!)确保目标文件夹存在 - 检查文件名是否包含非法字符,提前抛出异常
内容的提问来源于stack exchange,提问作者Guezt
相关产品推荐
相关产品推荐

