iOS에서 NativeWebSocket Close 호출 시 예외가 발생하는 문제
NativeWebSocket의 Close 처리는 Android와 iOS에서 다르게 동작한다.
모바일 카지노 게임의 소켓은 NativeWebSocket 라이브러리를 사용하고 있고, 이 문제는 NativeWebSocket issue #67에 리포트되어 있다.
이 이슈에서는 iOS에서 불필요한 지연으로 Close 이벤트가 늦게 도착하는 것을 원인으로 설명한다.
근본적인 해결 방법은 패키지를 바꾸거나 업데이트하는 것이다. 하지만 현재 서비스 흐름에서는 게임에서 로비로 돌아갈 때 소켓을 다시 생성하고 처리하므로, try-catch 블록으로 예외를 잡아 회피했다.
// Beforeawait m_Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, m_CancellationToken);
// Aftertry{ await m_Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, m_CancellationToken);}catch(Exception ex){ Debug.LogWarning($"WebSocket Exception {ex.Message}");}아래는 로그 전문이다.
<color=orange>[WS] Closed Web Socket code[Abnormal] dns[wss://chat-server.example.com/]</color>Game.Common.WebSocketService:OnCloseSocket(WebSocketHandle, WebSocketCloseCode)Game.Common.<>c__DisplayClass11_0:<_CreateSocket>b__1(WebSocketCloseCode)NativeWebSocket.<Receive>d__37:MoveNext()System.Threading.ExecutionContext:RunInternal(ExecutionContext, ContextCallback, Object, Boolean)System.Runtime.CompilerServices.MoveNextRunner:Run()<CoroutineWrapper>d__4:MoveNext()UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
<color=orange>CHAT: OnWSClose</color>Game.CHAT.ChatSocket:OnWSClose()Game.CHAT.<>c__DisplayClass3_0:<ConnectWebSocket>b__2()Game.Common.WebSocketService:OnCloseSocket(WebSocketHandle, WebSocketCloseCode)Game.Common.<>c__DisplayClass11_0:<_CreateSocket>b__1(WebSocketCloseCode)NativeWebSocket.<Receive>d__37:MoveNext()System.Threading.ExecutionContext:RunInternal(ExecutionContext, ContextCallback, Object, Boolean)System.Runtime.CompilerServices.MoveNextRunner:Run()<CoroutineWrapper>d__4:MoveNext()UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
SocketException: Connection timed outRethrow as IOException: Unable to read data from the transport connection: Connection timed out.at System.Net.Sockets.Socket+AwaitableSocketAsyncEventArgs.ThrowException (System.Net.Sockets.SocketError error) [0x00000] in <00000000000000000000000000000000>:0at System.Net.Sockets.Socket+AwaitableSocketAsyncEventArgs.GetResult (System.Int16 token) [0x00000] in <00000000000000000000000000000000>:0at System.Threading.Tasks.ValueTask`1+ValueTaskSourceAsTask+<>c[TResult].<.cctor>b__4_0 (System.Object state) [0x00000] in <00000000000000000000000000000000>:0at System.Net.Sockets.Socket+<>c.<.cctor>b__367_10 (System.IAsyncResult ares) [0x00000] in <00000000000000000000000000000000>:0at System.Threading.ThreadPoolWorkQueue.Dispatch () [0x00000] in <00000000000000000000000000000000>:0--- End of stack trace from previous location where exception was thrown ---
at Mono.Net.Security.MobileAuthenticatedStream.InnerRead (System.Boolean sync, System.Int32 requestedSize, System.Threading.CancellationToken cancellationToken) [0x00000] in <00000000000000000000000000000000>:0at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) [0x00000] in <00000000000000000000000000000000>:0at System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner.Run () [0x00000] in <00000000000000000000000000000000>:0at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction (System.Action action, System.Boolean allowInlining, System.Threading.Tasks.Task& currentTask) [0x00000] in <00000000000000000000000000000000>:0at System.Threading.Tasks.Task.FinishContinuations () [0x00000] in <00000000000000000000000000000000>:0at System.Threading.Tasks.Task.TrySetException (System.Object exceptionObject) [0x00000] in <00000000000000000000000000000000>:0at System.Net.Sockets.Socket+<>c.<.cctor>b__367_10 (System.IAsyncResult ares) [0x00000] in <00000000000000000000000000000000>:0at System.Threading.ThreadPoolWorkQueue.Dispatch () [0x00000] in <00000000000000000000000000000000>:0--- End of stack trace from previous location where exception was thrown ---