성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] A graphical depiction of the steps ...
[정성태] 질문을 주셔서 출판사 측에 문의를 했습니다. 약 한 달 정도 후...
[Thorondor
] @정성태 개인 블로그인데도 거의 커뮤니티 급 인 것 같아요. 요...
[정성태] Roll A Lisp In C - Reading ; https...
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <br /> <div class='mainCenterTitle'>.NET 비동기 Socket과 스레드</div><br /> <br /> 대부분의 Visual C++ 개발자들은 아마도 비동기 소켓을 구현해야 하는 상황에서 MFC의 CAsyncSocket 클래스를 사용하는 것이 일반적이었을 것입니다. 구현이 매우 쉽다는 장점이 있는데요, 예를 들어, Connect를 호출하면 단순히 상속받은 클래스에서 OnConnect 메서드를 재정의하면 되었고, 해당 메서드를 호출하는 것 또한 Connect/OnConnect 모두 동일한 스레드에서 실행이 되었습니다. 그런데, 문제는 이렇게 구현된 코드를 .NET으로 포팅하는 경우에는 문제가 발생할 여지가 있습니다. 왜냐하면, .NET의 비동기 소켓은 스레드를 달리하기 때문입니다. 어디... 어떻게 다른지 한번 살펴볼까요!<br /> <br /> 아래는 MFC의 CAsyncSocket을 이용한 간단한 예제입니다.<br /> <br /> <pre class='code'> class CMyAsyncSocketTest : public CAsyncSocket { public: CMyAsyncSocketTest(); virtual ~CMyAsyncSocketTest(); virtual void OnConnect(int nErrorCode) { CAsyncSocket::OnConnect(nErrorCode); wchar_t chBuf[4096]; wsprintf(chBuf, L"%d\r\n", ::<b>GetCurrentThreadId</b>()); ::OutputDebugString(chBuf); } }; BOOL CWinSocketTestDlg::OnInitDialog() { CDialog::OnInitDialog(); // 중간 생략 wchar_t chBuf[4096]; wsprintf(chBuf, L"%d\r\n", ::<b>GetCurrentThreadId</b>()); ::OutputDebugString(chBuf); m_socket.Create(); m_socket.AsyncSelect(FD_CONNECT); m_socket.Connect(L"127.0.0.1", 80); return TRUE; } </pre> <br /> 실행시켜 보면, OutputDebugString 출력 결과가 동일한 스레드 ID를 출력하는 것을 확인할 수 있습니다. 비교를 위해서, 위와 같은 소스 코드를 .NET 비동기 소켓으로 구현해 보면,<br /> <br /> <pre class='code'> private void Form1_Load(object sender, EventArgs e) { Socket socket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPHostEntry host = Dns.GetHostEntry("127.0.0.1"); IPAddress ipv4 = null; foreach (IPAddress ipAddr in host.AddressList) { if (ipAddr.AddressFamily == AddressFamily.InterNetwork) { ipv4 = ipAddr; break; } } IPEndPoint endPoint = new IPEndPoint(ipv4, 80); <b>Debug.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString());</b> socket.BeginConnect(endPoint, callback, null); } public void callback(IAsyncResult ar) { <b>Debug.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString());</b> } </pre> <br /> 이번 실행 결과는 MFC와는 달리, Thread ID 값이 서로 다르게 나옵니다. 즉, .NET에서는 해당 메서드 실행이 ThreadPool로부터 빌려온 스레드에서 이루어지기 때문에 이에 대한 고려를 미리 충분히 해줘야 합니다. 예를 들면, COM 개체 생성을 Form1_Load 코드에서 했다면, 예전의 MFC 코드에서는 OnConnect 메서드에서 해당 COM 개체를 정상적으로 접근할 수 있었겠지만, .NET의 비동기 코드에서는 불가능하게 된 것입니다.<br /> <br /> 가만 살펴보니, .NET의 소켓은 비동기 모델을 2가지로 지원해 주는 것을 알 수 있습니다. 위와 같이 Callback을 이용하는 방법도 있지만, .NET Event를 통한 방법도 제공하고 있습니다. 이에 대한 예제는 아래와 같습니다. (위의 예제와 중복된 코드는 생략했습니다.)<br /> <br /> <pre class='code'> private void Form1_Load(object sender, EventArgs e) { // 중간 생략 SocketAsyncEventArgs arg = new SocketAsyncEventArgs(); arg.RemoteEndPoint = new IPEndPoint(ipv4, 80); arg.Completed += new EventHandler<SocketAsyncEventArgs>(arg_Completed); socket.ConnectAsync(arg); } void arg_Completed(object sender, SocketAsyncEventArgs e) { <b>Debug.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString());</b> } </pre> <br /> 실행해 보시면 아시겠지만, 애석하게도 스레드를 달리 하는 것에는 아무런 차이가 없습니다. <br /> <br /> <hr style='width: 50%' /> <br /> <br /> 이에 대한 해결 방법은, 소켓 타입 자체에서는 찾을 수가 없었습니다. 다행히, .NET 2.0에서는 SynchronizationContext를 통해서 MFC의 CAsyncSocket 클래스와 비슷한 처리를 해줄 수가 있습니다. 이를 위해 코드를 다음과 같이 바꿀 수 있습니다.<br /> <br /> <pre class='code'> void arg_Completed(object sender, SocketAsyncEventArgs e) { SynchronizationContext tx = SynchronizationContext.Current; tx.Send(delegate(object objState) { <b>Debug.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString());</b> }, null ); } </pre> <br /> 위의 코드로 그럼 모든 문제가 해결된 것 같습니다. Send 메서드의 delegate 코드 블록 안에서는 COM 개체 접근을 해도 정상적으로 처리가 됩니다.<br /> <br /><br /><hr /><span style='color: Maroon'>[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
9129
(왼쪽의 숫자를 입력해야 합니다.)