{"record":{"id":"f123a53017e0a9d3","repo":"stride3d/stride","slug":"socket-closed","errorCode":null,"errorMessage":"Socket closed","messagePattern":"Socket closed","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"sources/engine/Stride.Engine/Engine/Network/SocketExtensions.cs","lineNumber":18,"sourceCode":"// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)\n// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.\n\nusing System;\nusing System.IO;\nusing System.Threading.Tasks;\n\nnamespace Stride.Engine.Network\n{\n    public static class SocketExtensions\n    {\n        public static async Task ReadAllAsync(this Stream socket, byte[] buffer, int offset, int size)\n        {\n            while (size > 0)\n            {\n                int read = await socket.ReadAsync(buffer, offset, size).ConfigureAwait(false);\n                if (read == 0)\n                    throw new IOException(\"Socket closed\");\n                size -= read;\n                offset += read;\n            }\n        }\n\n        public static async Task WriteInt32Async(this Stream socket, int value)\n        {\n            var buffer = BitConverter.GetBytes(value);\n            await socket.WriteAsync(buffer, 0, sizeof(int)).ConfigureAwait(false);\n        }\n\n        public static async Task<int> ReadInt32Async(this Stream socket)\n        {\n            var buffer = new byte[sizeof(int)];\n            await socket.ReadAllAsync(buffer, 0, sizeof(int)).ConfigureAwait(false);\n            return BitConverter.ToInt32(buffer, 0);\n        }\n","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/engine/Stride.Engine/Engine/Network/SocketExtensions.cs#L1-L36","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Wrap socket reads in try-catch for IOException and treat it as a disconnection (reconnect).","Check the remote endpoint's health/logs to see why the connection closed.","Keep the connection alive (heartbeats) or set timeouts consistent between peers.","Verify message framing matches on both sides so reads don't over-read and hit the close."],"exampleFix":"// before\nvar value = await stream.ReadStringAsync(); // throws when peer closed\n// after\nstring value;\ntry { value = await stream.ReadStringAsync(); }\ncatch (IOException) { Reconnect(); return; }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { var s = await stream.ReadStringAsync(); }\ncatch (IOException) { HandleDisconnect(); }","preventionTips":["Treat IOException on reads as a disconnect signal and reconnect.","Add heartbeats/idle timeouts consistent between both peers.","Match message framing on both ends to avoid over-reading a closing stream."],"tags":["network","socket","io","disconnection"],"backgroundTag":"connection-closed","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}