성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - Socket.Close 시 Socket.Receive 메서드에서 예외가 발생하는 문제</h1> <p> 아래와 같은 질문이 있는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > socket 종료 시 reveive수신부에서 에러 나는거 처리 문의 ; <a target='tab' href='https://www.sysnet.pe.kr/3/0/5693'>https://www.sysnet.pe.kr/3/0/5693</a> </pre> <br /> 정리해 보면 다음의 코드로 재현할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System.Net.Sockets; Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Connect("127.0.0.1", 80); ThreadPool.QueueUserWorkItem(socketClose, socket); byte[] buffer = new byte[4096]; <span style='color: blue; font-weight: bold'>socket.Receive(buffer);</span> // 소켓 Receive로 스레드 대기 socket.Close(); void socketClose(object? state) { // 1초 후 Socket.Close 호출 Thread.Sleep(1000); <span style='color: blue; font-weight: bold'>(state as Socket)?.Close();</span> } </pre> <br /> 위의 코드를 실행하면 Receive 호출에서 System.Net.Sockets.SocketException 예외가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Unhandled exception. System.Net.Sockets.SocketException (10053): An established connection was aborted by the software in your host machine. at System.Net.Sockets.Socket.Receive(Byte[] buffer) at Program.<Main>$(String[] args) in C:\...\ConsoleApp1\ConsoleApp1\Program.cs:line 11 </pre> <br /> C/C++로 소켓을 다뤄보신 분들은 아시겠지만, 사실 socket API 자체에서는 예외라는 것이 없습니다. 즉, .NET의 Socket은 <a target='tab' href='https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recv'>socket API의 recv 함수</a> 호출 결과에 대해 일부러 예외를 발생시키는 것입니다.<br /> <br /> 실제로 소스 코드를 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > referencesource/System/net/System/Net/Sockets/Socket.cs ; <a target='tab' href='https://github.com/microsoft/referencesource/blob/master/System/net/System/Net/Sockets/Socket.cs'>https://github.com/microsoft/referencesource/blob/master/System/net/System/Net/Sockets/Socket.cs</a> </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public int Receive(byte[] buffer) { return Receive(buffer, 0, buffer != null ? buffer.Length : 0, SocketFlags.None); } // Receives data from a connected socket into a specific location of the receive buffer. public int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags) { SocketError errorCode; int bytesTransferred = Receive(buffer, offset, size, socketFlags, out errorCode); if (errorCode != SocketError.Success) { <span style='color: blue; font-weight: bold'>throw new SocketException((int)errorCode);</span> } return bytesTransferred; } </pre> <br /> overload된 Receive 메서드들 중에 예외를 발생시키는 경우가 있는 것에 불과합니다. 따라서, <a target='tab' href='https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.receive#system-net-sockets-socket-receive(system-byte()-system-int32-system-int32-system-net-sockets-socketflags-system-net-sockets-socketerror@)'>예외를 발생시키지 않는 버전의 Receive 메서드</a>를 사용하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > byte[] buffer = new byte[4096]; socket.Receive(buffer, 0, buffer.Length, SocketFlags.None, <span style='color: blue; font-weight: bold'>out SocketError errorCode</span>); if (errorCode != SocketError.Success) { Console.WriteLine("Receive error"); } </pre> <br /> 이제는 Socket.Close가 발생해도 예외 없이 "out SocketError errorCode"의 인자로 결과를 알 수 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <a name='udp'></a> <br /> 그리고 해당 질문의 덧글을 보면 UDP 소켓에 대한 ReceiveFrom 메서드의 오류 질문이 나오는데요, <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System.Net; using System.Net.Sockets; Socket socket = new Socket(AddressFamily.InterNetwork, <span style='color: blue; font-weight: bold'>SocketType.Dgram, ProtocolType.Udp</span>); socket.Connect(IPAddress.Loopback, 15000); // TCP와는 달리 연결하지는 않고, Bind 역할만 담당 ThreadPool.QueueUserWorkItem(socketClose, socket); byte[] buffer = new byte[4096]; EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0); <span style='color: blue; font-weight: bold'>socket.ReceiveFrom(buffer, ref remoteEP);</span> // 소켓 ReceiveFrom으로 스레드 대기 void socketClose(object? state) { Thread.Sleep(1000); <span style='color: blue; font-weight: bold'>(state as Socket)?.Close();</span> } /* 예외 발생 Unhandled exception. System.Net.Sockets.SocketException (10004): A blocking operation was interrupted by a call to WSACancelBlockingCall. at System.Net.Sockets.Socket.ReceiveFrom(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, EndPoint& remoteEP) at System.Net.Sockets.Socket.ReceiveFrom(Byte[] buffer, EndPoint& remoteEP) at Program.<Main>$(String[] args) in C:\...\ConsoleApp1\ConsoleApp2\Program.cs:line 12 */ </pre> <br /> 아쉽게도 ReceiveFrom의 경우에는 예외를 발생시키지 않는 버전의 메서드가 없습니다. 따라서, 만약 이런 경우에도 예외를 원하지 않는다면 Receive 호출로 대체해야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Socket socket = new Socket(AddressFamily.InterNetwork, <span style='color: blue; font-weight: bold'>SocketType.Dgram, ProtocolType.Udp</span>); socket.Connect(IPAddress.Loopback, 15000); // 바인딩 역할 ThreadPool.QueueUserWorkItem(socketClose, socket); byte[] buffer = new byte[4096]; socket.Receive(buffer, 0, buffer.Length, SocketFlags.None, out SocketError errorCode); if (errorCode != SocketError.Success) { Console.WriteLine("Receive error"); } void socketClose(object? state) { Thread.Sleep(1000); (state as Socket)?.Close(); } </pre> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1961&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그나저나, Close 시 Receive에서 발생하는 (어차피 try/catch로 처리할) 예외가 얼마나 성능에 영향을 미칠 수 있을까요? 물론, 예외가 발생하면 일반적인 코드보다 성능이 느려지는 것은 맞습니다. 그렇기 때문에 <a target='tab' href='https://docs.microsoft.com/en-us/dotnet/api/system.int32.parse'>Int.Parse</a>와 같은 메서드도 예외가 발생하지 않는 버전의 <a target='tab' href='https://docs.microsoft.com/en-us/dotnet/api/system.int32.tryparse'>Int.TryParse</a>를 제공하게 된 것입니다.<br /> <br /> 하지만, Receive는 어떨까요? 대부분의 경우 우리는 네트워크 코드를 프로토콜을 결정해 Send/Receive를 하게 됩니다. 즉, 일반적인 통신에서는 Receive에서 예외가 발생할 수 있는 여지가 거의 없습니다. 가령, 네트워크가 강제로 끊겼거나 하는 등의 상황이거나... 아니면 잘못 만든 코드로 인해 Socket.Close가 발생하는 경우일 것입니다.<br /> <br /> 그런 상황에서 발생할 예외의 횟수라면 엄밀히 말해서 성능에 거의 영향이 없습니다. 아마도 특정 시간 내에서 유의미한 성능 저하를 보려면 상호 간의 네트워크 통신을 엄청난 고속으로 실행하면서도, 그 통신에 자주 Close 상황이 있어야 한다는 것인데... 현실적으로 그런 상황은 모든 통신 프로토콜에 버그가 있다고 봐야 합니다.<br /> <br /> 그러니, 너무 예외 발생에 신경을 쓰기보다는 좀 더 성능에 유의미하게 영향을 미치는 코드를 위주로 우선순위를 두고 접근하는 것을 권장합니다.<br /> <br /> (심지어 질문하신 분은, Receive의 out 인자에 대해 초기화하는 것까지도 속도 저하를 걱정하는데... 그 코드로 인한 속도 저하를 걱정해야 한다면 애당초 C#으로 해당 프로그램을 개발하는 것이 더 문제입니다. 그런 상황이라면 C/C++로 제작하는 것이 맞습니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1502
(왼쪽의 숫자를 입력해야 합니다.)