Win32 API나 MFC와 마찬가지로 .NET Framework에서도 비동기 방식으로 소켓 통신을 하기 위한 방법을 제공한다. 그 방법은 MSDN 내에 비동기 서버 소켓 사용과 비동기 클라이언트 소켓 사용에 관한 아티클에 상세하게 소개 되어 있다.

이 글에서 소개하고자 하는 AsyncSocketClient와 AsyncSocketServer는 .NET Framework에서 제공하는 비동기 소켓을 좀 더 사용하기 쉽게 만든 일종의 Wrapper 라이브러리라고 할 수 있다. 물론, .NET Framework 자체에서 소켓을 좀 더 고수준화한 TcpClient와 TcpListener란 클래스도 제공하지만; 제약이 많다;

물론, Socket에서 제공하는 모든 부분까지 구현한 것은 아니고; Connect, Close, Send, Receive, Accept 정도만 제공하는 것으로 소스를 공개함으로써 누구나 수정, 재배포가 가능하도록 하였다(다시 말해서; 자기가 필요한 부분은 추가로 구현해서 써도 상관없다.); 하지만, 가급적이면 저의 흔적을 말살하지 않기를..ㅠㅠ;

아, 그리고 이 라이브러리는 100% TCP 기반으로 구현되어 있다; UDP는 제공하지 않는다;

이 라이브러리가 .NET Framework에서 제공하는 소켓 클래스보다 편리한 부분은 소켓에서 발생하는 Connect, Close, Accept, Error, Send, Receive 등을 이벤트화 하였기에; 폼이나 다른 클래스에서 소켓을 핸들링 하는 것이 쉬워졌다; 다시 말해서, 폼이나 클래스에서 이벤트 핸들러만 재정의하면 소켓 이벤트를 처리할 수 있다;

사용자 삽입 이미지

위 그림에서 알 수 있듯이, 본 라이브러리는 기본적으로 AsyncSocketClass란 하나의 부모 클래스와 클라이언트 소켓 파트를 구현한 AsyncSocketClient, 서버 소켓 파트를 구현한 AsyncSocketServer로 이루어져 있다;

자식 클래스들을 좀 더 자세히 살펴보면 다음과 같다;

사용자 삽입 이미지
AsyncSocketClient는 public method로 Connect, Close, Send, Receive가 지원된다; 하지만; Receive는 특수한 경우를 제외하고는 개발자가 직접 호출할 필요는 없다; (이 부분은 나중에 좀 더 자세하게 설명하도록 한다..) 왜냐하면; 기본적으로 연결이 성공하면 데이터 수신을 위해 자동적으로 수신 대기 상태가 되기 때문이다;

만약, 데이터 수신을 성공하면, OnReceive 이벤트를 재정의 하여, 이 이벤트 핸들러에서 수신한 데이터를 지지고 볶고 하면 된다.OnConnect, OnClose, OnSend 모두 같다;

아래는 AsyncSocketClient를 이용해 구현한 샘플 프로그램의 코드 일부(이벤트 재정의 부분)이다.
사용자 삽입 이미지

여기서 중요한 것은 AsyncSocketClient 생성자의 인자로 숫자 0이 들어갔는데; 이는 다수의 AsyncSocketClient 인스턴스들을 생성을 가졍했을 때, 각각을 식별하기 위한 ID라고 할 수 있다.

그리고 그 아래 OnConnect 이벤트 핸들러가 있는데;
인자로 object sender와 AsyncSocketConnectionEventArgs e가 들어간다.

sender는 AsyncSocketClient로 캐스팅해서 쓰면, 이벤트가 발생한 소켓 클라이언트 자신이다;
(AsyncSocketClient worker = (AsyncSocketClient)sender;)

AsyncSocketConnectionEventArgs는 이벤트가 발생한 소켓 클라이언트의 ID 등을 포함하고 있다;

사용자 삽입 이미지

OnReceive를 살펴보면 넘어오는 Event 인자가 소켓의 ID, 수신한 바이트 수(ReceivedBytes), 수신한 데이터(ReceivedData)가 포함되어 있음을 알 수 있다. 참고로 위의 코드는 수신한 데이터가 byte[] 타입이기 때문에, string으로 캐스팅하기 위한 Encoding.... 코드가 포함되어 있다.

그 다음 AsyncSocketServer로 넘어가면;

AsyncSocketServer는 public method로 Listen, Stop이 지원된다; 만약 Client의 접근이 Accept 되면, OnAccept 이벤트가 발생하므로, OnAccept 이벤트를 재정의 하여, Client를 처리하면 된다;

사용자 삽입 이미지

AsyncSocketServer에서 가장 중요한 부분은 클라이언트 소켓의 접속을 처리하는 OnAccept 부분이다.

OnAccept에서 넘어오는 Event 인자는 접속이 허가된 소켓(e.Worker) 인스턴스가 포함되어 있다;

따라서, OnAccept 이벤트 핸들러에서는 AsyncSocketClient를 그대로 이용하기 위해, 이벤트 핸들러 내의
첫 줄과 같은 코드가 필요하다; 또한; 클라이언트의 ID가 중복되지 않도록, 새로운 ID도 부여한다.

그리고 그 다음이 앞서 언급한 AsyncSocketClient가 Receive를 호출해야할 특수한 상황이다;
구현 방법에 따라서; 이 부분은 Accept 코드 내에서 처리할 수도 있었지만; 어짜피 개발자가 이벤트 핸들러 연결도 필요하기 때문에; 그냥 이렇게 구현하였다.

끝으로 새로 생성한 클라이언트 소켓들을 미리 정의한 이벤트 핸들러와 연결하는 코드들이 나오고;
끝으로 클라이언트 리스트에 Add한다. (다수의 클라이언트 접속 가능 예를 들기 위해서 이렇게 했음);

좀 더 편리한 개발을 위해서는; MSDN 수준의 Class Specification을 쓰고 싶었지만; 시간 및 기타 여건이 허락되지 않는 관계로 이만 줄임!

대신에 라이브러리 및 예제 소스를 공개하니; 이를 이용하면 되지 않을까... 무책임하게 생각해봅니다;

다운로드: http://www.nohungry.net/Data/AsyncSocket.zip
크리에이티브 커먼즈 라이센스
Creative Commons License

Trackback

Trackback Address :: http://www.nohungry.net/tt1/trackback/160

Comments

  1. 카논 2010/03/09 10:52

    좋은 정보 감사합니다.
    서버/클라에 대한 구현 팁을 좀더 알려주셧으면 하는..
    초보자에게 정말 많은 도움이 될거 같습니다.

    perm. |  mod/del. |  reply.
    • 노헝그리 2010/03/09 17:33

      저도 아는 것이 없지만; 매우 기초적인 내용으로 조금씩 쓰려고 생각 중이었습니다;

      날개발자지만; 명색이 직딩이다보니~ 잘 안되지만; 틈틈이 써보도록 하겠습니다~^^;

What's on your mind?

댓글 입력 폼
[로그인][오픈아이디란?]

[C#] Auto FTP Uploader

2010/03/03 15:29
사용자 삽입 이미지

 회사에서 맡은 프로젝트 중... 실시간으로 생성되는 데이터를 원격의 FTP 서버에 전송해야할 일이 생겼다.

 상용 FTP 클라이언트 프로그램에서 일종의 매크로 기능을 사용하면 이와 같은 기능을 대신할 수 있지만; 상용 프로그램을 쓰는 것도 부담 스럽고; 내가 원하는대로 커스터마이징 하는 것이 불가능해서 그냥 만들었다.

Auto FTP Uploader의 로직은 매우 심플하다; 특정 Source Folder를 설정하고, 업로드 할 FTP 주소와 계정 정보를 입력해둔 다. Auto FTP Uploader는 Source Folder의 Rename 이벤트를 감지하여, 이벤트가 발생하면, 원격 FTP 주소에 업로드 한다.

 Source Folder의 Create 이벤트를 감지할 경우, 파일 작성이 완료되는 시점을 알 수 없으므로, 데이터를 생성하는 부분에서 파일이 작성되는 시간 동안에는 확장자를 TMP로 만들고, 파일 작성이 완료되면 확장자를 DAT로 바꾸게 하였기에 Rename 이벤트를 감지하도록 했다. (Temp Folder와 Complete 폴더는 원격 FTP 서버의 요구 사항에 의해 만든 것으로 파일이 작성되면 원격 FTP 서버에 동일한 파일명의 Dummy File을 업로드 한다.)

 프로그램 자체는 뭐 특별한 기능은 없지만; .NET Framework를 이용하면 간단한 FTP를 구현하는 것은 일도 아니라는 생각이 든다. 또, C#을 처음 접하는 이들에게는 C#을 이해하는데 도움이 되지 않을까 생각한다.

 이 프로그램에 사용된 주요 프로그래밍 테크닉은 다음과 같다.

 1. 파일 입/출력
 2. FtpWebRequest 클래스의 사용법
 3. 간단한 Thread의 사용 예
 4. ManualResetEvent, Monitor를 이용한 동기화 기법
 5. 델리게이트(delegate)를 이용한 클래스 또는 폼 간의 이벤트 발생 및 전달

 코멘트가 상세히 포함되어 있지는 않지만; 복잡한 로직은 거의 없고, 가급적 모듈별로 분리하도록 노력하였기 때문에 크게 어려움은 없지 않을까 생각해본다...;

사용자 삽입 이미지

소스 다운로드 : http://www.nohungry.net/Data/FTPUploader.zip

수정 및 재배포 자유, 영리적 목적의 사용도 자유! 단, 출처만 밝혀주세요;
크리에이티브 커먼즈 라이센스
Creative Commons License

Trackback

Trackback Address :: http://www.nohungry.net/tt1/trackback/159

Comments

What's on your mind?

댓글 입력 폼
[로그인][오픈아이디란?]

리팩토링(Refactoring) #2

2010/02/04 12:41
첫 포스팅을 하고 두 달만에 포스팅을 한다는;; 그동안 얼마나 게을렀는가를 스스로 반성.ㅠㅠ 앞으로 두 번의 포스팅에 걸쳐 리팩토링의 정의... 리팩토링을 왜 해야 되고, 리팩토링을 할 때 어려운 점 등을 정리하고; 그 다음부터 실제 기술적인 면을 정리할 생각이다.

리팩토링 - 소프트웨어를 보다 쉽게 이해할 수 있고, 적은 비용으로 수정할 수 있도록 겉으로 보이는 동작의 변화 없이 내부 구조를 변경하는 것.

 리팩토링은 소프트웨어의 모든 문제점을 해결할 수 있는 방법은 아니지만, 많은 문제점을 해결할 수 있는 유용한 도구가 될 수 있으며, 다양한 목적으로 사용할 수 있다.

구체적으로 리팩토링을 적용할 수 있는 사례를 살펴보자면~

- 리팩토링은 소프트웨어의 디자인을 개선 시킨다.
앞서 얘기 했듯이 단기적인 목적을 이루기 위해서, 또는 코드 전체의 디자인을 제대로 이해하지 못하는 상태에서 코드를 변경할 때, 결국 그 코드는 원래의 구조를 잃을 것이며, 시간이 흐르면 이 효과는 누적되어, 종국에는 어떻게 이도 저도 건드리지 못하는 몬스터가 되는 경우가 발생한다. 정기적인 리팩토링 작업은 이런 문제를 미연에 방지할 수 있으며, 코드의 디자인을 유지하도록 도와준다. 가장 간단한 리팩토링의 예는 중복된 코드를 제거하는 것!

- 리팩토링은 소프트웨어를 더 이해하기 쉽게 만든다.
굳이 남이 이해하기 쉽도록 이타적인 생각을 할 필요도 없다. 그 대상은 미래의 자신이 될 수도 있다. 아마 누구나 자신이 코딩한 프로그램을 시간이 흘러 다시 봤을 때, 쉽게 이해하지 못하는 경험이 있을 것이다.

- 리팩토링은 버그를 찾도록 도와준다.
당연한 얘기로 이해하기 쉬운 소프트웨어가 버그를 찾기도 쉽다.

- 리팩토링은 프로그램을 빨리 작성하도록 도와준다.
개발 이외에 리팩토링을 해야되는데, 그것이 정말 시간을 단축시켜 주는 것일까? 하지만, 정기적인 리팩토링을 통해 코드의 디자인이 유지되고, 코드의 가독성이 높아진다면, 이후 새로운 기능을 추가하거나 기능을 수정 변경할 때 훨씬 수월하다.

 그럼, 언제 리팩토링을 해야될까?
 사실 날을 잡고, 리팩토링을 하는 것은 개발자에게 너무 많은 부담이 될 수 있다. (정말 부지런한 사람만이 할 수 있을 것 같다.)

- 기능을 추가할 때 리팩토링을 하라.
새로운 기능을 추가하는 경우, 지금의 디자인이 기능 추가가 쉽지 않은 디자인일 수 있다. 그 때 아 예전에 디자인을 할 때 이런 식으로 했었다면, 후회를 하지 말고, 새로이 리팩토링을 하라. 그것이 미래에 똑같은 후회를 방지 할 수 있는 방법이다.

- 버그를 수정할 때 리팩토링을 하라.

- 코드 리뷰를 할 때 리팩토링을 하라.
코드 리뷰는 지식이 개발팀 전체로 확산되는 것을 돕는 좋은 방법이다.

리팩토링을 공부하면서, 정말 나에게 뼈져리게 와닿는 내용이 많다. 시간에 쫓겨서란 변명을 일단 급한대로 코딩을 해오다, 시간이 흘러 기능 추가나 수정이 발생했을 때, 정말 후회한 기억이 지금도 새록 새록 난다. 리팩토링의 가장 큰 적은 귀차니즘이다.
크리에이티브 커먼즈 라이센스
Creative Commons License

Trackback

Trackback Address :: http://www.nohungry.net/tt1/trackback/158

Comments

What's on your mind?

댓글 입력 폼
[로그인][오픈아이디란?]

리팩토링(Refactoring) #1

2009/12/07 17:15

 직장인은 3년차쯤 되면... 슬럼프가 온다고 하는데, 내가 딱 그 모양인 것 같다;
할 일은 많은데, 일도 잘 되지 않고; 일을 하더라도 왠지 모르게 어수선하고, 깔끔하지가 못 하다;

과연 내 문제는 무엇이고, 해결책은 뭘까... 곰곰히 생각해보다 내린 결론은 공부다; 아무리 일이 바쁘지만...
일이 바쁘지 않더라도 마냥 놀다보니 발전이 없다. 늘 하던 짓만 하다보니, 결국 매너리즘에 빠지게 되는 것;

그래서 입사 초기에 했던 것 처럼... 한가지 주제를 잡고, 공부를 해보기로 마음을 먹었다; 그래야 내 스스로도
발전이 있을게 아닌가;

어떤 주제로 공부하는 것이 좋을까... 고민하다; 진즉에 사놓고 읽지 못하고 있던 "리팩토링(Refactoriing) - 나쁜 디자인의 코드를 좋은 디자인으로 바꾸는 방법 / 마틴 파울러 저/ 윤성준, 조재박 역"을 대상으로 삼기로 했다.

1주일에 하루 정도 날짜를 정해, 한 Chapter씩 읽고, 공부를 했다는 의미를 되살리기 위해 블로그에 요약을 적기로 결정했다;

Chapter 1은 간단한 비디오 대여 프로그램의 일부를 가지고, 왜 리팩토링이 필요한가에 대해서 다루고 있다. 저자의 언급대로 간단한 프로그램이고, 재사용성이나 확장성을 염두에 두고 있지 않다면, 리팩토링은 무의미 할 것이다.

하지만 학창 시절에 숙제를 위해 만든 프로그램이 아니라면... 대부분의 경우, 확장 또는 재사용성을 당연히 고려해야 마땅하다. 매우 이상적인 경우라면, 프로그램 디자인 단계에서 완벽한 클래스의 구조를 만들어, 정말 읽기 쉽고, 어디든지 붙이기 쉽게 만들 수도 있지만 현실적으로는 불가능하다.

프로젝트를 진행하다보면 처음 설계된 프로그램 디자인과 별개로 코딩해 나가면서 맞딱드리는 여러 요인에 의해 시스템 본래의 모습과 디자인을 따른 구조는 점점 사라지고, 결국 완벽한 보안을 갖춘 (경쟁사에서 이 코드를 가져가도 해독이 불가능한...) 코드가 된다.

리팩토링은 이러한 관례와 반대로 다소 서투른 디자인을 취해 재작업하여 잘 디자인된 코드를 만드는 과정이라 이해할 수 있다. 각각의 단계는 A 클래스의 멤버 변수를 B 클래스로 옮기거나, 클래스 코드의 일부를 수퍼 클래스나 서브 클래스로 옮기거나 하는 것이다. 이러한 작업들이 모여 근본적으로 디자인을 개선하게 된다.

리팩토링에서 가장 중요한 개념은 내부(코드 및 디자인)를 바꾸면서 외부(기능)을 바꾸지 않는 것이기 때문에, 코드 변경에 문제가 없는지 확인을 위한 강력한 테스트가 필수적이다.

보다 구체적인 리팩토링 테크닉은 다음에 또 정리하기로 하고, 저자가 1장에서 강조한 말이 기억에 남아 여기도 남기고자 한다.

"컴퓨터가 이해할 수 있는 코드는 어느 바보나 다 짤 수 있다. 좋은 프로그래머는 사람이 이해할 수 있는 코드를 짠다."

크리에이티브 커먼즈 라이센스
Creative Commons License

Trackback

Trackback Address :: http://www.nohungry.net/tt1/trackback/157

Comments

  1. 마틴 2010/01/04 17:00

    기대가 되는군.
    자주 들를게.

    perm. |  mod/del. |  reply.

What's on your mind?

댓글 입력 폼
[로그인][오픈아이디란?]

 D/B에서 데이터를 가져오는 경우, 데이터를 저장 또는 변경하는 경우에 그 데이터가 많다면... 일반적인 동기 방식으로 처리할 경우, 프로그램에서는 매우 난감한 상황이 발생할 수 있다.

 데이터를 처리하기 전까지 소위 프로그램이 멍 때리는 상황... 다른 모듈의 동작까지 블럭(Block)이 되고, UI 마저 "응답 없음"이란 상태에 놓일 수 있다.

 이에 대한 해결책이 바로 비동기 방식으로 D/B를 처리하는 것이다.

 C# 아니 엄밀히 말하면 .NET에서는 데이터베이스 뿐만아니라 네트워크 등 다양한 경우에 대한 비동기 메소드(Method) 등을 지원한다. 데이터베이스 관련해서는 크게 ExecuteReader와 ExecuteNonQuery 동작에 대한 비동기 함수들을 지원한다. 두 함수의 차이는 SELECT와 같은 Transaction-SQL을 처리할 수 있는 함수라는 것과 UPDATE, INSERT, DELETE와 같은 Non-Transaction-SQL을 처리할 수 있는 함수라는 것이다.

 만약 앞서 설명한 함수들을 이용할 경우, 이 함수가 결과를 반환할 때 까지 나머지 모듈들은 블럭이 된다. 반면에, 이에 대한 비동기 함수들인 BeginExecuteReader-EndExecuteReader, BeginExecuteNonQuery-EndExecuteNonQuery를 사용하면 블럭이 되는 현상을 방지할 수 있다.

 함수의 간단한 사용 방법은 다음과 같다.

사용자 삽입 이미지

 모든 비동기 함수는 반드시 Begin~으로 시작하는 함수와 End~로 시작하는 함수가 쌍을 이루어야 된다.

간단한 설명을 위해 이 함수에 D/B Connection과 Close까지 포함되고, 데이터를 읽어오기 위한 코드도 포함되어 있지만.. 엄밀히 말하면 이들도 각각 분리되어야 한다.

 특히, D/B로 부터 읽어온 데이터를 처리하는 부분(Grid에 데이터를 넣든지, 그래프로 그리던지)은 별도의 함수로 구현해서 Thread로 처리하는 것이 바람직하다. 왜냐하면, 프로그램이 멍 때릴 정도로 읽어와야할 데이터가 많다면.. 필히 이 데이터를 처리하는 부분을 단순히 처리할 경우에도 단순히 문제가 될 수 있다.
크리에이티브 커먼즈 라이센스
Creative Commons License

Trackback

Trackback Address :: http://www.nohungry.net/tt1/trackback/148

Comments

What's on your mind?

댓글 입력 폼
[로그인][오픈아이디란?]

사용자 삽입 이미지

Visual Studio 2005로 작성한 멀쩡히 돌아가던 내 소스를 새로 설치한 PC에 옮겨서 빌드를 했더니, 저런 어처구니 없는 오류가 나왔다.

근데, 아무리 찾아봐도... 저런 파일은 없었다; 아.. 뭐지? Visual Studio 2005가 잘못 깔렸나?

구글을 찾아봐도 안 나온다; 범인은 바로... 아이콘(ico) 파일 때문이었다;

해결 방법은... 아래 그림과 같이 솔루션 탐색기에서 해당 프로젝트 명에서 우클릭한 후, 속성을 선택한다.

사용자 삽입 이미지

그리고 리소스에서 아이콘을 원래 있던 아이콘에서 기본 아이콘으로 빌드하니... 오류가 생기지 않았다.
사용자 삽입 이미지

문제가 발생한 원인은 이게 끝!

근데 도무지... 이게 왜! 생기는지는 나도 알 수 없다. 아이콘 파일도 해당 경로에 정확히 있는데 말이다;

혹시 아이콘이 잘못 만들어졌다면... 내 PC에서도 안되어야 하는데; 참;
크리에이티브 커먼즈 라이센스
Creative Commons License

Trackback

Trackback Address :: http://www.nohungry.net/tt1/trackback/144

Comments

What's on your mind?

댓글 입력 폼
[로그인][오픈아이디란?]

사용자 삽입 이미지
 내가 구현한 프로그램은 핵심 모듈은  초 당 1000Hz의 샘플링 레이트로 데이터를 처리하는 부분이다.

 이 프로그램을 디버그 모드로 장기 테스트를 돌리면, 꼭 이틀 정도가 지나면... 강제적으로 위 그림과 같이 _CrtDbgBreat(); 에 브레이크 포인트가 걸린다.

위 코드는 _heap_alloc_dbg()의 일부분이다.

이 문제의 근본적인 원인은 바로 if(lRequest == _crtBreakAlloc) 이 부분이다. 그리하여, 디버그 모드에서 NEW를 2^32 - 1회 호출하면, 자동으로 이 곳에 도달하게 되는 것이다.

친절한 구글사마를 통해 알아본 결과, 위와 같은 문제를 해결하는 방법에는 4가지가 있는데... 그 방법은 다음과 같다. (원문을 그대로 붙인다.)

more..


결국 가장 간단한 방법은  VC를 7.0 이상으로 쓰는 것인데... 이상하게도 C++은 아직도 6.0이 가장 편한 것 같다. 왠지 7.0 이상은 화면부터 맘에 안 들어--; (그럼에도 불구하고, C# 할 때는 VS 2005를 잘 쓰고 있다.)
크리에이티브 커먼즈 라이센스
Creative Commons License

Trackback

Trackback Address :: http://www.nohungry.net/tt1/trackback/132

Comments

What's on your mind?

댓글 입력 폼
[로그인][오픈아이디란?]

개발자 면접 문제 중...

2008/06/27 11:15
Find three ways to change one character in the following code so that the resulting
code will print exactly 20 minus signs

Remember: for each solution you can only change "one" character from this original code:

int
i, n=20;
for(i =0 ; i < n ; i--)
  printf
("-");

: 인텔 개발자 인터뷰에서 나온 문제랍니다!^^ 자신은 과연 몇 개나 생각했나요?ㅋ

more..


출처: http://theeye.pe.kr/
크리에이티브 커먼즈 라이센스
Creative Commons License
TAG ,

Trackback

Trackback Address :: http://www.nohungry.net/tt1/trackback/130

Comments

  1. 하늘바다 2008/06/27 17:06

    난 두개~ 으앗.... 첫번째 걸 놓치다니.... -_-

    perm. |  mod/del. |  reply.

What's on your mind?

댓글 입력 폼
[로그인][오픈아이디란?]

 우연히 웹서핑을 하던 도중, Visual Leak Detector란 녀석을 알게 되었다.
(사실, 제법 이전부터 알고는 있었지만, 쓰지는 않았다.)

사용법은 매우 단순하다. 코드에 #inclue <vld.h>를 추가해주고, 디버그 모드로 돌려보면, Memory Leak이
존재할 경우, 사정없이 뱉어준다.


이 녀석을 이용해, 현재 구현중이던 프로그램에 적용해보니, Memory Leak이 발견되었다는 것이 아닌가?!
허거덩..-_- 분명, MS 자체의 디버그 모드로 돌렸을 때는 발견되지 않았었는데..!!!

"뭐야뭐야! 나같은 천재에게 Memory Leak은 어울리지 않아-ㅁ-;; 분명, Visual Leak Detector의 버그 일거
야..-ㅁ-" 같은 헛소리를 하면서.. 차근차근 Memory Leak이 발생할만한 원인을 탐색하던 바... 이르게 된 곳
은 STL의 ifstream이었다.


난 모든 코드를 배제하고, MFC의 Dialog 기반의 프로젝트를 하나 생성했다. 그리고, 단지 2줄의 코드만 추
가했다.

ifstream fin("sample.txt");
fin.close();

그랬음에도.. 우리의 Visual Leak Detector는 Memory Leak을 찾아냈다. 여기에도 또 문제가 있는 것이,
"sample.txt"란 파일이 존재할 경우에만, Memory Leak을 뱉어냈다. 즉, 존재하지 않는 파일의 경우엔 파일
Open을 해도 Memory Leak을 뱉어내지 않았다.

나는 확인을 위해.. 데브피아에서 질문을 올렸는데..

A님: Visual Leak Detector를 사용해보지는 않았으나 바운스체커도 메모리릭이 발견되네요. stl부분에서

B님:

VS6.0에 포함되어 있는 STL 버전이 상당히 구닥다리 입니다.

딩컴웨어의 구닥다리 버전이 포함되어 있어서 6.0에서는 stl을 사용할때

외부 STL을 사용하곤 했지요..

STL_Port 같은것을 사용하세요..


2003 이상 버전에서 포함되어 있는 STL을 그대로 쓰셔도 상관 없습니다.


친절하게 두 분의 답변이 돌아왔다. 결국엔 Visual Studio 6.0에 포함되어 있는 STL에 문제가 있는 것이다.
난 좀 더 확인을 위해 (왜케 집요해졌는지 모르겠다.) 짧은 영어로 MSDN을 헤맸는데, Visual Studio 6.0에서
STL string으로 Multi-Thread 프로그램을 구현할 경우, Memory Corruption이 발생할 수 있다는 MS의 얘기를
발견할 수 있었다.
MS에서 제시하는 해결책 역시 데브피아에서 B님이 답변해준 것과 비슷하게, Visual Studio 2003 이상 버전
을 쓰거나 STL 3rd party 라이브러리를 쓰라는 얘기였다.

난 이제 껏, STL하면, SGI STL이 전부인줄 알았을 정도로 참으로 무지했다.
알고보니, gcc는 SGI STL을 기반으로 gcc 컴파일러에 맞게 구현해놓은 것을 쓰고 있고, MS는
Dinkumware 사의 STL을 쓰고 있었다. 그리고, Borland C++ Builder는 최근까지 Roguewave의
STL을 쓰다가, STL Port로 넘어갔다.

C++ 표준이 되어버린 STL일지라도 각 회사마다 STL은 조금씩 특징이 있었다.
대표적으로 STL Port와 Dinkumware의 STL의 list<> 클래스의 list 크기를 반환하는 size() 함수를 예로 들
면.. (http://crowmaniac.net/crowmania/ 를 참고한 어느 분의 블로그에서 참고했습니다.)

Dinkumware 사의 STL에서 제공하는 list의 size() 함수는 시간 복잡도가 O(1)이었다.
size_type size() const{   return (_MySize);   }

반면에, STL Port에서 제공하는 list의 size() 함수는 시간 복잡도가 O(n)이었다.
size_type size() const
{
       size_type _result = distance(begin(), end());
       return _result;
}

distance() 함수가 Bidirectional iterator기 때문에 O(n)인 것이다.

잉?! 그럼, 두 말 할 나위 없이, Dinkumware 사의 STL이 좋은 것이냐?
꼭, 그런 것은 아니다..^^

size()를 O(1)으로 구현한 Dinkumware 사의 STL은 태생적으로 O(n)의 splice(끼워넣기)를 가지는 반면에,
STL Port는 O(1)의 splice만 가진다.

다시 얘기하면, size()를 자주 호출해야할 코드에서는 Dinkumware의 STL이 낫고, splice를 자주해야 하는
코드에서는 STL Port가 낫다고 볼 수 있다.

뭐, 난 갠적으로 정렬이 필요하지 않은 데이터의 경운 걍 vector를 쓰고, 정렬이 필요한 경우엔 Hashtable을
구현한 map을 쓰기 때문에-ㅁ-;; list를 잘 쓰지 않는다.

암튼.. 이제 STL Port를 깔러 가야겠다-ㅁ-

크리에이티브 커먼즈 라이센스
Creative Commons License

Trackback

Trackback Address :: http://www.nohungry.net/tt1/trackback/117

Comments

  1. 마틴 2007/11/29 11:58

    Visual Leak Detector라.. 좋은거 알았네 ^^*

    perm. |  mod/del. |  reply.
    • NOhungry 2007/11/29 23:09

      Memory Leak은 프로그래머들의 가장 큰 적이지요-ㅁ-

  2. 까막 2007/11/30 11:50

    어머나.. 어느분이 제 글을 참고하셨는지 갑자기 궁금.. 해지네요 :)
    STLPort 괜찮습니다. 씨익.

    Memory Leak을 코드단계에서 방지하는 방법중 하나가 Smart Pointer이지요.
    http://www.boost.org 에서 제공하는 boost::shared_ptr<>을 사용해 보시는 것도 좋을듯 합니다. :)

    perm. |  mod/del. |  reply.
    • NOhungry 2007/11/30 13:56

      오옷.. 안녕하세요.ㅋ

      STLPort 괜찮죠..^^ 근데, 까막님은 STLPort 버전 어떤거 쓰세요? 전 처음에 5.1.4 설치하려고 했더니, 빌드하고 Path를 제대로 설정했음에도 계속 Link Error 뜨고, 그래서..

      다시 4.5.3으로 바꿨답니다-ㅁ-

  3. 원두 2007/11/30 13:33

    Visual Leak Detector~ 괜찮은 녀석이군. 나도 나중에 함 써 봐야지~
    메모리 Leak이 몇개나 나올지... 내심 두렵구먼~ㅋㅋ

    perm. |  mod/del. |  reply.
    • NOhungry 2007/11/30 13:57

      에~ 애시당초 Memory Leak은 코드 단계에서 방지하는게 좋죠. 프로그래머가 메모리 해제를 하는게 아니라, 소멸자가 해주도록 하는 방향으로..^^

      RAII란 개념인데, SmartPointer라고도 하죠.

      사람들이 대부분 boost 라이브러리를 많이 쓰더라구요. 전 그냥, STL에 있는 auto_ptr를 쓰거나, 그냥 구현해서 쓴답니다.ㅋ

  4. 까막 2007/11/30 18:50

    STLPort는 5.1.4(at VC++2003, 2005) 씁니다. :)

    std::auto_ptr도 좋긴한데 컨테이너에 넣을수가 없다는 최대단점이(!!).. ;ㅁ;

    perm. |  mod/del. |  reply.
  5. 우하하 2008/08/07 08:42

    다음 URL을 참고해서 VC6에 있는 STL에 대해서 버그픽스를 해보시죠.
    http://woohaha.egloos.com/790425
    버그픽스한 제 시스템에서는 메모리릭이 발생하지 않는 것으로 봐서 VC6에 들어있는 STL의 버그 때문에 생기는 현상인 듯 싶군요.
    [참조]
    http://www.dinkumware.com/vc_fixes.html
    http://madchick.egloos.com/237931

    perm. |  mod/del. |  reply.

What's on your mind?

댓글 입력 폼
[로그인][오픈아이디란?]

우리 회사에서 개발 중인 무선 계측기는 검색 기능을 제공하는데, 검색 방법은 UDP 프로토콜을 이용한 브로드캐스팅이다. 짜증스럽게도, UDP 프로토콜로 브로드캐스팅을 하기 위해서는 IP 대역이 맞아야 한다.

우리의 사랑스런(?) 무선계측기들은 IP가 192로 시작하고... 우리 회사의 PC들은 고정 IP를 사용하는데.. 211로 시작한다. 따라서, 무선계측기를 테스트하려고 하면, 항상 IP를 바꿔주는 노가다가 필요하다. 단지, UDP로 브로드캐스팅을 하기 위해서..-_-

일일이 네트워크 환경 - 설정 - TCP/IP 설정..... 으로 이어지는 귀차니즘을 이기지 못하고, 알아본바.. WMI라는 녀석을 알게 되었다.

WMIWindows Management Instrumentation인데, Windows 기반 운영체제의 management 데이터를 위한 인프라 구조라고 할 수 있다.

쉽게, 예를 들자면.. 내 운영체제의 버전이 얼마인지? 또는 내 컴퓨터의 IP를 변경하고 싶다던지? 내지는 원격으로 컴퓨터에 프로그램을 설치하고 싶다던지... 이 모든 것이 다 WMI로 가능하다는 것이다.

더 상세한 내용은 MSDN을 참고하기로 하고.. 이미 이전에 WMI를 이용해 C#으로 간단히 IP와 서브넷 마스크, 게이트웨이 등을 설정할 수 있는 프로그램을 만든 바가 있어서 C++로도 간단히 1~2시간이면 할 수 있을거라 여겼다.

하지만, Wrapper Class와 많은 예제가 제공되던 C#과는 달리, C++에서는 WMI가 완전히 COM API 레벨에서 해결해야 하고, 내가 원하는 수준의 예제가 없었다. (내가 못 찾은건지도 모른다-ㅁ-)

그러다보니.. 삽질에 삽질을 거듭하다.. 결국 야근까지 하게되며.. 무려 6시간을 잡고 헤맸다.

잡설이 길었는데.. WMI를 이용해 Local PC의 IP를 바꾸는 내용으로 돌아와서...

구현 환경은 Windows XP Professional, Microsoft Visual Studio 6.0 SP6이다.
내 기억이 맞다면, Microsoft Visual Studio 6.0에서 WMI를 구현하기 위해서는 아마도 Microsoft Platform SDK 또는 WMI SDK를 설치해야 하는 것으로 알고 있다.

그리고 코드의 전체적인 흐름은 다음과 같다.

Step 1. COM 라이브러리를 초기화한다.
Step 2. COM Security Level을 설정한다.
Step 3. WMI에 대한 초기 Locator를 획득한다.
Step 4. WMI에 접근한다.
Step 5. Proxy의 Security Level을 설정한다.


여기까지는 사실, WMI를 프로그래밍 하기 위한 준비 단계라 할 수 있다. 이 다음부터는, IP와 서브넷마스크를 설정하기 위한 단계이다.

Step 6. ExecQuery()를 이용해, Class Object Enum을 가져온다.

hr = pServices->ExecQuery(
bstr_t("WQL"),
bstr_t("SELECT * FROM Win32_NetworkAdapterConfiguration"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnum
);

쉽게 설명하면, Win32_NetworkAdapterConfiguration란 클래스로부터 Network Adpater들의 Enum을 가져온다고 볼 수 있다. WMI의 Win32 Class에 대한 보다 상세한 정보는 http://msdn2.microsoft.com/en-us/library/aa394084.aspx 를 참조하기 바란다.

Step 7. Step 6에서 획득한 Network Adapter들 중에서 IPEnabled(IP 변경 가능 속성)이 TRUE인 녀석의 Index를 찾는다.

Step 8. IP와 서브넷마스크의 값을 설정한다.

Step 9. Object Path를 설정한다.
여기서 Object Path에 대해서 설명하자면.. IP와 서브넷을 변경하기 위해 Win32_NetworkAdapterConfiguration 클래스에서 제공하는 MethodEnableStatic이다. 이 Method의 타겟이 바로 Object Path라고 할 수 있다. (EnableStatic의 보다 자세한 내용은 http://msdn2.microsoft.com/en-us/library/aa390383.aspx 를 참조하기 바람)

이 부분이 필요한 이유는 비록 내 PC에서 네트워크 환경에서 보이는 로컬 연결 속성이 하나라고 해서, Network Adapter가 1개가 아니고, 로컬 연결 속성으로 사용되지 못하기 때문에, 눈에 보이지 않는 Network Adapter가 다수 존재한다. 따라서, Step 7도 이러한 이유에서 이다.

결국, EnableStatic의 타켓은 Win32_NetWorkAdapterConfiguration.Index이고, Step 7에서는 이 Index를 찾기 위한 단계였다.

Step 10 ~ Step 14는 앞서 설정한 속성들을 Network Adapter에 반영하기 위한 단계들인데, 여기에 등장하는 함수들은 MSDN을 참고하기 바란다.

다음의 코드는 단순히 기능 구현에 중점을 뒀기 때문에, 코드 최적화에 대해서는 전혀 고려하지 않은 막장 코드이므로, 실제 응용을 위해서, 다듬을 필요가 있다. 

소스코드 다운로드: http://www.nohungry.net/Data/IPHandler.zip

크리에이티브 커먼즈 라이센스
Creative Commons License

Trackback

Trackback Address :: http://www.nohungry.net/tt1/trackback/116

Comments

  1. 조해성 2008/03/13 22:40

    좋은 자료 잘 보았습니다.
    다만, 장치의 Index 값을 얻을 때 1을 시작으로 하고 있으나, XP Home Edition 이나 XP Embedded 등의 다른 버전 OS에서는 0부터 시작하는 경우도 있습니다.
    따라서 임의적인 증가 수치를 사용하는 것은 문제 발생의 소지가 있구요. 대신 uint32 Index 멤버를 참조하는 것이 정확합니다.

    perm. |  mod/del. |  reply.
    • NOhungry 2008/03/14 17:36

      그렇군요!

      좋은 지적 감사 드립니다.ㅎ

What's on your mind?

댓글 입력 폼
[로그인][오픈아이디란?]