stride3d/stride · error · IOException

Socket closed

Error message

Socket closed

What it means

SocketExtensions.ReadAllAsync loops until the requested number of bytes is read; a ReadAsync returning 0 means the remote end closed the stream, so it throws IOException("Socket closed"). It guards callers from treating a clean close as end-of-protocol.

Solutions

  1. Wrap socket reads in try-catch for IOException and treat it as a disconnection (reconnect).
  2. Check the remote endpoint's health/logs to see why the connection closed.
  3. Keep the connection alive (heartbeats) or set timeouts consistent between peers.
  4. Verify message framing matches on both sides so reads don't over-read and hit the close.

Example fix

// before
var value = await stream.ReadStringAsync(); // throws when peer closed
// after
string value;
try { value = await stream.ReadStringAsync(); }
catch (IOException) { Reconnect(); return; }
Defensive patterns

Strategy: try-catch

Try / catch

try { var s = await stream.ReadStringAsync(); }
catch (IOException) { HandleDisconnect(); }

Prevention

When it happens

Trigger: Reading from a TCP stream whose remote peer closed the connection before delivering all requested bytes — ReadAsync returns 0 inside ReadAllAsync (used by ReadStringAsync and other helpers).

Common situations: Server crash or restart mid-message; remote host closed socket due to timeout or firewall idle cutoff; client reading a response after the server finished and disposed the socket.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/f123a53017e0a9d3. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Engine/Engine/Network/SocketExtensions.cs:18

// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

using System;
using System.IO;
using System.Threading.Tasks;

namespace Stride.Engine.Network
{
    public static class SocketExtensions
    {
        public static async Task ReadAllAsync(this Stream socket, byte[] buffer, int offset, int size)
        {
            while (size > 0)
            {
                int read = await socket.ReadAsync(buffer, offset, size).ConfigureAwait(false);
                if (read == 0)
                    throw new IOException("Socket closed");
                size -= read;
                offset += read;
            }
        }

        public static async Task WriteInt32Async(this Stream socket, int value)
        {
            var buffer = BitConverter.GetBytes(value);
            await socket.WriteAsync(buffer, 0, sizeof(int)).ConfigureAwait(false);
        }

        public static async Task<int> ReadInt32Async(this Stream socket)
        {
            var buffer = new byte[sizeof(int)];
            await socket.ReadAllAsync(buffer, 0, sizeof(int)).ConfigureAwait(false);
            return BitConverter.ToInt32(buffer, 0);
        }

View on GitHub (pinned to 96fad776d2)