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

C# Socket接收数据触发Encoding.GetString索引越界异常问题

问题背景
  • Socket接收数据解析时触发运行时异常
  • 异常信息:System.ArgumentOutOfRangeException: 'Index and count must refer to a location in the buffer.
  • 待明确两点:
    • 该异常的触发原因
    • Encoding.ASCII.GetString(byte[] bytes, int index, int count)中index参数的实际作用
  • 异常触发位置为数据包解析逻辑,对应问题代码如下:
public string Username { get; set; }
public string HashPassword { get; set; }
public string HWID { get; set; }

public Command(string username, string password, string hwid)
{
    Username = username;
    HashPassword = password;
    HWID = hwid;
}

public Command(byte[] data)
{
    int usernameLenght = BitConverter.ToInt32(data, 0);
    Username = Encoding.ASCII.GetString(data, 1, usernameLenght);

    int passwordLenght = BitConverter.ToInt32(data, 2);
    HashPassword = Encoding.ASCII.GetString(data, 3, passwordLenght); // 异常触发行

    int hwid = BitConverter.ToInt32(data, 4);
    HWID = Encoding.ASCII.GetString(data, 5, hwid);
}

public byte[] ToByteArray()
{
    List<byte> byteList = new List<byte>();
    byteList.AddRange(BitConverter.GetBytes(Username.Length));
    byteList.AddRange(Encoding.ASCII.GetBytes(Username));
    byteList.AddRange(BitConverter.GetBytes(HashPassword.Length));
    byteList.AddRange(Encoding.ASCII.GetBytes(HashPassword));
    byteList.AddRange(BitConverter.GetBytes(HWID.Length));
    byteList.AddRange(Encoding.ASCII.GetBytes(HWID));
    return byteList.ToArray();
}
具体解答

index参数作用

Encoding.ASCII.GetString的三个参数定义:

  • bytes:待解码的原始字节数组
  • index:字节数组中启动解码操作的起始偏移位置,从0开始计数
  • count:从起始偏移位置开始,连续读取的字节总数
    方法执行时会强制校验index、count均非负,且index + count不超过字节数组总长度,不满足条件就会抛出你遇到的参数越界异常。

异常触发的根本原因

你的反序列化偏移计算逻辑完全错误,和自身实现的ToByteArray序列化规则不匹配:

  1. 序列化时调用BitConverter.GetBytes(字符串长度)写入的长度值是int类型,固定占用4个字节,不是1个字节
  2. 反序列化时你错误假设长度字段仅占1字节,每次读取长度后仅将偏移加1,导致后续所有长度字段的读取位置完全错位,读出来的长度值是字节数组中随机的脏数据,大概率是一个远大于缓冲区剩余长度的值,最终触发越界。

举个实际的偏移例子:序列化时用户名长度写入在字节数组0-3的位置,从索引4开始才是用户名的ASCII内容。但你反序列化时从0位置读完用户名长度,直接从索引1开始读用户名内容,把长度字段剩余的3个字节当成了用户名内容的一部分,后续读密码长度时从索引2取值,拿到的根本不是你写入的密码长度,而是用户名内容中的字节值,自然会出现计算的读取范围超出缓冲区边界的问题。

修复方案

用动态偏移量逐段读取,每读完一段就把偏移往后移动对应长度,修复后的反序列化代码如下:

public Command(byte[] data)
{
    int offset = 0;
    // 读取用户名
    int usernameLength = BitConverter.ToInt32(data, offset);
    offset += 4; // int类型长度占4字节,读完偏移后移4位
    Username = Encoding.ASCII.GetString(data, offset, usernameLength);
    offset += usernameLength; // 读完内容,偏移后移内容对应长度

    // 读取密码
    int passwordLength = BitConverter.ToInt32(data, offset);
    offset += 4;
    HashPassword = Encoding.ASCII.GetString(data, offset, passwordLength);
    offset += passwordLength;

    // 读取HWID
    int hwidLength = BitConverter.ToInt32(data, offset);
    offset += 4;
    HWID = Encoding.ASCII.GetString(data, offset, hwidLength);
}

额外注意:TCP是面向流的协议,不存在天然的数据包边界,不能假设单次Socket接收操作就能拿到完整的序列化字节数组,接收端必须实现粘包/半包处理逻辑,攒够完整数据包长度后再传入构造函数解析,否则仍可能出现解析异常。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 09:06:21