강의

멘토링

커뮤니티

인프런 커뮤니티 질문&답변

아휘님의 프로필 이미지
아휘

작성한 질문수

[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버

유니티 연동 #4

UnhandledException 으로 서버 터지는 경우

작성

·

27

0

안녕하세요,

해당 강의에서 만든 코드 기반으로 제 게임에 맞춘 서버를 AWS EC2에서 몇 주째 실행중입니다. 개발 컴퓨터에서 테스트할때도 그렇고 EC2에서 할때도 그렇고 강제 종료될때가 있어서 꺼지면 로그 남기게 해서 몇개 원인을 찾았는데요, UnhandledException이 뜨면서 강제종료 되는 경우가 두가지 있는 것 같습니다.

제 유추 상으로는 포트포워딩으로 게임 포트 이외에는 인바운드를 거절했는데도 쓰레기IP들이 서버를 터트리고 가는 것 같습니다.

 

첫번째.

[11-24 18:31:38] FATAL [UnhandledException] 
System.Net.Sockets.SocketException (10054): An existing connection was forcibly closed by the remote host.
   at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, Boolean disconnectOnFailure, String callerName)
   at System.Net.Sockets.Socket.Shutdown(SocketShutdown how)
   at ServerCore.Session.Disconnect() in \Server\ServerCore\Session.cs:line 103
   at ServerCore.Session.OnRecvCompleted(Object sender, SocketAsyncEventArgs args) in \Server\ServerCore\Session.cs:line 193
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Net.Sockets.SocketAsyncEventArgs.<>c.<.cctor>b__173_0(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
   at System.Threading.ThreadPoolTypedWorkItemQueue`2.System.Threading.IThreadPoolWorkItem.Execute()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()

이 오류는 Session.cs에 소켓 Shutdown에서 발생했습니다.

public void Disconnect() {
    if (Interlocked.Exchange(ref _disconnected, 1) == 1)
        return;

    OnDisconnected(_socket.RemoteEndPoint);
    _socket.Shutdown(SocketShutdown.Both);
    _socket.Close();
    
    
    Clear();
}

서버가 터지면 안되니 임시 방편으로 try catch로 감싸놨습니다.

try {
    _socket.Shutdown(SocketShutdown.Both);
} catch (Exception e) {
    Console.WriteLine($"[NEW ADDED] SOCKET SHUTDOWN Failed {e}");
}
try {
    _socket.Close();
} catch (Exception e) {
    Console.WriteLine($"[NEW ADDED] SOCKET CLOSE Failed {e}");
}

 

두번째.

[11-25 10:16:12] FATAL [UnhandledException] 
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Sockets.Socket'.
   at System.Net.Sockets.Socket.get_RemoteEndPoint()
   at ServerCore.Listener.OnAcceptCompleted(Object sender, SocketAsyncEventArgs args) in Server\ServerCore\Listener.cs:line 45
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Net.Sockets.SocketAsyncEventArgs.<>c.<.cctor>b__173_0(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
   at System.Threading.ThreadPoolTypedWorkItemQueue`2.System.Threading.IThreadPoolWorkItem.Execute()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
--------------------------------------------------

이 오류는 Listener.cs의 RemoteEndPoint를 가져오는 부분에서 발생했는데요,

void OnAcceptCompleted(object sender, SocketAsyncEventArgs args) {
    if (args.SocketError == SocketError.Success) {
        Session session = _sessionFactory.Invoke();
        session.Start(args.AcceptSocket);
        session.OnConnected(args.AcceptSocket.RemoteEndPoint);
    } else
        Console.WriteLine(args.SocketError.ToString());

    RegisterAccept(args);
}

비쥬얼 스튜디오에서도 args.AcceptSocket.RemoteEndPoint 는 null일수도 있다고 뜨기는 하는데

이부분도 != null을 붙여서 해야하는지 try catch로 감싸야하는지 궁금합니다.

근본적인 원인은 예외가 왜 발생하는지 알아야하는데 전부 API 단이라 try catch 혹은 != null 빼고는 대책을 강구하기가 어렵습니다.

 

요약하자면

  1. 이런 UnhandledException이 나올때 해당 코드를 try { } catch로 해주것이 괜찮은 방법인가요?

  2. 혹시 근본적인 해결책이 있다면 궁금합니다.

답변 2

0

Rookiss님의 프로필 이미지
Rookiss
지식공유자

와우 잘 찾으셨습니다.
강의에선 누락 되었으나 C# 네트워크 관련 함수 호출할 땐
싹 다 try-catch를 해주시는 것이 맞습니다.

M2 프로젝트에서는 저런 부분 보완 처리 코드도 추가 되었던 것으로 기억합니다.

아휘님의 프로필 이미지
아휘
질문자

감사합니다 ^^ 사랑합니다!!!!★

0

안녕하세요, 인프런 AI 인턴입니다. 아휘님께서 겪고 있는 문제는 네트워크 프로그래밍에서 종종 발생할 수 있는 연결 문제와 관련된 사항입니다. 두 가지 사례에 대해 하나씩 살펴보겠습니다.

첫 번째 오류: SocketException - “An existing connection was forcibly closed by the remote host.”

이 오류는 일반적으로 네트워크 연결이 예기치 않게 끊어진 경우에 발생합니다. 예를 들어, 클라이언트가 서버와의 연결을 강제로 닫았을 때 발생할 수 있습니다. 다음과 같은 조치를 고려할 수 있습니다:

  1. 예외 처리: 이미 시도하신 대로 try-catch로 소켓의 ShutdownClose를 감싸는 것은 올바른 접근 방식입니다. 네트워크 환경에서는 예상치 못한 오류가 발생할 수 있기 때문에 이러한 예외 처리가 필요합니다.

  2. 로그 기록: 오류 발생 시 로그를 통해 문제가 발생한 상황을 기록하여 원인을 분석하는 데 도움이 될 수 있습니다.

두 번째 오류: ObjectDisposedException - “Cannot access a disposed object.”

이 오류는 이미 해제된 소켓 객체에 접근하려 할 때 발생합니다. 이는 네트워크 비동기 작업이 완료되기 전에 소켓이 제대로 해제되지 않았을 때 생길 수 있습니다.

  1. null 체크 추가: AcceptSocketnull인지 확인한 후에 RemoteEndPoint를 접근하도록 코드를 수정할 수 있습니다.

    if (args.AcceptSocket != null && args.SocketError == SocketError.Success) {
       Session session = _sessionFactory.Invoke();
       session.Start(args.AcceptSocket);
       if (args.AcceptSocket.RemoteEndPoint != null) {
           session.OnConnected(args.AcceptSocket.RemoteEndPoint);
       }
    }
    
  2. 코드 흐름 점검: 연결 수립과 소켓 상태 관리를 철저히 점검하여 비동기 작업 중 소켓 객체가 폐기되지 않는지 확인하는 것이 중요합니다.

전반적인 조언

  • 예외 처리 관행: 각 네트워크 작업에 대해 예외 처리를 도입하는 것은 네트워크 프로그래밍에서 필수적입니다. 예외 처리를 통해 프로그램이 비정상 종료되지 않도록 관리할 수 있습니다.

  • 근본적인 해결책: 근본적인 해결책을 찾기 위해서는 네트워크 흐름의 모든 단계에서 문제를 파악하고 처리할 수 있는 예외를 정의하는 것이 중요합니다. 또한, 네트워크 보안을 강화하고 외부 비정상 트래픽을 차단하는 장치를 사용해 보실 것을 권장합니다.

관련된 유사한 질문과 답변은 다음 링크에서 확인하실 수 있습니다:
- “프로그램 에러 관련 질문…”

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

아휘님의 프로필 이미지
아휘

작성한 질문수

질문하기