TCP客户端断开触发System.StackOverflowException异常求助
TCP客户端断开引发StackOverflowException的修复及重连实现
异常原因分析
当TCP客户端断开连接时,stream.EndRead()会返回0(表示无更多数据可读),但当前代码未处理该情况,而是直接再次调用BeginRead()。同时_reading始终为true,导致readCompleteCallback与BeginRead无限循环调用,最终触发栈溢出异常。此外,连接断开时EndRead()还可能抛出IOException,未被捕获也会导致异常扩散。
修复方案
- 处理连接断开信号:判断
bytesRead == 0时,停止读取流程,清理资源。 - 捕获IO异常:包裹
EndRead()调用,捕获连接断开时的异常。 - 实现重连逻辑:断开后启动重试连接机制,带间隔延迟避免频繁重试。
修改后的完整代码
private bool _reading; private Stream _stream; private TcpClient _client; private IAsyncResult _readResult; private bool _disposed; private CancellationTokenSource _reconnectCts; // 用于取消重连 private byte[] _parseBuffer = new byte[0]; private byte[] _buffer = new byte[1024]; private int _maxBytesRead; private void readCompleteCallback(IAsyncResult readAsyncResult) { if (_reading) { Stream stream = (Stream)readAsyncResult.AsyncState; try { int bytesRead = stream.EndRead(readAsyncResult); // 客户端断开连接:bytesRead为0 if (bytesRead == 0) { StopReading(); StartReconnect(); return; } if (bytesRead > _maxBytesRead || bytesRead == _buffer.Length) { _maxBytesRead = bytesRead; } byte[] newParseBuffer = new byte[_parseBuffer.Length + bytesRead]; _parseBuffer.CopyTo(newParseBuffer, 0); Array.Copy(_buffer, 0, newParseBuffer, _parseBuffer.Length, bytesRead); _parseBuffer = newParseBuffer; // 解析消息 int bytesConsumed = MessageFactory.GetNextMessages( _parseBuffer, 0, _parseBuffer.Length, out var messages); // 处理未消费的缓冲区数据 if (bytesConsumed < _parseBuffer.Length) { byte[] remainingBuffer = new byte[_parseBuffer.Length - bytesConsumed]; Array.Copy(_parseBuffer, bytesConsumed, remainingBuffer, 0, remainingBuffer.Length); _parseBuffer = remainingBuffer; } else { _parseBuffer = new byte[0]; } // 继续读取下一批数据 _readResult = stream.BeginRead( _buffer, 0, _buffer.Length, new AsyncCallback(readCompleteCallback), stream); } catch (IOException ex) { // 连接异常断开,比如客户端强制关闭 StopReading(); StartReconnect(); } } } /// <summary> /// 停止读取并清理资源 /// </summary> private void StopReading() { _reading = false; _stream?.Close(); _client?.Close(); _disposed = true; } /// <summary> /// 启动重连逻辑 /// </summary> private async void StartReconnect() { _reconnectCts?.Cancel(); _reconnectCts = new CancellationTokenSource(); var token = _reconnectCts.Token; while (!token.IsCancellationRequested) { try { // 替换为你的服务器地址和端口 _client = new TcpClient(); await _client.ConnectAsync("127.0.0.1", 8888); // 重连成功,恢复读取 _stream = _client.GetStream(); _disposed = false; _reading = true; _parseBuffer = new byte[0]; // 重置解析缓冲区 _readResult = _stream.BeginRead( _buffer, 0, _buffer.Length, new AsyncCallback(readCompleteCallback), _stream); break; } catch (SocketException) { // 连接失败,延迟重试 await Task.Delay(3000, token); } } } /// <summary> /// 手动停止重连(比如程序退出时) /// </summary> public void StopReconnect() { _reconnectCts?.Cancel(); _reconnectCts?.Dispose(); }
关键修改点说明
- 断开检测:通过
bytesRead == 0和捕获IOException判断连接断开,立即停止读取并触发重连。 - 资源清理:
StopReading方法统一关闭流和客户端,标记状态。 - 重连机制:使用异步重试,每次失败后延迟3秒,支持通过
CancellationToken取消重连。 - 缓冲区优化:用
Array.Copy替代手动循环,提升性能。
内容的提问来源于stack exchange,提问作者John Pendarvis
相关产品推荐
相关产品推荐

