성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
글쓰기
제목
이름
암호
전자우편
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# - I/O 스레드를 사용한 비동기 소켓 서버/클라이언트</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;' > C# - 파일의 비동기 처리 유무에 따른 스레드 상황 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12251'>https://www.sysnet.pe.kr/2/0/12251</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;' > Asynchronous Server Socket Example ; <a target='tab' href='https://learn.microsoft.com/ko-kr/dotnet/framework/network-programming/asynchronous-server-socket-example'>https://learn.microsoft.com/ko-kr/dotnet/framework/network-programming/asynchronous-server-socket-example</a> </pre> <br /> 테스트해보겠습니다. 사실 서버 측의 비동기 동작은 Accept가 전부이므로 그 부분만 요약해서 정리하면 다음과 같습니다.<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; using System.Net; using System.Net.Sockets; using System.Threading; namespace ConsoleApp1 { class Program { public static ManualResetEvent _eventAccept = new ManualResetEvent(false); static void Main(string[] args) { IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, 11000); <span style='color: blue; font-weight: bold'>Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);</span> try { listener.Bind(localEndPoint); listener.Listen(10); while (true) { _eventAccept.Reset(); Console.WriteLine("Waiting for a connection..."); <span style='color: blue; font-weight: bold'>listener.BeginAccept(AcceptCallback, listener);</span> _eventAccept.WaitOne(); } } catch { } } private static void <span style='color: blue; font-weight: bold'>AcceptCallback</span>(IAsyncResult ar) { Console.WriteLine("Accepted"); <span style='color: blue; font-weight: bold'>Console.ReadLine();</span> _eventAccept.Set(); Socket listener = (Socket)ar.AsyncState; Socket clientSocket = listener.EndAccept(ar); clientSocket.Close(); } } } </pre> <br /> 소켓의 비동기가 다소 편리한 점이 있다면, <a target='tab' href='https://www.sysnet.pe.kr/2/0/12251'>지난 글에서 다룬 FileStream</a>과는 달리 명시적으로 비동기 소켓 지정이 필요 없다는 것입니다. 단순히 그냥 Begin/End APM 패턴 호출을 하면 .NET Framework BCL 내부에서 FILE_FLAG_OVERLAPPED 관련 처리를 자동으로 해주기 때문입니다.<br /> <br /> 실제로 위의 예제를 실행해 BeginAccept까지 실행한 시점에 windbg로 살펴보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>!threads</span> ThreadCount: 3 UnstartedThread: 0 BackgroundThread: 2 PendingThread: 0 DeadThread: 0 Hosted Runtime: no Lock ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 0 1 4238 0000022c068a30c0 202a020 Preemptive 0000022C08501CD0:0000022C08501FD0 0000022c06878800 0 MTA 5 2 8950 0000022c068ce4c0 2b220 Preemptive 0000000000000000:0000000000000000 0000022c06878800 0 MTA (Finalizer) 6 3 4ffc 0000022c069302a0 1020220 Preemptive 0000000000000000:0000000000000000 0000022c06878800 0 Ukn (Threadpool Worker) </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>!eestack</span> --------------------------------------------- Thread 0 Current frame: ntdll!NtWaitForMultipleObjects+0x14 Child-SP RetAddr Caller, Callee ...[생략]... 00000047c7faee50 00007ffe5df59c9f (MethodDesc 00007ffe5db78d98 +0x2f <span style='color: blue; font-weight: bold'>System.Threading.WaitHandle.WaitOne</span>(Int32, Boolean)), calling (MethodDesc 00007ffe5db78dc0 +0 System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle, Int64, Boolean, Boolean)) 00000047c7faee90 00007ffe1f9d0a8c (MethodDesc 00007ffe1f8c5a28 +0x18c ConsoleApp1.Program.Main(System.String[])) ...[생략]... 00000047c7fafc80 00007ffe7f04cda4 clr!CorExeMain+0x14, calling clr!_CorExeMainInternal 00000047c7fafcc0 00007ffe804c8c01 mscoreei!CorExeMain+0x112 00000047c7fafcf0 00007ffe808a1560 MSCOREE!GetShimImpl+0x18, calling MSCOREE!InitShimImpl 00000047c7fafd00 00007ffe808aacd2 MSCOREE!CorExeMain_Exported+0x102, calling KERNEL32!GetProcAddressStub 00000047c7fafd20 00007ffe808aac42 MSCOREE!CorExeMain_Exported+0x72, calling MSCOREE!guard_dispatch_icall_nop 00000047c7fafd50 00007ffe96346fd4 KERNEL32!BaseThreadInitThunk+0x14, calling KERNEL32!guard_dispatch_icall_nop 00000047c7fafd80 00007ffe96c9cec1 ntdll!RtlUserThreadStart+0x21, calling ntdll!guard_dispatch_icall_nop --------------------------------------------- Thread 5 Current frame: ntdll!NtWaitForMultipleObjects+0x14 Child-SP RetAddr Caller, Callee ...[생략]... 00000047c86ffbf0 00007ffe7f038706 <span style='color: blue; font-weight: bold'>clr!FinalizerThread::FinalizerThreadStart</span>+0x116, calling clr!ManagedThreadBase_DispatchOuter 00000047c86ffc50 00007ffe7ef15863 clr!operator delete+0x33 00000047c86ffc90 00007ffe7ef1b5b5 clr!Thread::intermediateThreadProc+0x8b 00000047c86ffd10 00007ffe7ef1b590 clr!Thread::intermediateThreadProc+0x66, calling clr!_chkstk 00000047c86ffd50 00007ffe96346fd4 KERNEL32!BaseThreadInitThunk+0x14, calling KERNEL32!guard_dispatch_icall_nop 00000047c86ffd80 00007ffe96c9cec1 ntdll!RtlUserThreadStart+0x21, calling ntdll!guard_dispatch_icall_nop --------------------------------------------- Thread 6 Current frame: ntdll!NtWaitForMultipleObjects+0x14 Child-SP RetAddr Caller, Callee 00000047c7fef5e0 00007ffe947b8910 KERNELBASE!WaitForMultipleObjectsEx+0xf0, calling ntdll!NtWaitForMultipleObjects ...[생략]... 00000047c7fef8d0 00007ffe7eff1397 <span style='color: blue; font-weight: bold'>clr!ThreadpoolMgr::WaitThreadStart</span>+0xdd, calling KERNEL32!WaitForMultipleObjectsEx 00000047c7fef940 00007ffe96346fd4 KERNEL32!BaseThreadInitThunk+0x14, calling KERNEL32!guard_dispatch_icall_nop 00000047c7fef970 00007ffe96c9cec1 ntdll!RtlUserThreadStart+0x21, calling ntdll!guard_dispatch_icall_nop </pre> <br /> BeginAcccept에 따른 비동기 호출은 TCP device driver에 전달되어 Main을 실행하던 스레드만 WaitOne에 걸린 것을 볼 수 있습니다. 이후, 클라이언트 측의 소켓 연결로 AcceptCallback이 실행돼 "Console.ReadLine()" 메서드 호출 시점에 다시 windbg로 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:010> <span style='color: blue; font-weight: bold'>!threads</span> ThreadCount: 5 UnstartedThread: 0 BackgroundThread: 4 PendingThread: 0 DeadThread: 0 Hosted Runtime: no Lock ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 0 1 4238 0000022c068a30c0 202a020 Preemptive 0000022C08501CD0:0000022C08501FD0 0000022c06878800 0 MTA 5 2 8950 0000022c068ce4c0 2b220 Preemptive 0000000000000000:0000000000000000 0000022c06878800 0 MTA (Finalizer) 6 3 4ffc 0000022c069302a0 1020220 Preemptive 0000000000000000:0000000000000000 0000022c06878800 0 Ukn (Threadpool Worker) 7 4 5dc8 0000022c06936fd0 8029220 Preemptive 0000022C08502760:0000022C08503FD0 0000022c06878800 1 MTA (Threadpool Completion Port) 11 5 6bf4 0000022c06938da0 8029220 Preemptive 0000000000000000:0000000000000000 0000022c06878800 0 MTA (Threadpool Completion Port) </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:010> <span style='color: blue; font-weight: bold'>!eestack</span> --------------------------------------------- Thread 0 Current frame: ntdll!NtWaitForMultipleObjects+0x14 Child-SP RetAddr Caller, Callee ...[생략]... 00000047c7faee50 00007ffe5df59c9f (MethodDesc 00007ffe5db78d98 +0x2f <span style='color: blue; font-weight: bold'>System.Threading.WaitHandle.WaitOne</span>(Int32, Boolean)), calling (MethodDesc 00007ffe5db78dc0 +0 System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle, Int64, Boolean, Boolean)) 00000047c7faee90 00007ffe1f9d0a8c (MethodDesc 00007ffe1f8c5a28 +0x18c ConsoleApp1.Program.Main(System.String[])) ...[생략]... 00000047c7fafcc0 00007ffe804c8c01 mscoreei!CorExeMain+0x112 00000047c7fafcf0 00007ffe808a1560 MSCOREE!GetShimImpl+0x18, calling MSCOREE!InitShimImpl 00000047c7fafd00 00007ffe808aacd2 MSCOREE!CorExeMain_Exported+0x102, calling KERNEL32!GetProcAddressStub 00000047c7fafd20 00007ffe808aac42 MSCOREE!CorExeMain_Exported+0x72, calling MSCOREE!guard_dispatch_icall_nop 00000047c7fafd50 00007ffe96346fd4 KERNEL32!BaseThreadInitThunk+0x14, calling KERNEL32!guard_dispatch_icall_nop 00000047c7fafd80 00007ffe96c9cec1 ntdll!RtlUserThreadStart+0x21, calling ntdll!guard_dispatch_icall_nop --------------------------------------------- Thread 5 Current frame: ntdll!NtWaitForMultipleObjects+0x14 Child-SP RetAddr Caller, Callee ...[생략]... 00000047c86ffbf0 00007ffe7f038706 <span style='color: blue; font-weight: bold'>clr!FinalizerThread::FinalizerThreadStart</span>+0x116, calling clr!ManagedThreadBase_DispatchOuter 00000047c86ffc50 00007ffe7ef15863 clr!operator delete+0x33 00000047c86ffc90 00007ffe7ef1b5b5 clr!Thread::intermediateThreadProc+0x8b 00000047c86ffd10 00007ffe7ef1b590 clr!Thread::intermediateThreadProc+0x66, calling clr!_chkstk 00000047c86ffd50 00007ffe96346fd4 KERNEL32!BaseThreadInitThunk+0x14, calling KERNEL32!guard_dispatch_icall_nop 00000047c86ffd80 00007ffe96c9cec1 ntdll!RtlUserThreadStart+0x21, calling ntdll!guard_dispatch_icall_nop --------------------------------------------- Thread 6 Current frame: ntdll!NtDelayExecution+0x14 Child-SP RetAddr Caller, Callee 00000047c7fef830 00007ffe947b818e KERNELBASE!SleepEx+0x9e, calling ntdll!NtDelayExecution 00000047c7fef8a0 00007ffe947b8202 KERNELBASE!SleepEx+0x112, calling ntdll!RtlActivateActivationContextUnsafeFast 00000047c7fef8d0 00007ffe7eff12f9 <span style='color: blue; font-weight: bold'>clr!ThreadpoolMgr::WaitThreadStart</span>+0x90, calling KERNEL32!SleepEx 00000047c7fef940 00007ffe96346fd4 KERNEL32!BaseThreadInitThunk+0x14, calling KERNEL32!guard_dispatch_icall_nop 00000047c7fef970 00007ffe96c9cec1 ntdll!RtlUserThreadStart+0x21, calling ntdll!guard_dispatch_icall_nop --------------------------------------------- Thread 7 Current frame: ntdll!NtReadFile+0x14 Child-SP RetAddr Caller, Callee ...[생략]... 00000047c87ff200 00007ffe5df98773 (MethodDesc 00007ffe5db7d3a8 +0x163 <span style='color: blue; font-weight: bold'>System.IO.StreamReader.ReadLine</span>()) ...[생략]... 00000047c87ffbb0 00007ffe7f09fbd5 clr!ThreadpoolMgr::CompletionPortThreadStart+0x604 00000047c87ffc20 00007ffe7ef15863 clr!operator delete+0x33 00000047c87ffc60 00007ffe7ef1b5b5 clr!Thread::intermediateThreadProc+0x8b 00000047c87ffd60 00007ffe7ef1b590 clr!Thread::intermediateThreadProc+0x66, calling clr!_chkstk 00000047c87ffda0 00007ffe96346fd4 KERNEL32!BaseThreadInitThunk+0x14, calling KERNEL32!guard_dispatch_icall_nop 00000047c87ffdd0 00007ffe96c9cec1 ntdll!RtlUserThreadStart+0x21, calling ntdll!guard_dispatch_icall_nop --------------------------------------------- Thread 11 Current frame: ntdll!NtRemoveIoCompletion+0x14 Child-SP RetAddr Caller, Callee 00000047c8b3fbc0 00007ffe947ce9bf <span style='color: blue; font-weight: bold'>KERNELBASE!GetQueuedCompletionStatus</span>+0x4f, calling ntdll!NtRemoveIoCompletion 00000047c8b3fc20 00007ffe7f09faa6 clr!ThreadpoolMgr::CompletionPortThreadStart+0x215, calling KERNEL32!GetQueuedCompletionStatusStub 00000047c8b3fc90 00007ffe7ef15863 clr!operator delete+0x33 00000047c8b3fcd0 00007ffe7ef1b5b5 clr!Thread::intermediateThreadProc+0x8b 00000047c8b3fe50 00007ffe7ef1b590 clr!Thread::intermediateThreadProc+0x66, calling clr!_chkstk 00000047c8b3fe90 00007ffe96346fd4 KERNEL32!BaseThreadInitThunk+0x14, calling KERNEL32!guard_dispatch_icall_nop 00000047c8b3fec0 00007ffe96c9cec1 ntdll!RtlUserThreadStart+0x21, calling ntdll!guard_dispatch_icall_nop </pre> <br /> I/O 스레드 하나(위의 출력에서는 7번 스레드)가 AcceptCallback을 수행하고 있음을 확인할 수 있습니다. 즉, 비동기 I/O 호출에 따른 전형적인 스레드 사용예를 따르고 있는 것입니다.<br /> <br /> <img onclick='toggle_img(this)' class='imgView' alt='io_thread_socket_dgm_1.png' src='/SysWebRes/bbs/io_thread_socket_dgm_1.png' /><br /> <br /> 이하, BeginReceive, BeginSend 모두 동일하게 I/O 스레드를 사용하는 것에는 변함이 없습니다.<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1601&boardid=331301885'>첨부 파일은 이 글의 예제를 포함</a>하며, <a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1602&boardid=331301885'>다이어그램 또한 PPT 원본</a>을 올렸습니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
5801
(왼쪽의 숫자를 입력해야 합니다.)