C#调用NetworkStream.ReadAsync报参数超出有效值范围错误求助
问题背景
非开发人员临时接手项目,遇到TCP通信报错,报错截图如下:
经弹窗逐段调试,定位到报错触发代码段如下:
try { String type = "I"; String fullMessage = Convert.ToChar(1) + type + Convert.ToChar(2) + Convert.ToChar(3) + Convert.ToChar(13); NetworkStream serverStream = client.GetStream(); byte[] outStream = Encoding.ASCII.GetBytes(fullMessage); serverStream.Write(outStream, 0, outStream.Length); serverStream.Flush(); ; byte[] inStream = new byte[10025]; await serverStream.ReadAsync(inStream, 0, (int)client.ReceiveBufferSize); string returndata = System.Text.Encoding.ASCII.GetString(inStream); if (returndata.Length == 0) { MessageBox.Show("isError = true in try"); isError = true; connected = false; } } catch (Exception e) { MessageBox.Show(e.ToString()); isError = true; connected = false; }
错误原因
从截图报错信息(对应Socket错误码10054:远程主机强迫关闭现有连接),结合代码逻辑,触发原因按概率从高到低排列:
- 发送的报文不符合服务端协议规范,服务端收到非法报文后直接主动断开连接。你构造的报文使用了SOH(0x01)、STX(0x02)、ETX(0x03)这类工业控制类协议常用的控制字符,这类协议对报文格式要求极严,目前你用单个回车符(0x0D)作为结尾,而绝大多数同类协议要求结尾必须是CRLF(0x0D 0x0A,即回车加换行),部分协议还要求附加校验字段,格式不对直接断连是这类服务端的通用处理逻辑。
- 读取逻辑存在严重错误:
ReadAsync方法的返回值是本次实际读到的字节数,返回0即代表服务端已经关闭连接。你现在完全没有接收这个返回值,直接把长度10025的完整缓冲区转成字符串,哪怕只读到1个字节,后面的10024个字节都是默认的\0空字符,既无法正确判断返回内容长度,后续解析也会出问题。另外你传入的读取长度是client.ReceiveBufferSize(默认值8192),虽然和初始化的10025长度缓冲区不会越界,但属于逻辑不严谨的写法。 - 存在无效代码和缺失前置校验:
NetworkStream是TCP流,不存在应用层缓冲区,Flush()方法对其完全无效,不会起到“立即发送数据”的作用;代码没有在读写前校验TcpClient的连接状态,半连接、已断开的情况下直接读写必然抛异常。
修复方案
- 优先核对协议文档:找到项目里和服务端对接的协议说明,确认你构造的身份标识报文的控制字符顺序、结尾符、校验规则是否完全符合要求,这是解决10054错误的核心。
- 修正网络流读写逻辑,修复后的完整代码如下:
try { // 前置校验连接状态 if (!client.Connected) { isError = true; connected = false; return; } string type = "I"; // 注意:这里的结尾符请根据实际协议调整,示例为常见的CRLF结尾 string fullMessage = Convert.ToChar(1) + type + Convert.ToChar(2) + Convert.ToChar(3) + "\r\n"; NetworkStream serverStream = client.GetStream(); byte[] outStream = Encoding.ASCII.GetBytes(fullMessage); serverStream.Write(outStream, 0, outStream.Length); byte[] inStream = new byte[client.ReceiveBufferSize]; // 接收实际读取的字节数 int actualReadLen = await serverStream.ReadAsync(inStream, 0, inStream.Length); // 读取长度为0即服务端正常关闭连接 if (actualReadLen == 0) { MessageBox.Show("服务端已主动关闭连接"); isError = true; connected = false; return; } // 只转换实际读到的字节段,避免空字符干扰 string returndata = Encoding.ASCII.GetString(inStream, 0, actualReadLen); if (string.IsNullOrEmpty(returndata)) { isError = true; connected = false; } } catch (IOException ioEx) when (ioEx.InnerException is SocketException { ErrorCode: 10054 }) { MessageBox.Show("连接被服务端强制断开,请优先检查发送的报文格式是否匹配协议要求"); isError = true; connected = false; } catch (Exception e) { MessageBox.Show(e.ToString()); isError = true; connected = false; }
- 额外注意点:TCP是面向流的协议,
ReadAsync不会保证一次读完服务端返回的所有数据,如果后续要处理长度更长的返回报文,需要循环读取直到读到协议约定的结束符或者达到约定报文长度,不能假设一次Read就能拿到完整数据。
内容的提问来源于stack exchange,提问作者KJ Work
相关产品推荐
相关产品推荐

