You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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触发原因及修复

  1. 服务端SendData未await异步操作:原代码中WriteAsync和FlushAsync没有用await,导致数据发送乱序、不完整,客户端读到错误的数据包长度。
  2. 客户端未循环读取完整数据包:ReadAsync不一定能一次读完指定长度的字节,需要循环读取直到拿到完整数据。
  3. 服务端写入无效数据:原代码写入整个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触发原因及修复

  1. 目标文件夹不存在:Path.Combine后的路径所在文件夹可能未创建,导致无法生成文件。
  2. 文件名包含非法字符:文件名如果包含系统不允许的字符(如/、\、:等),会导致路径无效。

修复已包含在上述客户端修正代码中:

  • 用Directory.CreateDirectory(Path.GetDirectoryName(path)!)确保目标文件夹存在
  • 检查文件名是否包含非法字符,提前抛出异常

内容的提问来源于stack exchange,提问作者Guezt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 03:44:55