성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <div style='font-family: 맑은 고딕, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>WCF의 InactivityTimeout</div><br /> <br /> "<a target='_tab' href='http://www.yes24.com/24/goods/3661429'>유수석의 WCF 바이블</a>", 다들 읽어보셨죠?<br /> WCF 실무에 뛰어드시기 전에 꼭 한번 읽어보시기를 바랍니다. (사실 저도 ^^; 아직 9장까지밖에 못 읽었습니다.)<br /> <br /> 어쨌든! 저 역시 위의 책을 읽으면서 많이 배우고 있습니다. 예를 들어 FaultException 예외를 사용하면 세션이 Faulted 상태로 빠지지 않는다는 사실을 몰랐었고, WCF가 지원하는 세션을 크게 "트랜스포트 세션", "보안 세션", "신뢰할 수 있는 세션"으로 나눈다는 것을 머릿속으로 정리해놓지는 못했었습니다.<br /> <br /> 그런데,,, 옥의 티가 하나 있는데요. 바로 "신뢰할 수 있는 세션"의 "InactivityTimeout" 설명이 완벽하지 못하다는 정도! (완벽?)<br /> <br /> 그 책의 636페이지에 보면 아래와 같은 설명이 있는데요.<br /> <br /> <span style='BACKGROUND-COLOR: #ccffcc; FONT-STYLE: italic; MARGIN: 10px 0px 10px 10px; WIDTH: 800px; FONT-FAMILY: 맑은 고딕, Consolas, Verdana; COLOR: #005555'> "<br /> 신뢰할 수 있는 세션은 자체적으로 타임아웃을 InactivityTimeout 속성을 통해 설정할 수 있다. 이 타임아웃 시간 동안 클라이언트가 서비스를 호출하지 않으면 신뢰할 수 있는 세션은 종료된다. <br /> ... [생략] ...<br /> ReceiveTimeout과 InactivityTimeout 속성이 모두 설정될 수 있으므로 신뢰할 수 있는 세션에 실제로 적용되는 타임아웃 시간은 두 속성값 중 작은 값으로 결정됨을 알아두자.<br /> "</span><br /><br /> <br /> 위의 설명에 의하면, "ReceiveTimeout 설정과 InactivityTimeout 속성은 동일한 기능을 하는데, 클라이언트가 서버에 대해 지정된 ReceiveTimeout 또는 InactivityTimeout 시간 동안 호출하지 않으면 세션이 종료된다."로 요약될 수 있습니다. 단지, "트랜스포트 세션"에서는 ReceiveTimeout 값이 사용되고, "신뢰할 수 있는 세션"에서는 부가적으로 InactivityTimeout 값이 하나 더 제공된다는 정도의 의미로 해석됩니다.<br /> <br /> 하지만, 실제로 테스트 해보면 이 설명이 사실과 다르다는 것을 알 수 있습니다.<br /> <br /> 예전에 제가 아래의 글에서 InactivityTimeout 값을 설명한 적이 있었는데요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > WCF : netTcpBinding에서의 각종 Timeout 값 설명 ; <a target='_tab' href='/2/0/536'>http://www.sysnet.pe.kr/2/0/536</a> </pre> <br /> 그 때는 말로만 설명하는 것으로 그쳤지만, 마침 <a target='_tab' href='/3/0/899'>질문</a>해 주신 분도 있고 하니 실험적으로 알 수 있는 방법을 제공하도록 하겠습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 이를 테스트 해보기 위해, 세션 사용이 가능한 netTcpBinding 종단점을 제공하는 서비스를 만들고 config에 다음과 같이 ReceiveTimeout, InactivityTimeout을 설정합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > <netTcpBinding> <binding name="netTcpBindingConfig" <b style='COLOR: blue'>receiveTimeout="00:01:30"</b> > <security mode="None"/> <reliableSession enabled="true" <b style='COLOR: blue'>inactivityTimeout="00:00:10"</b> ordered="true" /> </binding> </netTcpBinding></pre> <br /> 서비스의 메서드는 간단하게 Echo 메서드 하나만 정의하고 다음과 같이 시간 출력을 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > public void Echo() { Console.WriteLine(<b style='COLOR: blue'>"Echo: " + DateTime.Now.ToString("HH:mm:ss")</b>); OperationContext.Current.Channel.Closed += new EventHandler(Channel_Closed); OperationContext.Current.Channel.Faulted += new EventHandler(Channel_Faulted); } void Channel_Faulted(object sender, EventArgs e) { Console.WriteLine(<b style='COLOR: blue'>"Endpoint Faulted: " + DateTime.Now.ToString("HH:mm:ss")</b>); } void Channel_Closed(object sender, EventArgs e) { Console.WriteLine(<b style='COLOR: blue'>"Endpoint Closed: " + DateTime.Now.ToString("HH:mm:ss")</b>); } </pre> <br /> 그리고, 클라이언트에서는 바인딩 값에 아래와 같이 시간을 다르게 설정합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > netTcpBinding.ReliableSession.Enabled = true; netTcpBinding.ReliableSession.<b style='COLOR: blue'>InactivityTimeout = new TimeSpan(0, 0, 15);</b> </pre> <br /> 이제, 서비스를 시작하고 클라이언트에서 접속해서 Echo 메서드를 호출하면 다음과 같이 출력되고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > D:\...\bin>ConsoleApplication1.exe Press any key to exit... Echo: <b style='COLOR: blue'>02:09:45</b> </pre> <br /> 클라이언트에서 연결을 닫지 않은 체로 1분 30초가 지나면 서버 측의 ReceiveTimeout에서 지정한 데로 연결이 끊깁니다. 그래서 최종적으로 아래와 같은 출력 결과를 얻을 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > D:\...\ConsoleApplication1\bin>ConsoleApplication1.exe D:\...\bin>ConsoleApplication1.exe Press any key to exit... Echo: <b style='COLOR: blue'>02:09:45</b> Endpoint Closed: <b style='COLOR: blue'>02:11:15</b> </pre> <br /> 보시는 것처럼, 위와 같은 결과를 얻기 위해 정확하게 1분 30초가 소요되었습니다. 만약, 책의 내용대로라면 10초만에 연결이 끊겼어야 합니다.<br /> <br /> 자, 이제 그럼 InactivityTimeout 값을 테스트 해볼까요?<br /> <br /> 서버 측은 여전히 동일하게 실행하고, 클라이언트는 실행 후 연결을 닫지 않고 "Ctrl + C" 키를 누르거나 혹은 작업관리자를 통해서 강제 종료를 해봅니다. 이번에는 서버와 클라이언트에서 다음과 같이 동작하는 것을 확인할 수 있습니다.<br /> <br /> <img alt='wcf_inactivityTimeout_test_1.png' src='/SysWebRes/bbs/wcf_inactivityTimeout_test_1.png' /><br /> <br /> 보시는 것처럼, 클라이언트는 2시 13분 07초에 프로그램을 (비정상) 종료했고 서버는 정확히 InactivityTimeout 값에 지정된 10초 후인 2시 13분 17초에 클라이언트와의 연결을 종료시켰습니다. <br /> <br /> 자, 그럼 이번엔 반대로 서버를 강제 종료해봅니다. <br /> <br /> <img alt='wcf_inactivityTimeout_test_2.png' src='/SysWebRes/bbs/wcf_inactivityTimeout_test_2.png' /><br /> <br /> 서버가 (비정상) 종료한 시간은 2시 16분 03초이고, 클라이언트는 14초 걸려서 연결을 Faulted로 돌렸는데 이 값은 클라이언트 측의 코드를 통해 지정한 15초에 가까운 값입니다.<br /> <br /> 이 정도면, InactivityTimeout 값에 대해 알아볼 수 있는 실제적인 실험이었다고 볼 수 있겠지요.<br /> <br /> <a target='_tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=534&boardid=331301885'>테스트 한 프로젝트</a>를 올려두었으니 각자 로컬에서 테스트 해보시고 의심스러운 부분이 있으면 질문 주시기 바랍니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 제가 위에서 "완벽"하지 못하다고 말씀을 드렸는데요. 위의 테스트 결과대로라면 "완벽"이 아니라 아예 틀렸다고 볼 수 있는데, 왜 그런 단어를 사용했을까요? 궁금하지 않으세요? ^^<br /> <br /> InactivityTimeout 값이 매우 헷갈리는 것이 하나 있는데요. 바로 서버 측의 InactivityTimeout 값을 10초 미만으로 조정하면 그 동작이 ReceiveTimeout과 동일하다는 이상한(?) 규칙을 가지고 있다는 점입니다. 즉, 위의 테스트에서 서버 측의 InactivityTimeout 값을 6초로 지정하면, 6초 내에 클라이언트로부터 호출이 없으면 연결이 끊기게 됩니다.<br /> <br /> 엄청 재미있는 사실은, 클라이언트 측의 InactivityTimeout 값을 10초 미만으로 지정하는 것은 무관하다는 것입니다. 즉 서버 측에 대해서만 10초 미만으로 지정한 경우 ReceiveTimeout처럼 동작합니다.<br /> <br /> 제 생각에는 아마도 이것은 버그가 아닐까 생각됩니다. 일관성을 위해서 InactivityTimeout 값을 10초 미만으로 지정해도 위의 테스트 결과대로 나와야 합니다. (버그가 아니라면, 아마도 상대편이 죽은 경우 곧바로 자원을 회수해야 하는 특별한 서비스를 위해서 10초 미만으로 지정되는 경우에 한해 그와 같은 동작을 일부러 하도록 한 것이 아닐런지...!)<br /> <br /> 아무튼, 이런 이상한 현상으로 인해 10초 미만에 대해서는 책에서 설명한 내용이 맞기 때문에 "완벽"하지 않다고 표현한 것입니다.<br /> <br /> 참고로, .NET 3.5/4.0에서 모두 위와 같이 10초 현상을 겪고 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 좀 더 부가적인 설명을 드리자면, 위의 InactivityTimeout 실험 데이터는 아주 이상적인 경우에 맞아 떨어진 수치입니다. 왜냐하면, InactivityTimeout 값을 판단하기 위한 서버와 클라이언트의 heart-beat 체크가 정확히 1초마다 이뤄지는 것은 아니기 때문입니다. 확인을 위해 소켓 API를 가로채기 해보면 다음과 같은 결과를 얻을 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > [Srv] Send: 03시 01분 19초 [Cln] Send: 03시 01분 19초 [Srv] Send: 03시 01분 26초 [Cln] Send: 03시 01분 26초 [Srv] Send: 03시 01분 34초 [Cln] Send: 03시 01분 34초 [Srv] Send: 03시 01분 41초 [Cln] Send: 03시 01분 41초 </pre> <br /> 서버(Srv)와 클라이언트(Cln) 모두 서로를 대략 7초 간격으로 확인하는 것을 볼 수 있는데요. 7초 간격은 클라이언트 측의 15초에서 절반으로 나뉜 시간입니다. 만약 18초로 설정하면 9초 간격으로 서로 체크하는 것이 확인됩니다.<br /> <br /> 테스트에 의하면, heart-beat을 체크하는 주기는 "서버와 클라이언트 중 큰 시간의 절반"과 "서버와 클라이언트 중 작은 값"을 비교해서 낮은 값으로 설정하게 됩니다.<br /> <br /> 예를 들어, 서버는 15초, 클라이언트는 20초라고 설정되어 있다면 "20 / 2 = 10초 < 15초"이기 때문에 10초로 선택되어 클라이언트, 서버 양측 모두 10초 단위로 서로 살아 있는지를 확인하게 됩니다.<br /> <br /> (물론, 이 실험값은 공식적으로 문서화된 것이 아니기 때문에 향후 닷넷 프레임워크에서는 달라질 수 있겠고!)<br /> <br /> 그래서, 정확하게 상대편에서 종료한 시점부터 7초가 아니고, 체크하는 바로 그 시점에 상대편이 살아있어야 합니다. 즉, 위의 소켓 가로채기에서 보면 클라이언트가 03시 01분 37초에 종료했다면 그로부터 7초 후에 서버가 연결을 끊는 것이 아니고, 03시 01분 41초 - 체크를 하는 그 시점에 확인 후 곧바로 연결을 종료하게 됩니다.<br /> <br /> 한 가지 더, 위의 heart-beat 주기는 ReceiveTimeout에도 적용됩니다. 이 때문에, ReceiveTimeout 값이 1분 30초이고, InactivityTimeout 설정의 영향으로 heart-beat이 10초로 설정되어 있다면 최대 1분 40초까지 ReceiveTimeout이 적용될 수 있습니다. 즉, 03시 01분 10초에 마지막으로 클라이언트로부터 호출을 받았던 서비스인 경우 heart-beat 확인이 03시 02분 39초에 되었다면 그 시점에는 1분 30초가 지난 것이 아니기 때문에 연결이 끊기지 않다가 다음번 heart-beat 확인이 되는 03시 02분 49초가 되어서야 연결이 끊기게 됩니다.<br /> <br /><br /><hr /><span style='color: Maroon'>[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
5379
(왼쪽의 숫자를 입력해야 합니다.)