[.NET 5] 달라진 'SocketAsyncEventArgs'의 동작 때문에 'Completed' 이벤트가 오지 않는 현상 해결하기
.NET 4.x에서 'SocketAsyncEventArgs'를 쓰다가 그대로 .NET 5로 업그레이드하면 'Completed'이벤트가 오지 않는 현상이 있습니다.
(참고 : github - DGSocketAssist/DGSocketAssist1/)
아래 스크린 샷은 .NET 4에서 만든 'SocketAsyncEventArgs'를 .NET 5에서 사용한 경우입니다.
'SaeaSend_Completed'이벤트가 오지 않은 걸 확인할 수 있습니다.
아래 스크린 샷은 .NET 4에서 정상 작동한 경우입니다.
이것은 .NET 5부터 'ReceiveAsync', 'SendAsync'가 동기로 동작하기 때문입니다.
이 포스팅은 'ReceiveAsync'을 기준으로 설명합니다.
('SendAsync'도 똑같기 때문)
1. .NET 5에서는 'SocketAsyncEventArgs'가 다르게 동작한다.
.NET 5에서는 조건에 따라 'SocketAsyncEventArgs'가 비동기로 동작합니다.
그러니 기존 코드는 동기로 동작하는 대신 'Completed'가 발생하지 않으므로 'ReceiveAsync'를 호출하고 바로
'Completed'이벤트를 직접 호출하면 동작은 하게 됩니다.
bool bReceive_Completed = this.SocketMe.ReceiveAsync(this.m_saeaReceive);
this.SaeaReceive_Completed(this.SocketMe, this.m_saeaReceive);
2. 비동기 호출
동기로 동작함으로서 생기는 문제를 해결하려면 별도의 스레드를 생성하여 그 안에서 'ReceiveAsync'를 호출하면 됩니다.
Task.Run(() => {
this.SocketMe.ReceiveAsync(this.m_saeaReceive);
this.SaeaReceive_Completed(this.SocketMe, this.m_saeaReceive);
});
이렇게 하면 .NET 4.x이전 때와 비슷하게 동작합니다.
* 2024-05-20 추가 *
비동기로 호출했더니 아주 가끔 신호가 무시되는 경우가 있습니다.
그래서 지금은 그냥 동기로 사용하고 있습니다.(Task.Run를 제거)
너무 가끔이라 이 원인이 아닐거로 생각하고 있었는데 제거하고 나니 일단 해당 증상이 없는 것으로 보입니다.
정확하게 이게 원인이였는지 다른 것과 섞여서 아다리가 맞은 건지는 알 수 없습니다.
전체 로그를 모니터링하는건 너무 시간이 걸리는 문제라 말이죠....
3. .NET 4와 호환시키기
위의 코드로 수정하면 .NET 4.x이하 버전에서는 동작 하지 않습니다.
다행히 'ReceiveAsync'의 리턴은 동기로 동작했는지를 알려줍니다.
(참고 : MS Learn - SocketAsyncEventArgs 클래스 )
비동기로 동작하면 참(true)
동기로 동작하면 거짓(false)
가 리턴되므로 이것을 이용해 'Completed' 이벤트를 수동으로 호출할지 결정하면 됩니다.
Task.Run(() => {
bool bReceive_Completed = this.SocketMe.ReceiveAsync(this.m_saeaReceive);
if (false == bReceive_Completed)
{
this.SaeaReceive_Completed(this.SocketMe, this.m_saeaReceive);
}
});
이렇게 하면 자연스럽게 .NET 4.x이전 버전에서는 비동기 동작 후 'Completed' 호출이 되고
. NET 5 이상 버전에서는 새로운 스레드에서 동기 작업 후 'Completed' 호출이 됩니다.
마무리
테스트 프로젝트 : github - DGSocketAssist/DGSocketAssist1/
'DGSocketAssist1_Client'를 참조하면 기존 코드
'DGSocketAssist1_Client_Upgrade'를 참조하면 .NET 5에서도 동작하는 코드입니다.
참고 : 성태의 닷넷 이야기 - .NET Framework: 2056. C# - await 비동기 호출을 기대한 메서드가 동기로 호출되었을 때의 부작용
닷넷끼리 사용할 때는 재연이 안 될 수도 있습니다.
여러 버전이 섞여 있는 서버/클라이언트에서 재연이 안 됐습니다.(되는 것도 있고 아닌 것도 있음...)
c++로 만들어진 옛날 서버에 붙이면 재연이 되는 것으로 보아 닷넷 자체에서 뭔가 한 건 없을 겁니다.
흔히 '아다리'가 맞은 상황이겠죠 ㅎㅎㅎㅎ