'분류 전체보기'에 해당되는 글 317건

  1. 2012.01.18 멀티스레드 -> 멀티플렉싱
  2. 2012.01.18 멀티 스레딩 서버 vs 멀티 플렉싱 서버의 장단점 + UDP VS TCP
  3. 2012.01.18 멀티플렉싱(multiplexing)
  4. 2012.01.18 Stream, Dgram 설명
  5. 2012.01.18 Invoke 개념 이해하기
  6. 2012.01.18 TCP/IP Socket with C#
  7. 2012.01.18 델리게이트 선언뒤에 할당
  8. 2012.01.17 Delegate(5) : 람다식
  9. 2012.01.17 C# 람다식
  10. 2012.01.17 람다식(Lambda Expression)
  11. 2012.01.17 매소드를 인수로 넘기기..
  12. 2012.01.17 InvokeRequired
  13. 2012.01.17 InvokeRequired & Cross Thread 처리방법
  14. 2012.01.17 인터페이스를 상속한 자신을 파라미터로 호출한 경우.
  15. 2012.01.17 Interface 변수 상속한 하위 클래스 함수 호출.
  16. 2012.01.17 스레드 콜백함수에 파라미터 보내는 방법 - 스레드, delegate 사용
  17. 2012.01.17 Cross-thread operation not valid
  18. 2012.01.17 Delegate
  19. 2012.01.16 Hashtable, HashSet<T>, Dictionary<TKey, TValue>
  20. 2012.01.16 Generic 컬렉션 - List, Dictionary, Queue, Stack
  21. 2012.01.16 DictionaryEntry 구조체
  22. 2012.01.16 [C#] internal, protected internal
  23. 2012.01.13 C#에서 AxSHDocVw.Dll 활성화 시키기 [출처] C#에서 AxSHDocVw.Dll 활성화 시키기|작성자 신우아빠
  24. 2012.01.13 MSN 메신저 스타일의 Task Bar 점멸 기능 구현
  25. 2012.01.13 포커스를 빼앗지 않는 폼 구현
  26. 2012.01.13 iBatis 장점
  27. 2012.01.13 일반 구성 설정(ASP.NET)
  28. 2012.01.12 Spring .Net 활용 가이드
  29. 2012.01.10 Framework-based Development - 오픈 소스 적용 사례
  30. 2012.01.10 Framework-based Development - 종류
posted by 방랑군 2012. 1. 18. 23:02

select 함수 원형

#include <sys/types.h> <sys/time.h> <unistd.h>

int select(int n, fd_set *readfd, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

 

어느 교실에 학생들이 열명이 있다.

 

선생님은 한 분이신데 수업시간 내내 각각의 학생이 선생님께 질문을 한다. 그래서 이 학교에서는 어쩔 수 없이 학생 한명당 교사 한 명을 두게 하였다.  따라서 현재 교사가 총 열 명이다. 한 학생이 전학을 오면 교사도 한 명 늘리고, 두 명의 학생이 전학을 오면 교사도 두 명을 더 늘린다. 전학을 온 녀석들도 쉴새없이 질문을 한다.

 

'학생 - 클라이언트', '선생님 - 서버' 라고 생각해 보면, 이 반의 운영 방식은 멀티 프로세스(멀티 스레드) 기반으로 돌아가고 있다는 것을 알 수 있다.

 

시간이 지남에 따라 학생들의 궁금점은 해소되었고, 질문의 수가 갑자기 줄어들어 버렸다. 한 시간에 학생 한 명당 질문 수가 많아야 세 번을 넘지 않는다. 그래서 학교측에서는 그 정도면 한 명의 교사가 충분히 담당을 할 수 있다고 생각을 하고, 효율적인 교사의 활용을 위해서 한 명의 교사만 그 반에 할당하기로 결정하였다.

 

이제 학생들은 질문을 하기 위해서 손을 들어야 하고, 교사는 손을 든 학생이 있는지 없는지 확인하다가 손을 든 학생이 있다면 질문을 받게 될 것이다.

 

이러한 경우의 운영 방식은 'I/O 멀티플렉싱' 방법이다. 서버는 주기적으로 데이터를 전송해오는 클라이언트가 있는지 확인을 하다가, 발견한 경우에, 그 클라이언트로부터 데이터를 수신한다.

 

비교해 보면, 멀티 프로세스 기반 서버 모델이 적합한 경우와 I/O 멀티플렉싱 기반 서버 모델이 적합한 경우가 다르다는 것을 짐작할 수 있을 것이다.

'강좌 > C#' 카테고리의 다른 글

Thread vs ThreadPool  (0) 2012.01.18
마샬링 (Marshaling)  (0) 2012.01.18
멀티 스레딩 서버 vs 멀티 플렉싱 서버의 장단점 + UDP VS TCP  (0) 2012.01.18
멀티플렉싱(multiplexing)  (0) 2012.01.18
Stream, Dgram 설명  (0) 2012.01.18
posted by 방랑군 2012. 1. 18. 23:01

있을만한데 질의 게시판에 없네요..;;

 

스레드를 많이 사용하면 단점이 무엇일까요..;;

 

 

멀티 플렉싱 방식인 Select는 헤더함수를 보니까 fd_array가 64개로 잡혀있어서 소켓을 64개까지밖에 컨트롤을 못하더군요..

 

실제로 테스트해보니까 64개 이후에는 Select처리가 되지 않더군요..;;

 

실제 스레딩과 플렉싱 서버의 장단점이 무엇이 있을까요.. 음.. 많은 답변 부탁드립니다.

 

 

 

+

UDP와 TCP통신을 자주 사용하게 되는데요,

실제 UDP같은 경우는 데이타 손실이 일어나도 되는 통신에 사용되고

TCP같은 경우 데이터 손실이 없어야 할 압축파일 같은 파일 전달시 사용되게 됩니다.


TCP의 경우, 3 way hand shaking이 일어나기 때문에 손실이 발생하지 않지요.

 

속도의 차이다라는 부분은 접근과 종료할 경우 3way hand shaking이 일어나는 부분에서 시간이 걸리기 때문이고

오랜시간 통신을 할경우 TCP와 UDP의 속도차이는 없다고 알고있습니다.

 

여기서 질문인데요. 속도의 차이도 크지 않고 데이터의 전달도 확실치 않은 UDP 통신은 무엇을 전달할 때사용되나요..?

딱히 UDP 통신의 매력을 모르겠습니다. 반드시 UDP로 만들어야하는 프로그램들이 존재하나요?

이 글에 답변 등록하기
[채택답변] re:ApiClasser
0
2011-07-14 오후 11:59:52
 질문자 인사 : 친절한 답변 감사드려요~^^
  진정한 (omlnet)  진정한님께 메시지 보내기진정한님을 내 주소록에 추가합니다.진정한님의 개인게시판 가기 번호: 863758  

스레드 관련 해서는 처리 구조가 어떠한가에 따라 달리 설명 될수 있는 부분입니다 .

 

일반적으로 스레드가 많다면 그많큼 관리 및 프로그램 하기가 어려워 지죠..

 

그렇다고 해도 스레드를 많이 사용한다고 성능 향상이 되는것도 아닙니다 .^^

 

적절 하게 사용하는게 좋습니다.

 

---------------------

 

UDP 관련해서 현재 현장에서도 사용하고 있기도 합니다 .^^

 

1:1 통신 중이면 TCP 와 UDP 통신에는 아무런 차이를 느끼지 못합니다. ^^

 

하지만 n:n 통신중에 사용한다면 TCP 같은 경우는 병목 현상이 발생합니다. ^^

 

그럼으로 느려지고 패킷 손실 부분을 채우기 위해서 재 전송 하는 부분도 동작 하게 되는거죠.

 

그만큼 이 느려 집니다.

 

하지만 UDP 는 다릅니다. 느려지면 그대도 날려 버리고 다음 메시지를 받으면 됩니다.

 

UDP 경우 하드웨어 제어에 사용됩니다.

'강좌 > C#' 카테고리의 다른 글

마샬링 (Marshaling)  (0) 2012.01.18
멀티스레드 -> 멀티플렉싱  (0) 2012.01.18
멀티플렉싱(multiplexing)  (0) 2012.01.18
Stream, Dgram 설명  (0) 2012.01.18
Invoke 개념 이해하기  (0) 2012.01.18
posted by 방랑군 2012. 1. 18. 22:59
멀티플렉싱은 하나의 전송로를 여러사용자가 동시에 사용해서 효율성을 극대화 하는 것이다. 아날로그 신호들은 보통, 반송파 대역폭이 다른 주파수 대역의 서브 채널들로 나뉘어지는 FDM을 사용하여 다중화되며, 각각은 하나의 신호를 동시에 병렬로 전송한다.

디지털 신호들은 보통, 서로 교호하는 시간 슬롯 동안 다중 신호들이 같은 채널을 통해 전송되는 TDM을 사용하여 다중화된다. 일부 광섬유 네트워크에서, DWDM을 사용하여 다중화된 신호 내에 별개의 빛 파장으로서 다중 신호들이 함께 전송된다.




IO멀티플렉싱이란?
 - 클라이언트와 입/출력하는 프로세스를 하나로 묶어버리는 형식 
 - 프로세스가 고속의 전송로에 해당




멀티 프로세스와 멀티플렉싱 비교
  1. 멀티 프로세스 기반의 서버 
    - 클라이언트와 서버간의 송수신 데이터의 용량이 큰 경우 적합
    - 송수신이 쉬지않고 연속적으로 발생하는 경우 적합 

  2. 멀티플렉싱 기반의 서버 
    - 클라이언트와 서버간의 송수신 데이터 용량이 작은 경우 적합 
    - 송수신이 연속적이지 않은 경우에 적합 
    - 멀티 프로세스 기반에 비해 많은 수의 클라이언트 처리에 적합 

'강좌 > C#' 카테고리의 다른 글

멀티스레드 -> 멀티플렉싱  (0) 2012.01.18
멀티 스레딩 서버 vs 멀티 플렉싱 서버의 장단점 + UDP VS TCP  (0) 2012.01.18
Stream, Dgram 설명  (0) 2012.01.18
Invoke 개념 이해하기  (0) 2012.01.18
TCP/IP Socket with C#  (0) 2012.01.18
posted by 방랑군 2012. 1. 18. 22:58

Socket Programming

-------
소켓
-------
소 켓 이란? 두 프로그램이 네트워크를 통해 서로 통신을 수행 할 수 있도록 양쪽에 생성 되는 링크의 단자이다. 두 소켓이 연결되면 서로 다른 프로세스끼리(프로세스가 원격에 있든지 로컬에 있든지 상관 없다.) 데이터를 전달 할 수 있다. 결국 소켓이 구현됨으로써 네트워크 및 전송 계층의 캡슐화가 가능해 졌다.

소켓은 원래 캘리포니아 버클리 대학 분교에서 UNIX 용으로 개발 되었으며 유닉스에서의 입출력 메소드의 표준인 개방/읽기/쓰기/닫기 메커니즘을 따른다.

-------
소켓 형식
-------
1.        스트림 소켓
스 트림소켓은 양방향으로 바이트 스트림을 전송 할 수 있는 연결 지향형 소켓으로 양쪽 어플리케이션이 모두 데이터를 주고 받을 수 있다는 것을 의미 한다. 스트림소켓은 오류수정, 전송처리, 흐름제어 등을 보장 해 주며 송신된 순서에 따른 중복되지 않은 데이터를 수신 하게 된다. 이 소켓은 각 메시지를 보내기 위해 별도의 연결을 맺는 행위를 하므로 약간의 오버헤드가 존재 한다. 그러므로 소량의 데이터 보다는 대량의 데이터를 보내는 경우에 적당 하다. 스트림소켓은 이러한 품질의 통신을 수행 하기 위하여 TCP 프로토콜을 사용 한다.

2.        데이터그램 소켓
명시적으로 연결을 맺지 않으므로 비 연결형 소켓이라고 한다. 메시지는 대상 소켓으로 전송되며 대상 소켓은 메시지를 적절히 수신 한다. 스트림 소켓을 사용하는 것이 데이터그램 소켓을 사용 하는 것 보다 더 신뢰성이 높은 방법이지만 연결을 수립하는데 드는 오버헤드는 무시 할 수 없다. 데이터그램 소켓을 사용하려면 클라이언트에서 서버로 데이터를 전송 할 때 UDP를 사용 한다. 이 프로토콜에서는 메시지의 크기에 약간의 제한이 있으며 메시지의 확실 한 전달 역시 보장 하지 않으며 통신 중 데이터를 잃어 버리더라도 오류를 되돌리지 않는다.
3.        Raw 소켓
Raw 소켓은 패킷을 가져오면 TCP/IP 스택상의 TCP, UDP 계층을 우회하여 바로 애플리케이션으로 송신하는 소켓이다. 이런 소켓에서 패킷은 TCP/IP 필터를 통해 전달 되지 않으므로 원형 그대로의 패킷을 볼 수 있다. 이는 모든 데이터를 적절히 처리하거나 헤더를 제거하고 이를 파싱 하는 과정은 모두 수신 애플리케이션에서 담당해야 하는 것이다. 실제 Raw 소켓을 이용하여 프로그래밍을 하는 일은 거의 드물며 만약 시스템 소프트웨어나 패킷을 분석하는 프로그램을 개발 시에는 필요 할 수도 있다. 즉 Raw Socket은 저수준 프로토콜 애플리케이션을 작성 할 때 주로 사용 된다고 보면 된다.


---------
포트
---------
여 러 개의 애플리케이션들이 동시에 통신을 수행하기 위하여 포트가 정의 되는데 기본적으로 포트는 IP 주소 표기를 확장 하는 개념 이다. 네트워크에서 패킷을 수신하는 애플리케이션이 동시에 실행 되고 있는 컴퓨터로 연결을 맺을 때 송신자가 알고 있는 수신 애플리케이션의 고유 포트 번호를 이용하여 대상 프로세스를 식별 하는 것이다.

소켓 번호는 컴퓨터의 IP 주소와 TCP 애플리케이션에서 사용하는 포트 번호로 이루어져 있다. IP주소는 인터넷 상에서 유일 하고 포트 번호는 개별 컴퓨터에서 유일하기 때문에 전체 인터넷에서 소켓 번호 또한 유일 한 것이다.

-----------------------
.NET 에서 소켓 다루기
-----------------------
System.Net.Sockets 네임스페이스의 클래스들은 .NET에서 지원하는 소켓들을 제공 한다.

System.Net.Sockets.MulticastOption : IP 멀티캐스트 그룹에 참여 하거나 탈퇴하기 위한 IP 주소 값을 설정 한다.

System.Net.Sockets.NetworkStream : 데이터를 주고 받는 하위 스트림을 구현 한다. 이는 TCP/IP 통신 채널에 대한 연결을 나타내는 고수준 추상형이다. 이 클래스를 이용하여 네크워크 소켓을 통해 데이터를 주고 받을 수 있다. NetworkStream은 버퍼 기능이 지원되지 않으므로 BufferedStream을 중간 저장 매체로 함께 사용 하기도 한다.
System.Net.Sockets.TcpClient : Socket 클래스를 기반으로 하여 작성 되었으며 고수준 TCP 서비스를 제공하여 준다.

System.Net.Sockets.TcpListener : Socket 클래스를 기반으로 작성 되었으며 서버 애플리케이션에서 사용 한다.이 클래스는 들어오는 클라이언트의 연결을 리스닝 하며 애플리케이션에게 연결된 요청을 알려 준다.

System.Net.Sockets.UdpClient : UDP 서비스를 구현하기 위한 클래스

System.Net.Sockets.SocketException : 소켓에서 오류가 존재할 때 발생하는 예외

System.Net.Sockets.Socket : 소켓 애플리케이션의 기본 기능을 제공 한다.


------------------------
System.Net.Sockets.Socket
------------------------
Socket 클래스는 네트워크 프로그래밍에서 중요한 역할을 담당 하는데 클라이언트와 서버 사이의 모든 동작을 수행 한다.윈도우 소켓 API의 해당 하는 메소드로 매핑 된다.

아래는 몇 가지 속성 이다.

AddressFamily : 소켓의 주소 계열을 획득, Socket.AddressFamily 열거형의 값 이다.
Available : 읽을 수 있는 데이터 량을 Return
Blocking : 소켓이 블로킹 모드 인지 확인
Connected : 소켓이 원격 호스트에 연결 되어 있는 지
LocalEndPoint : 로컬 종점을 돌려 줌
ProtocolType : 소켓의 프로토콜 형식을 돌려 준다.
SocketType : 소켓의 타입을 돌려 준다.

아래는 몇 가지 메소드 이다.
Accept() : 들어오는 연결을 다루기 위한 새로운 소켓을 생성
Bind() : 들어오는 연결을 리스닝 하기 위하여 소켓을 로컬종점으로 연결
Close() : 소켓을 종료
Connect() : 원격 호스트에 연결을 맺는다.
Listen() : 리스닝 상탸로 만든다, 이것은 서버 소켓에서만 사용 된다.
Receive() : 연결된 소켓으로부터 데이터를 수신 한다.
Send() : 연결된 소켓으로 데이터를 송신 한다.
Shutdown() : 소켓에 대한 연결을 비 활성화 한다.

----------------------

Server Socket 작성 하기

----------------------

1. 소스 코드를 작성 하기 전에 우선 전체적인 코드를 개략적으로 이해 하기로 하자 .

우선 서버의 입장에서 로컬 종점을 생성 한다 . 리스닝을 위한 소켓을 열기 전에 로컬 종점 주소를 작성 해야 하며 서비스에 대한 종점을 생성 하기 위해 호스트의 IP 주소와 서비스의 포트 번호를 조합하여 TCP/IP 서비스의 고유 주소를 정의 한다 . Dns 클래스는 로컬네트워크 기기에서 지원하는 네트워크 주소에 대한 정보를 되돌려 주는 메소드를 제공 한다 . 로컬 네트워크 기기가 하나 이상의 네트워크 주소를 가지거나 로컬 시스템이 하나 이상의 네트워크 기기를 지원 한다면 Dns 클래스는 모든 네트워크 주소에 대한 정보를 되돌려 주며 애플리케이션은 아 배열에서 적절한 주소를 선택해야 한다 .

// 소켓에 사용할 종점을 설정

IPHostEntry ipHost = DNS.Resolve(“localhost”);

IPAddress ipAddr = ipHost.AddressList[0];

IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 11000);

2. 다음은 Socket 클래스의 새로운 인스턴스를 이용하여 스트림소켓을 생성 한다 . 이미 리스닝에 사용 할 로컬 종점을 작성 하였으므로 바로 소켓을 생성 할 수 있다 .

//TCP/IP 소켓을 생성

Socket sListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

AddressFamily 열거형은 Socket 인스턴스가 주소를 해석 하기 위해 사용하는 주소 스키마를 나타낸다 . 몇 가지 값들은 아래와 같다 .

AddressFamily.InterNetwork : IP 버전 4 주소

AddressFamily.InterNetwork V6: IP 버전 6 주소

AddressFamily.Ipx : IPX/SPX 주소

AddressFamily.NetBios: NetBios 주소

SocketType 은 TCP 와 UDP 소켓을 구분 하는데 이용 가능한 값은 아래와 같다 .

SocketType.Dgram : 데이터그램을 지원 , Dgram 은 Udp ProtocolType 과 AddressFamily.InterNetwork 와 함께 사용되어야 한다 .

SocketType.Raw : Raw Socket

SocketType.Stream : 스트림 소켓을 지원 , Stream 은 Tcp ProtocolType 과 AddressFamily.InterNetwork 와 함께 사용되어야 한다 .

세번째 파라미터 , 네번째 파라미터는 소켓에 필요한 프로토콜 형식을 정의 한다 . 그 값은 아래와 같다 .

Raw : Raw 패킷 프로토콜

Tcp : TCP

Udp : UDP

Ip : Internet Protocol

•  다음 단계는 Bind() 메소드를 사용하여 소켓에 이름을 부여 한다 . 생성자를 이용하여 소켓을 개방하면 소켓에 아무런 이름도 할강되어 있지 않다 . 즉 Bind() 메소드를 통해 소켓을 Local 종점에 연결 시키는 것이다 .

try {

sListener.Bind(ipEndPoint);

•  이제 소켓이 생성 되었고 , 이름이 바인딩 되었으므로 Listen() 을 사용하여 들어오는 연결에 대해 리스닝을 수행 할 수 있다 . 이때 파라미터에는 큐에 대기중인 연결의 최대 개수를 지정 한다 .

sListener.Listen(10);

•  위에서 리스닝을 했으므로 이젠 Accept() 를 이용하여 클라이언트의 연결을 수신하여 클라이언트와 서버의 이름 연결을 완료 한다 . Accept() 메소드는 대기중인 요청 큐로 부터 먼저 들어온 연결을 가지고 와 이를 처리할 새로운 소켓을 생성 한다 . 새로운 소켓이 생성 되었다고 하더라도 원래 소켓은 계속 리스닝을 수행 하므로 복수의 클라이언트 요청을 처리하기 위해서는 멀티쓰레드를 사용 한다 .

while(true) {

...

Socket handler = sListener.Accept();

•  Send(), Receive() 메소드를 이용하여 데이터를 보내고 , 받는다 .

string data = null;

while(true) {

byte[] bytes = new byte[1024];

// 클라이언트로부터 수신된 데이터

int byteRes = handler.Receive(byte);

// 바이트를 문자열로 변환

data += Encoding.Default.GetString(bytes, 0, byteRec);

// 메시지의 끝인지 확인

if (data.IndexOf(“”) > -1) {

break;

}

}

•  루프를 빠져 나온 후 클라이언트에게 응답을 돌려주기 위해 새로운 바이트 배열을 준비 한다 . 변환을 마친 후 Send() 메소드를 이용하여 데이터를 보내자

string theReply = “Thank you for those ” + data.Length.ToString() + “ characters …”;

byte[] msg = Encoding.Default.GetBytes(theReply);

handler.Send();

•  서버와 클라이언트의 데이터 교환이 끝나면 Close() 를 이용하여 소켓을 종료 한다 . 항상 Close() 를 하기 전에 Shutdown() 을 이용하여 남아 있는 데이터를 확실히 제거 하자 . 각 소켓 인스턴스 마다 Close() 메소드를 호출 해야 한다 .

handler.Shutdown(SocketShutdown.Both);

handler.Close();

SocketShutdown 값은 열거형으로 아래와 같은 값을 취한다 .

Both : 송 . 수신용 소켓 모두

Receive : 수신용

Send : 송신용 소켓

소스 코드는 아래와 같다 .[SocketServer.cs]

using System;

using System.Net.Sockets;

using System.Net;

using System.Text;

public class SocketServer

{

public static void Main (string [] args)

{

// establish the local end point for the socket

IPHostEntry ipHost = Dns.Resolve("localhost");

IPAddress ipAddr = ipHost.AddressList[0];

IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 11000);

// create a Tcp/Ip Socket

Socket sListener = new Socket(AddressFamily.InterNetwork,

SocketType.Stream, ProtocolType.Tcp);

// bind the socket to the local endpoint and

// listen to the incoming sockets

try

{

sListener.Bind(ipEndPoint);

sListener.Listen(10);

// Start listening for connections

while (true)

{

Console.WriteLine("Waiting for a connection on port {0}",ipEndPoint);

// program is suspended while waiting for an incoming connection

Socket handler = sListener.Accept();

string data = null;

// we got the client attempting to connect

while(true)

{

byte[] bytes = new byte[1024];

int bytesRec = handler.Receive(bytes);

data += Encoding.ASCII.GetString(bytes,0,bytesRec);

if (data.IndexOf("") > -1)

{

break;

}

}

// show the data on the console

Console.WriteLine("Text Received: {0}",data);

string theReply = "Thank you for those " + data.Length.ToString()

+ " characters...";

byte[] msg = Encoding.ASCII.GetBytes(theReply);

handler.Send(msg);

handler.Shutdown(SocketShutdown.Both);

handler.Close();

}

}

catch(Exception e)

{

Console.WriteLine(e.ToString());

}

} // end of Main

}

----------------------

Client Socket 작성 하기

----------------------

1. 클라이언트 코드가 서버와 다른 점은 Connect() 메소드 부분이다 . 이것은 클라이언트가 원격의 서버에 연결 하고자 할 때 사용하는 메소드 이다 . 사용하기 위해서는 우선 원격 종점을 설정 해야 한다 .

// 소켓에 사용할 원격 종점을 설정

IPHostEntry ipHost = DNS.Resolve(“127.0.0.1”);

IPAddress ipAddr = ipHost.AddressList[0];

IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 11000);

Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

sender.Connect(ipEndPoint);

Connect() 메소드로 소켓을 종점 파라미터로 지정된 원격 호스트와 연결을 맺는다 . 일단 연결이 이루어 지면 데이터를 보내고 받을 수 있다 .

string theMessage = “This is a test”;

byte[] msg = Encoding.Default.GetBytes(theMessage+””);

// 소켓을 이용하여 데이터를 보냄

int bytesSend = sender.Send(msg);

// 원격으로부터 ( 서버 ) 응답을 수신

int bytesRec = sender.Receive(bytes);

이젠 마지막으로 Shutdown() 을 호출하여 소켓을 해제 한다 . 그리고 Close() 하자

sender.Shutdown(SocketShutdown.Both);

sender.Close();

[ 아래는 SocketClient.cs 의 소스 파일 이다 .]

using System;

using System.Net.Sockets;

using System.Net;

using System.Text;

public class SocketClient

{

// If you specify any text as a command-line argument, it will be sent to the server.

// e.g. SocketClient "Send this text" will send that string

// If no command-line arguments are specified, a default string is sent

public static void Main (string [] args)

{

// data buffer for incoming data

byte[] bytes = new byte[1024];

// connect to a Remote device

try

{

// Establish the remote end point for the socket

IPHostEntry ipHost = Dns.Resolve("127.0.0.1");

IPAddress ipAddr = ipHost.AddressList[0];

IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 11000);

Socket sender = new Socket(AddressFamily.InterNetwork,

SocketType.Stream, ProtocolType.Tcp);

// Connect the socket to the remote endpoint. Catch any errors

sender.Connect(ipEndPoint);

Console.WriteLine("Socket connected to {0}",

sender.RemoteEndPoint.ToString());

//string theMessage=Console.ReadLine();

string theMessage;

if (args.Length==0)

theMessage = "This is a test";

else

theMessage = args[0];

byte[] msg = Encoding.ASCII.GetBytes(theMessage+"");

// Send the data through the socket

int bytesSent = sender.Send(msg);

// Receive the response from the remote device

int bytesRec = sender.Receive(bytes);

Console.WriteLine("The Server says : {0}",

Encoding.ASCII.GetString(bytes,0, bytesRec));

// Release the socket

sender.Shutdown(SocketShutdown.Both);

sender.Close();

}

catch(Exception e)

{

Console.WriteLine("Exception: {0}", e.ToString());

}

}

}

posted by 방랑군 2012. 1. 18. 22:58
System.Windows.Forms.Control 클래스는 Invoke() 메서드를 포함하고 있다. 닷넷의 GUI 컴포넌트들은 Control 클래스를 상속받아 구현되었으므로 [Windows 응용 프로그램] 템플릿으로 작성된 윈폼 멀티 스레드 프로그램에서 인보크를 사용할 수 있다. 스레드에서 Invoke()는 컨트롤 내부에 있는 특정 구문을 실행할 때 사용된다.

Snap1.png

'강좌 > C#' 카테고리의 다른 글

멀티플렉싱(multiplexing)  (0) 2012.01.18
Stream, Dgram 설명  (0) 2012.01.18
TCP/IP Socket with C#  (0) 2012.01.18
델리게이트 선언뒤에 할당  (0) 2012.01.18
Delegate(5) : 람다식  (0) 2012.01.17
posted by 방랑군 2012. 1. 18. 22:55
  1. TCP/IP 소켓 프로그래밍 with C#
    1. 기본 용어 해석
    2. 중요 클래스
      1. IPAddress
      2. IPHostEntry
      3. DNS
      4. TcpClient
      5. EndPoint
      6. IPEndPoint
      7. TcpListener
      8. NetworkStream
      9. UdpClient
      10. .Net Socket
      11. SocketException
    3. .NET 입출력 클래스
    4. 넌 블로킹 입출력
    5. 멀티 플렉싱
    6. 스레드
    7. 비동기 입출력
    8. 다중 수신자
      1. 브로드 캐스트
      2. 멀티 캐스트
    9. 연결 종료
    10. 버퍼 교착상태
    11. TCP 소켓 생존 주기
    12. TCP 연결 종료
    13. 디 멀티 플렉스
  2. 실무자를 위한 C# 네트워크 프로그래밍
    1. C# 프로그램 컴파일및 실행
    2. C# 프로그램 디버깅
    3. 네트워크 트래픽 관리
      1. WinPcap 드라이버
      2. WinDump
      3. Analyzer
    4. 네트워크 패킷 분석
      1. 이더넷 계층
      2. IP 계층
      3. TCP 계층
      4. UDP 계층
    5. IP 주소 정보 찾기
    6. 네트워크 상에서의 데이터 이동
    7. 프로세스
    8. 스레드
    9. 스레드 풀

TCP/IP 소켓 프로그래밍 with C#

기본 용어 해석

컴퓨터 네트워크는 통신 채널로 연결된 많은 기기들로 이루어진다. 이러한 기기들을 호스트(hosts)와 라우터(routers)라 한다.
프로토콜(protocol)이란 통신하는 프로그램 간에 주고받는 패킷의 내용과 방법에 대한 규약.
호스트 간에만 데이터를 전달하는 IP와는 달리 한 프로그램으로부터 다른 프로그램까지의 데이터 전송 전 과정을 관리하므로 TCP와 UDP는 단말간 전송 프로토콜(end-to-end transport protocol)이라고 한다.
TCP는 신뢰 가능한 바이트 스트림 채널(reliable byte-stream channel)을 제공하고 연결 기반(connection-oriented) 프로토콜이기 때문에 통신하는 두 컴퓨터의 TCP 연결이 확립되어야 한다. 이러한 연결의 확립은 통신하는 두 컴퓨터의 TCP 프로토콜 구현간에 핸드쉐이크 메시지(handshake message)를 주고 받는 것으로 이루어진다.

UDP는 이와 반대로 IP에서 발생한 문제들을 복구하려 하지 않는다. 오히려 단순한 IP의 최선형(best-effort) 데이터그램 서비스를 확장하여 어플리케이션 프로그램 간에서도 이 서비스를 이용할 수 있도록 만든다.

이름 해석 서비스(name-resolution service)는 여러 소스로부터 정보를 취합한다. 이러한 소스들 중에는 DNS(domain name system)와 로컬 설정 데이터베이스(local configuration database)가 대표적이다. DNS는 도메인 네임(domain name)을 인터넷 주소와 기타 다른 정보로 매핑하는 분산 데이터 베이스. 로컬 설정 데이터베이스는 일반적으로 운영체제 자체 시스템으로 도메인 네임과 인터넷 주소의 매핑 작업을 로컬에서 수행한다. 윈도우는 hosts텍스트 파일에 unix기반 시스템은 /etc/hosts 파일이 존재한다.

URL(universal resource locator)은 이름을 해석하여 인터넷 주소를 확인한다.
포트(port)는 IANA(Internet Assigned Number Authority)가 잘 알려진 포트(well-known port)를 지정한다.

디렉터리 서비스(directory service)는 클라이언트가 디렉터리 구조를 통해 서버가 제공하는 서비스와 서버 내부의 장소들을 열람하는 서비스.

소켓(socket)란 어플리케이션이 데이터를 주고 받을 수 있는 하나의 추상적인 통로이다. 각 소켓은 프로토콜 집합(protocol suite)과 이 집합내의 여러개의 서로 다른 프로토콜 스택(stack)을 참조하게 된다. 오늘날 TCP/IP에서 사용하는 주요 소켓은 스트림소켓(stream socket)과 데이터그램 소켓(datagram socket)이다. 스트림 소켓은 IP를 근본으로 단말간 프로토콜(end-to-end protocol)로 사용하기 때문에 안정적인 바이트 스트림 서비스를 제공한다. 데이터 그램 소켓은 역시 IP를 근본으로 한 UDP소켓을 단말간 프로토콜로 이용하기 때문에 어플리케이션에서 단일 메시지당 65,500 바이트 길이의 메시지까지 전송할 수 있는 최선형(best-effort) 데이터그램 서비스를 제공한다.

빅엔디언(big-endian)은 최상위 바이트가 가장 먼저 전송되고 최하위 바이트가 가장 나중에 전송되는 순서이다.
대부분의 네트워크 프로토콜에 쓰이는데 네트워크 바이트 오더(network byte order)라고도 한다. 그외 자바(java)와 같은 언어에도 쓰인다.
리틀엔디언(little-endian)은 최상위 바이트가 가장 나중에 전송되고 최하위 바이트가 가장 먼저 전송되는 순서이다.
윈도우 운영체제의 주요 아키텍처인 인텔, AMD, 알파 기반의 컴퓨터는 리틀 엔디언을 사용한다.

중요 클래스

IPAddress

// IP네트워크가 갖는 하나의 인터페이스에 대한 주소를 반환
public IPAddress(long address);

public static short HostToNetworkOrder?(short);
public static int HostToNetworkOrder?(int);
public static long HostToNetworkOrder?(long);
public static short NetworkToHostOrder?(short);
public static int NetworkToHostOrder?(int);
public static long NetworkToHostOrder?(long);

public static IPAddress Parse(string address);
// 도트 표기로 된 스트링 형태의 IP주소를 IPAddress인스턴스로 변환

public static readonly IPAddress Any; // 0.0.0.0
public static readonly IPAddress Broadcast; // 255.255.255.255
public static readonly IPAddress Loopback; // 127.0.0.1

IPHostEntry?

// Dns 클래스의 GetHostByName?(), GetHostByAddress?(), GetHostEntry?()로 반환되는 컨테이너 클래스
public string HostName? { get; set; }
public string[] Aliases { get; set; }
public IPAddress[] AddressList? { get; set; }

DNS

// DNS로 부터 호스트명 또는 IP주소와 관련된 정보를 수집할 수 있는 여러 정적 메소드
public static IPHostEntry
? GetHostByAddress?(IPAddress address);
public static IPHostEntry? GetHostByAddress?(string address);
public static IPHostEntry? GetHostByName?(string hostname);
public static string GetHostName?();
public static IPHostEntry? GetHostEntry?(IPAddress address);
public static IPHostEntry? GetHostEntry?(string hostname);

TcpClient?

// 소켓 하위클래스, TCP연결을 통해 다른 호스트와 접속하고, 데이터를 전송하고 수신하는데 사용되는 여러 메소드를 제공
public TcpClient?();
public TcpClient?(IPEndPoint? localEP);
public TcpClient?(string hostname, int port);

public void Close();
public void Connect(IPEndPoint? endpoint);
public void Connect(IPAddress address, int port);
public void Connect(string hostname, int port);
public NetworkStream? GetStream?();

protected Socket Client { get; set; }

public 속성으로 설정 가능한 소켓 옵션
LingerState? // 소켓의 지연시간 설정
NoDelay? // 전송이나 수신 버퍼가 꽉 차지 않을 경우 지연을 방지하는 변수값을 설정
ReceiveBufferSize? // 수신 버퍼의 크기 설정
ReceiveTimeout? // 읽기 작업시 데이터를 수신하기까지 기다리는 시간 설정
SendBufferSize? // 전송 버퍼의 크기 설정
SendTimeout? // 쓰기 작업 시작시 데이터를 송신완료까지 기다리는 시간 설정

EndPoint?

// IPEndPoint?의 추상 클래스

IPEndPoint?

// IP주소와 포트번호 형태로 TCP/IP 엔드 포인트를 나타낸다.
public IPEndPoint?(long address, int port);
public IPEndPoint?(IPAddress address, int port);

public IPAddress Address { get; set; }
public int Port { get; set; }

TcpListener?

// TCP네트워크 클라이언트로부터 들어오는 연결 요청을 대기한다.
public TcpListener?(IPEndPoint? localEP);
public TCpListener?(IPAddress address, int port);

public Socket AcceptSocket?();
public TcpClient? AccepTcpClient?();
public bool Pending(); // 수락 가능한 연결 요청 있을 경우 true
public void Start();
public void Stop();

public EndPoint? LocalEndpoint? { get; }
protected Socket Server { get; }

NetworkStream?

// Stream 클래스의 하위 클래스
public virtual void Close();
public abstract int Read(byte[] buffer, int offset, int length);
public abstract void Write(byte[] buffer, int offset, int length);

public virtual bool DataAvailable? { get; } // 스트림으로부터 읽어들일 데이터가 존재하는 경우 true

UdpClient?

public UdpClient?();
public UdpClient?(int port);
public UdpClient?(IPEndPoint? localEP);
public UdpClient?(string hostname, int port);

public void Close();
public void Connect(IPEndPoint? endpoint);
public void Connect(IPAddress addr, int port);
public void Connect(string hostname, int port);

public byte[] Receive(ref IPEndPoint? remoteEP);

public int Send(byte[] dgram, int length);
public int Send(byte[] dgram, int length, IPEndPoint? endPoint);
public int Send(byte[] dgram, int length, string honstname, int port);

protected Socket Client { get; set; }

.Net Socket

public Socket(AddressFamily?, SocketType?, ProtocolType?);
// TCP 사용시 (Address.Family.InterNetwork?, SocketType?.Stream, ProtocolType?.Tcp)

public Bind(EndPoint? localEP); // 소켓을 결합한다. IPAddress.Any(0,0,0,0)객체와 명시된 포트번호로 구성된 IPEndPoint?인스턴스를 파라미터로 받아서 로컬주소와 포트에 결합한다.
public void Close();
public Connect(EndPoint? remoteEP);
public object GetSocketOption?(SocketOptionLevel?, SocketOptionName?);
public void GetSocketOption?(SocketOptionLevel?, SocketOptionName?, byte[]);
public byte[] GetSocketOption?(SocketOptionLevel?, SocketOptionName?, int);
public void Listen(int backlog); // 연결 요청 큐의 최대 길이
public bool Poll(int microsecond, SelectMode? mode);
// 객체의 상태 확인. SelectMode?.SelectWrite? 쓰기 가능한지 확인, SelectMode?.SelectRead? 읽기 가능한지 확인, SelectMode?.SelectError? 오류 존재 여부 확인

public int Receive(byte[] buffer);
public int Receive(byte[] buffer, SocketFlags? flags);
public int Receive(byte[] buffer, int length, SocketFlags? flags);
public int Receive(byte[] buffer, int offset, int length, SocketFlags? flags);

public int ReceiveFrom?(byte[] buffer, ref EndPoint? remoteEP);
public int ReceiveFrom?(byte[] buffer, SocketFlags? flags, ref EndPoint? remoteEP);
public int ReceiveFrom?(byte[] buffer, int length, SocketFlags? flags, ref EndPoint? remoteEP);
public int ReceiveFrom?(byte[] buffer, int offset, int length, SocketFlags? flags, ref EndPoint? remoteEP);

public static void Select(IList readableList, IList writeableList, IList errorList, int microseconds);

public int Send(byte[] buffer);
public int Send(byte[] buffer, SocketFlags? flags);
public int Send(byte[] buffer, int length, SocketFlags? flags);
public int Send(byte[] buffer, int offset, int length, SocketFlags? flags);

public int SendTo?(byte[] buffer, ref EndPoint? remoteEP);
public int SendTo?(byte[] buffer, SocketFlags? flags, ref EndPoint? remoteEP);
public int SendTo?(byte[] buffer, int length, SocketFlags? flags, ref EndPoint? remoteEP);
public int SendTo?(byte[] buffer, int offset, int length, SocketFlags? flags, ref EndPoint? remoteEP);

public void SetSocketOption?(SocketOptionLevel? optionLevel, SocketOptionName? optionName, byte[] optionValue);
public void SetSocketOption?(SocketOptionLevel? optionLevel, SocketOptionName? optionName, int optionValue);
public void SetSocketOption?(SocketOptionLevel? optionLevel, SocketOptionName? optionName, object optionValue);
/*
SocketOptionLeve? 열거체
옵션이 적용될 계층을 정한다.
IP
Socket
Tcp
Udp

SocketOptionName?
http://dotgnu.org/pnetlib-doc/System/Net/Sockets/SocketOptionName.html

SocketFlags?
DontRoute? 라우팅 테이블을 사용하지 않고 전송한다.
MaxIOVectorLength? 데이터를 전송하고 수신하는데 사용할 WSABUF 구조의 개수에 대한 표준수치를 제공한다.
None
OutOfBand? 대역외(out-of-band) 데이터를 처리한다.
Partial 메시지의 일부를 전송하고 수신할 수 있다.
Peek 현재 수신중인 메시지를 확인한다.
*/

public void Shutedown(SocketShutdown? how);
// 데이터를 전송하고 수신하는 기능을 중단시킨다. SocketShutdown?.Send, SocketShutdown?.Receive, SocketShutdown?.Both

public bool Connected { get; } // 가장 최근 입출력 작업후 객체가 원격 리소스에 연결되어 있는지 나타낸다.
public EndPoint? LocalEndPoint? { get; } // 로컬 엔드 포인트를 가져온다.
public EndPoint? RemoveEndPoint? { get; } // 원격 엔드 포인트를 가져온다.

SocketException?

public override int ErrorCode? { get; } // WinSock? 2.0 에러코드와 일치한다.
public virtual string Message { get; } // 사용자가 읽을 수 있는 오류 메시지

.NET 입출력 클래스

BufferedStream? // 입출력 최적화를 위한 버퍼링을 시행
BinaryReader?/BinaryWriter? // 기본 데이터 타입의 읽기/쓰기를 처리
MemoryStream? // 메모리를 백킹 스토어(backing store)로 하는 스트림을 생성하고, 임시 버퍼와 파일 대용으로 사용할 수 있다.
Stream // 모든 스트림에 대한 기본 추상 클래스
StreamReader?/StreamWriter? // 특정 인코딩을 캐릭터 입출력을 스트림에 대해서 처리
StringReader?/StringWriter? // 특정 인코딩을 캐릭터 입출력을 스트림에 대해서 처리
TextReader?/TextWriter? // 캐릭터 입출력에 대한 기본 추상 클래스로서 StreamReader?/StreamWriter?클래스와 StringReader?/StringWriter?클래스들의 부모클래스이다.

넌 블로킹 입출력

(1) 입출력 상태를 사전에 점검
TcpClient
?클래스로부터 데이터를 읽어들이는 과정에서 NetStream?의 DataAvailable?속성이 true이면 읽을 데이터가 존재하는 것
TcpListener? 클래스로부터 Pending()메쏘드 사용하여 AcceptTcpClient?()메소드 또는 AcceptSocket?() 메소드를 호출하기 전에 연결 요청이 있는지를 점검 있으면 true.
Socket 클래스의 int타입의 Available속성을 통해 읽어들일 데이터의 존재 여부 확인. 네트워크로 부터 수신 했지만, 아직 읽어들이지 않은 데이터량을 바이트 단위의 정수로 기억. 즉 Available 속성이 0보다 클 경우 읽기 동작은 블록을 걸지 않는다.

(2) 블로킹 타임아웃 콜
Socket 클래스의 Poll()메소드. 메소드 인자의 시간 만큼 블록하여 요청을 대기한다.
Socket 클래스의 SetSocketOption?()메소드로 타임아웃(timeout)을 설정한다. TcpClient도? ReceiveTimeout? 속성을 통해 타임아웃을 설정할 수 있다.

(3) 넌 블로킹 소켓
Socket 클래스의 Blocking 속성을 false로 변경한다.

멀티 플렉싱

Socket클래스의 Select()메소드
하나이상의 입출력을 대기하는 소켓 목록을 검색한다.
IList 인터페이스형의 소켓목록중 하나 이상의 입출력이 요청되면, 실행시 소켓 목록은 입출력이 준비된 Socket으로 재구성 된다.

스레드

클라이언트 단위 스레드
스레드 풀

비동기 입출력

(1) 작업이 완료된 시점에서 호출할 콜백(callback)메소드를 지정
(2) 주기적으로 폴링(polling)하면서 메소드가 완료했는지를 확인
(3) 비동기 작업을 완료한 이후 전체가 완료되기까지 블록을 걸어 대기

.NET 프레임워크는 매우 유연한 비동기 API를 제공한다.
프레임워크 라이브러리 뿐 아니라 네트워크 입출력, 스트림 입출력, 파일 입출력, DNS룩업 메소드까지 넌 블로킹 형태로 제공한다.
사용자 메소드를 포함해 어떠한 메소드도 비동기 형태로 재구성할 수 있다. !! 이책의 점위를 넘는다-_-

비동기 입출력은
시작 콜(begin call)과 콜의 결과를 취득하기위한 완료 콜(end call)이 있다.
시작및 완료 동작은 대칭 구조를 이루고 있어 각 시작 메소드는 어디에선가 완료 메소드로 대응해야 한다. 이러한 조치를 취하지 않으면 오랫동안 실행하는 프로그램에서 마무리되지 않는 비동기 콜에 대한 상태 관리가 매우 어려워진다. 메모리 릭이나 CPU 독점과 같은 경우가 생긴다는 이야기..

NetworkStream? 클래스의 비동기 형태 Write, Read
public override IAsynResult? BeginRead?(byte[] buffer, int offset, int size, AsyncCallback? callback, object state);
public override IAsynResult? BeginWrite?(byte[] buffer, int offset, int size, AsyncCallback? callback, object state);

public override int EndRead?(IAsyncResult? asyncResult);
public override void EndWrite?(IAsyncResult? asyncResult);

AsyncCallback? ac = new ASyncCallback?(myMethodToCall?);
...
public static void myMethodToCall?(IAsyncResult? result);
{

// result.AsyncState?는 Begin메소드에서 넘겨준 object형.
// 반드시 Begin과 반대되는 형으로 End해주어야 한다. 예) EndRead?(result); // 인자로 넘어온 result가 들어가야한다. 반환값은 동기함수인 Read()메소드에서 반환하는 값과 같다.
...

}

이런 콜백외의 비동기 콜작업은 효용이 없다.

다중 수신자

일대일(one-to-one) 통신을 유니캐스트(unicast)라 한다.
일대다(one-to-many) 서비스에는 브로드캐스트(braodcast)와 멀티캐스트(multicast) 두가지가 있다.
브로드 캐스트는 모든(로컬) 네트워크 호스트가 동일한 메시지를 수신한다.
멀티 캐스트에서는 메시지가 멀티 캐스트 주소(multicast address)로 전송되고 이 메시지를 수신하고자 하는 호스트만이 네트워크를 통해 이 메시지를 수신하게 된다. 일반적으로 브로드캐스트 혹은 멀티캐스트 동작에는 UDP소켓만이 사용가능하다.

브로드 캐스트

로컬 브로드캐스트(local broadcast)주소 (255.255.255.255)는 동일한 네트워크상의 모든 호스트에 메시지를 전달한다.
이더넷(Ethernet) 네트워크 상의 한 호스트는 동일한 이너넷 상의 모든 호스트에게 메시지를 전달할 수 있지만, 메시지는 라우터에 의해서 전달되지는 않는다. IP주소 또는 직접 브로드 캐스트 주소(Direct boradcast address) 를 명시하는데 이는 해당 네트워크상의 모든 호스트에 메시지를 브로드캐스트하지만, 대부분의 인터넷 라우터는 효율의 이유로 브로드캐스트 메시지를 전송하지 않는다.

사용법은 유니캐스트와 같으며 목적지 주소를 브로드캐스트 주소로 변경하면 된다.

멀티 캐스트

멀티 캐스트는 IP설계시부터 지정된 일전 구간(224.0.0.0 ~ 239.255.255.255)의 멀티캐스트 전용구간이 있다.
유니 캐스트와 차이는 해당 주소가 멀티캐스트용이라는 점과 초기 멀티캐스트 데이터그램에 대해서 TTL(Time to Live)값을 설정해야 한다는 점 뿐이다. TTL값은 데이터가 라우터를 지날때 마다 주로 1씩 감소하며 TTL 값이 0이되면 패킷은 제거된다.

SocketOptionName?.MuilticastTimeToLive?옵션에서 TTL을 설정하며
SocketOptionName?.AddMembership?옵션과 SocketOptionName?.DropMembership?옵션과
UdpClient?의 JoinMulticastGroup?()메소드나 DropMulticastGroup?()메소드로 그룹을 가입하거나 탈퇴하여 멀티캐스트 메시지 수신이 가능하다.

그리고 SocketOptionName?.ReuseAddress?을 적용해야 호스트로부터 동시에 두 개이상의 멀티캐스트 메시지를 수신할수 있다.

연결 종료

Close()메소드를 사용해 연결을 종료할 경우 송,수신 버퍼에 남아있는 내용은 전송이 더 이상 불가하고 Exception을 발생시킨다. Shutdown()메소드를 통해 송,수신버퍼 종료 예약을 하고 송수신이 완료됨을 확인후 Close()메소드를 통해 소켓을 종료시키는게 옳다.

버퍼 교착상태

전송및 수신 큐버퍼는 자신이 동작할 수 있는 용량에 제한이 있다.
서로 Send나 Receive상태로 들어가는 경우 데드락 상태로 들어갈수 있지만...
또한 너무 큰 데이터를 순간적으로 주고 받으려 하는 경우 송, 수빈 버퍼 이상의 데이터를 가지게 되면 전송이 완료되기 전까지 블록 상태가 된다.
만약 서버와 클라이언트가 모두 이런 경우가 생긴다면 역시 서로 데드락의 경우가 발생한다.
가장 간단한 해결방법은 송 수신을 하나의 스레드가 아닌 다른 스레드를 통해 송신과 수신을 분리하여 블록 상태가 들어갔을경우 한방향으로 해결이 되도록 유도해야한다.
처음 네트워크 프로토콜을 정의할 때 부터 고려해서 설계 해야한다.

TCP 소켓 생존 주기

TCP 핸드쉐이크는 3방향 핸드쉐이크가 사용된다. 클라이언트로부터 서버로 전송하는 연결요청과 서버가 클라이언트에게 보내는 확인 응답, 그리고 클리어언트가 다시 서버에 보내는 확인 응답이다.
클라이언트의 Connect()는 두번째 핸드쉐이크가 완료될 경우에 연결 확립이 된다. 서버의 Accept*()의 경우 원래 소켓과는 다른 새로운 소켓을 생성하여 세번의 핸드쉐이크가 완료되면 연결이 확립되고 원래의 소켓은 다시 클라이언트를 대기하게 된다.

TCP 연결 종료

전송 버퍼에 데이터가 남아있는데 그대로 프로그램을 종료할 경우 프로그램이 알지 못하는 상황에서 데이터 손실이 생긴다.
Shutdown()이나 Close()를 통해 종료 핸드쉐이크 교환후 종료해줘야한다.
프로토콜 설계시 Close()메소드를 먼저 호출하는 쪽에서 데이터 수신 여부를 확인후 종료 메소드를 호출하여 Shutdown(옵션)을 통해 Receive, Send를 하나씩 종료해주면 된다.

SocketOptionName?.Linger 은 Close()메소드가 종료 핸드쉐이크를 완료되기를 대기하면 반환하지 않는 시간을 제어한다. 타임아웃을 초단위로 설정한다.

TCP연결을 종료하는데 있어 시간 대기 상태(Time-Wait State)가 있다.
소켓 종료후 네트워크상 똑같은 소켓으로 다시 생성이 되어 전송을 하여 이전 소켓으로 전송된 데이터가 네트워크상 딜레이 있을 경우 어느 소켓에서 발생된 데이터인지 오인이 생길수 있다.
이 대기 시간(quiet time)은 구현에 따라 다르게 설정되는데 적게는 30초에서 4분까지가 된다.(패킷이 네트워크상 존재할 수 있는 최대 시간의 두배)
윈도우는 4분이 기본값이다.
문제는 같은 포트로 소켓을 생성할 수 없다는 점이다. ErrorCode? 값이 10048에 해당하는 Exception을 발생시킨다.

디 멀티 플렉스

실무자를 위한 C# 네트워크 프로그래밍

C# 프로그램 컴파일및 실행

C# 컴파일러
csc (C Sharp Compiler)
커맨드 옵션
/out:filename
/main:classname
/target:target(winexe, dll, module, ...)
/debug : type (실행중인 프로세스에 디버거를 붙이는 기본(full)형식 또는 디버깅 툴에서 접근할 수 있는 .pdb형식인 pdbonly
/resource : resource

C# 프로그램 디버깅

컴파일시 실행파일에 속성변수를 설정함으로 CLR JIT컴파일러로 하여금 코드를 추적하게 만들고, 이로써 디버거에서 참고할 추적 정보를 기록할 프로그래머 데이터베이스(PDB : Programmer DataBase?) 파일을 생성하게된다.
여기서 설정되는 속성 변수는 JITTracking 플래그라 부른다. 이 플래그는 컴파일러로부터 생성된 네이티브 코드를 MSIL명령어로, 또 이 명령어를 결국 원래의 소스 코드로 디컴파일해야 한다는 정보를 CLR JIT컴파일러에 전달한다. 이 모든 정보는 실행 파일에 대한 PDB 파일에 저장된다.

dbgclr (GUI 디버거)
visual studio와 유사한 형식

cordbg (커맨드라인 디버거)
s 소스 코드 한 개 라인을 실행
si 소스 코드 한 개 라인을 실행
so 소스 코드 한 개 라인을 건너뜀
ss 다음 네이티브 또는 IL 명령을 실행
p <arg> 변수 현재 값을 출력
pro 프로그램 실행에 대한 시스템 프로세스 정보를 출력
reg 현재 쓰레드에 대한 CPU 레지스터 정보 출력
run <prog> 디버거에서 prog 프로그램을 실행
break 소스에 브레이크지점 설정 또는 출력
sh 현재 실행중인 코드 라인과, 전후 5개 라인을 출력

IL DASM(Microsoft Intermediate Language Disassembler)

네트워크 트래픽 관리

WinPcap? 드라이버

Politecnico di Torino 의 NetGroup?팀이 개발한 모든 윈도우즈 시스템에서 네트워크 패킷을 수집할 수 있도록 설계.

WinDump?

커맨드 라인 옵션
-a 네트워크와 브로드캐스트 주소를 네임으로 변경
-B size 수신 버퍼 크기를 size로 수정
-c count 패킷을 count만큼만 수집
-D 시스템에서 사용가능한 모든 네트워크 인터페이스를 표시
-e 각 출력줄에 링크 레벨의 정보를 출력
-F file File 파일에 정의된 필터를 적용
-i interface interface네트워크를 모니터링 하는데, interface는 인터페이스 이름 혹은 ?D 명령어로 지정된 인터페이스 번호가 될 수 있다.
-n 주소를 이름으로 변화하지 않도록 설정
-N FQDN(Fully Qualified Domain Name)을 출력하지 않도록 설정
-q 간단한 형태로 패킷 정보를 출력
-r file 덤프 파일 file로부터 패킷을 읽어들임
-S 절대 TCP일련 번호를 출력
-s snaplen 패킷으로 부터 snaplen 만큼의 바이트를 수집. 기본 수치는 68이다.
-t 각 라인에 시간을 출력하지 않음
-w file 결과를 file에 출력
-X 각 패킷을 hex형태와 ASCII형태로 출력
-x 각 패킷을 hex형태로 출력

필터 조건문 적용
windump ip // 네트워크에 흐르는 ip패킷만을 수집
windump ip host 203.241.228.251 // 특정 ip주소로부터 오가는 네트워크 패킷 수집
windump ip src 203.241.228.251 // 특정 ip주소로부터 오는 네트워크 패킷 수집
windump ip host 203.241.228 // 특정 서브넷으로부터 오가는 패킷 수집

Analyzer

GUI사용. 사용 권유.

네트워크 패킷 분석

네트워크 패킷 구성

이더넷 헤더 | IP 헤더 | TCP 헤더 or UDP 헤더 | DATA

이더넷 계층

이더넷 헤더(Ethernet header)는 이더넷 802.2, 이더넷 802.3 그리고 이더넷 버전 2의 세가지 종류의 이더넷 패킷이 있따.
이더넷 802.2, 이더넷 802.3은 IEEE 표준 프로토콜로서 이더넷 계층의 트래픽을 위해서 정의되었다.
이더넷 버전 2는 표준 프로토콜은 아니지만, 이더넷 네트워크에서 가장 흔하게 쓰이는 레거시(legacy) 프로토콜이다. 원도우즈 시스템을 포함해서 거의 모든 장치들은 기본적으로 IP패킷을 전송하는데 이더넷 2프로토콜을 사용한다.

이더넷 헤더
6바이트 길이의 목적지 이더넷(혹은 MAC) 주소 // 3바이트의 벤더 식별자, 또는 OUIO(Orgranizationally Unique Identifier) 와 3바이트 길이의 벤더만의 고유 장치 일련 번호. IANA(Internet Assigned Numbers Authority)가 각 벤더의 MAC주소를 지정.
6바이트 길이의 소스 이더넷(혹은 MAC) 주소
2바이트 길이의 다음 계층 프로토콜 식별자 // IP는 0800
46에서 1500바이트 길이의 데이터 요약 정보(data payload) // 최소 46바이트야 최소 이더넷 의 길이가 64바이트로 맞춰진다.
4바이트 길이의 체크섬(checksum)

IP 계층

IP필드 비트 설명
Version 4 IP헤더 버전 형식(현재 버젼은 4)
Header Length 4 IP 패킷 헤더의 길이(단위 : octet)
Type of Service 8 패킷에 적절한 서비스 품질([BadWikiTag]Qulity of Service) 종류 (실시간 서비스가 많아 짐에 따라 높은 품질을 설정하기도 한다.)
Total Length 16 IP패킷 전체의 길이(단위 : octet)
Identification 16 IP패킷의 고유 식별 ID
Flags 3 패킷의 분할(fragmentation) 여부 및 추가 단편 존재 여부를 나타내는 플래그(항상0의값의 Reserved 플래그|IP패킷이 분할되지 않았다는 것을 나타내는 Don't Fragment(DF)플래그|IP패킷이 분할되었으며, 다른 분할된 단편들이 도착할 것임을 나태내는 More Fragment(MF)플래그)
Flagment offset 13 IP패킷 내의 분할/ 단편의 위치
Time to Live(TTL) 8 패킷이 네트워크 내에서 소멸하지 않고 유지될 수 있는 최고 시간(단위 : 초)
Protocol 8 다음 데이터 계층의 프로토콜 타입 (1:ICMP, 2:IGP, 6:TCP, 8:EGP, 9:CiscoIGP, 17:UDP, 99 CiscoEIGRP)
Header Checksum 16 IP헤더 데이터에 대한 체크섬
Source Address 32 소스 장치의 IP주소
Destination Address 32 목적지 장치의 IP주소
Options 가변 IP패킷 속성을 나타내는 기타 필드

주소 필드

하이비트 네트워크 주소 호스트 주소 타입
0 7비트 24비트 클래스A
10 15비트 16비트 클래스B
110 21비트 8비트 클래스C
1110 0비트 28비트 클래스D

DDN(Dotted Decimal Notaion)으로 표현하면

Class A 0.x.x.x - 127.x.x.x
Class B 128.x.x.x - 191.x.x.x
Class C 192.x.x.x - 223.x.x.x
Class D 224.x.x.x - 254.x.x.x
클래스 D는 두개 부분으로 나뉜다.
IP멀티 캐스트 주소 224.x.x.x - 239.x.x.x
실험적 네트워커 240.x.x.x - 255.x.x.x

TCP 계층

중요한 기능
장치 간에 복수 연결 세션 추적 (소스포트와 목적지 포트)
패킷 오더(순서)의 추적과 잃어버린 패킷에 대한 재전송 요청(일련 번호및 승인 번호)
두 장치간의 연결 스트림을 열고 닫는 기능(TCP플래그)

TCP 필드 비트 설명
Source Port 16 소스 장치 포트 번호(바인딩 하지 않을 경우 임의의 번호로 지정된다.)
Destination Port 16 목적지 장치의 포트 번호
Sequence Number 32 처음 연결되면(Syn bit = 1)을 보내면 차례로 증가한다. (단위 : octet)
Acknowledge number 32 Ack 비트가 1로 set되어있을때, 송신부가 보내야 수신부의 예측값. 즉 향후 송신부에서 보내야할 일련번호에 대한 수신부에서 예측하는 의미. 수신부의 Acknowledge number에서 송신부에서 보낸 초기 일련번호를 빼면 수신부에서 지금까지 받은 TCP데이터의 전체 길이 값을 가지게된다. 물론 올바른 데이터 전송 가정하에 공식이다.
DataOffset? 4 TCP헤더의 길이. 단위는 4 octet (32비트)단위로 offset을 곱해주면 실제 헤더의 길이가 된다. 한마디로 HeaderLength?. 옵션을 사용하지 않으면 5 (즉 octet은 4bit.. 5*4=20.. 바로 TCP헤더 크기)
Reserved 6 예약된 공간
Contorol Bits 6 연결, 연결 해제, 데이터, 응답 등에 대한 정보를 갖는다. 각각 1비트에 해당한다.

U(URG) : Urgent pointer valid flag A(ACK) : Acknowledgement number valid flag. 송신부에 응답할 때 set P(PSH) : Push flag. TCP 데이터가 송신될 때 set R(RST): Reset connection flag. 연결이 끊김(강제)을 나타낼 때 set S(SYN): Synchnonize sequence flag. 연결을 시도하고자 할 때 set F(FIN): End of data flag. 연결을 끊을 때 set
Window 16 수신부에서 현재 수신할 수 있는 TCP 데이터의 갯수. 운영체제 설정마다 다르다.(단위 : octet)
Checksum 16 체크섬
Urgent Pointer 16 URG Flag가 1로 세팅되어있을 경우. UrgentData?의 마지막 시퀀스 정보를 갖는다.
Option 가변 표준 TCP헤더에 없는 정보를 전송할때 쓰임

TCP 세션은
Opening handshake

Session communication

Closing handshake

TCP 연결 상태

CLOSED 세션이 비활성이다.
LISTEN 장치는 특정 포트에서 수집할 데이터를 대기한다.
SYN-SENT 장치는 SYN플래그를 전송하여 세션을 시작하였으며, 원격 장치에서 이를 확인하고 SYN플래그를 재전송 할 것을 대기 중이다.
SYN-RECEIVED 장치는 SYN플래그를 수집하였으며, SYN플래그와 ACK플래그를 다시 전송하여 세션을 시작할 것을 확인하고 원격 장치로 부터 ACK플래그를 대기중이다.
ESTABLISHED 두 장치 모두 opening handshake 절차를 완료했으며 서로간에 데이터 패킷을 전송 할 수 있다.
FIN-WAIT-1 장치에는 FIN플래그를 전송하여 세션을 종료하고자 한다.
FIN-WAIT-2 장치에는 FIN플래그를 수집했고, FIN플래그와 ACK플래그를 전송했다.
LAST-ACK 장치는 FIN플래그를 수집하여 응답으로 FIN플래그를 전송했고, 원격 장치로부터 ACK플래그를 대기중이다.
TIME-WAIT LAST-ACK 상태 이후 장치는 원격 장치에서 추가적인 데이터 전송하지 않는지를 확인한 뒤 정식으로 세션 포트를 닫을 수 있도록 약간의 대기 시간을 갖는다.

UDP 계층

IP통신에 자주 사용되는 또 하나의 하이레벨 프로토콜이다. TCP와는 달리 UDP는 데이터를 전송하는 네트워크 장치간에 비연결(connectionless) 전송경로가 있어
세션 확립 플래그와 연결 상태 등과 관련된 오버헤드가 그다지 많지 않다. (TCP헤더 20바이트, UDP헤더 8바이트) 각 UDP세션은 일방향으로 전송되는 단 한개의 패킷에 지나지 않는다.

UDP 필드 비트 설명
Source Port 16 소스 장치 포트 번호(바인딩 하지 않을 경우 임의의 번호로 지정된다.)
Destination Port 16 목적지 장치의 포트 번호
Payload Length 16 데이터 내용의 길이
CheckSum? 16 체크섬

IP 주소 정보 찾기

1. IPConfig 사용
2. 레지스트리 사용
3. WMI 사용 (Windows Management Instrumentation)

마이크로소프트가 구현한 Web-Based Enterprise Management(WBEM)의 한종류 이다. WBEM은 Distributed Management Task Force. Inc(DMTF) 에서 개발한 것으로 네트워크 환경에서 시스템 정보를 접근하는 표준
윈도우 2000이후, 지원하고 이전 윈도우즈는 WMI Software Developers Kit 를 통해 WMI 시스템을 별도로 설치 할 수 있다. (
http://www.microsoft.com/downloads/search.asp?로 부터 다운)

WMI 내에 있는 WMI Win32_NetworkAdapterConfiguration? 표는 시스템에서 설치된 네트워크 장치와 관련된 정보를 기록하고 있다.

필드 설명
DefaultIPGateway 장치에 할당된 IP 라우터 주소의 어레이
Description 네트워크 장치에 대한 설명
DHCPEnabled 장치가 동적으로 IP주소를 할당하는지의 여부
DHCPServer IP 주소를 할당하기 위해 사용되는 DHCP 서버
DNSHostName? 호스트명을 확인하는데 사용되는 DNS 호스트
IPAddress 장치에 할당된 IP주소의 어레이
IPEnabled 장치가 네트워크 상에서 IP를 사용하는지의 여부
IPSubnet 장치가 사용하는 IP서브넷 주소의 어레이
MACAddress 네트워크 장치에 할당된 이더넷 MAC 주소


using System.Management; 
...
ManagementObjectSearcher query = new ManagementObjectSearcher("SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = 'TRUE'");
ManagementObjectCollection queryCollection = query.Get();
foreach(ManagetmentObject mo in queryCollection)
{
...
Console.WriteLine( (string[])mo["IPAddress"] );
}

네트워크 상에서의 데이터 이동

C# 바이너리 데이터 타입

데이터 타입 바이트 설명
sbyte 1 -128 ~ 127 까지의 signed 정수
byte 1 0 ~ 255 까지의 unsigned 정수
short 2 -32,768 ~ 32,767 까지의 signed 정수
ushort 2 0 ~ 65,535 까지의 unsigned 정수
int 4 -2,147,483,648 ~ 2,147,483,647 까지의 signed 정수
uint 4 0 ~ 4,294,967,295 까지의 unsigned 정수
long 8 -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 까지의 signed 정수
ulong 8 0 ~ 18,446,744,073,709,551,615 까지의 unsigned 정수
float 4 1.5 * 10^-45 ~ 3.4 * 10^38 까지의 7자리의 유효숫자를 갖는 부동소수
double 8 5.0 * 10^-324 ~ 1.7 * 10^308 까지의 15~16자리의 유효숫자를 갖는 부동소수
decimal 16 1.0 * 10^-28 ~ 7.9 * 10^28 까지의 28~29자리의 유효숫자를 갖는 부동소수

BitConverter? Class

public static byte[] GetBytes([TYPE] value); // 특정 타입을 바이트 배열로 반환 
public static [TYPE] To[TYPE](byte[] value, int startIndex); // 바이트를 특정 타입으로 변환

// string형은 Encoding 클래스의 Encoding.ASCII.GetString()함수 사용

Buffer Class // 기본 형식 배열을 조작

public static void SetByte(Array array, int index, byte value); // 배열 특정 위치의 값을 변경 
public static void BlockCopy(Array src, int srcOffset, Array dst, int dstOffset, int count); // 배열의 블록 단위 복수

프로세스

Process class

public static Process GetCurrentProcess(); 

public static Process[] GetProcesses(); // 실행중인 프로세스 전부를 찾는다.

public static Process GetProcessById(int); // 실행중인 프로세스 찾기
public static Process[] GetProcessesByName(string); // 실행중인 프로세스 찾기

public static Process Start(string); // 오버로드 되어있음. 프로그램 이름으로 실행하는 방법외에 인자 넣을수도 있고 ProcessStartInfo 클래스에 정보 채워서 실행 가능.
public void Kill(); // 프로세스 강제 종료
public bool CloseMainWindow(); // 메인 윈도우에 닫기 메시지를 전송하여 종료를 권유. 메인 윈도우가 없거나 모달 대화상자일경우 실패하기도 한다.
public void Close(); // 프로세스 종료. 강제 종료가 아니라 연결된 리소스 전부를 해제후 종료.

스레드

Thread class

Process 클래스의 속성을 통해 Collection을 얻어온다. 
public ProcessThreadCollection Threads {get;}

public Thread(ThreadStart start); //스레드 생성자 델리게이트형 ThreadStart를 요구한다. 

public delegate void ThreadStart(); // 실행할 스레드 메소드를 지정한다.

public void Start();

public Thread(ParameterizedThreadStart start); // ParameterizedThreadStart로 등록하면  

[ComVisibleAttribute(false)]
public delegate void ParameterizedThreadStart(Object obj);

public void Start(Object state); // 스레드에 Object형 매개변수를 전달할 수 있다.

스레드 풀

ThreadPool? class
작업 항목 게시, 비동기 I/O 처리, 다른 스레드 대신 기다리기 및 타이머 처리에 사용할 수 있는 스레드 풀을 제공합니다.

생성자가 없고 모든 메소드는 정적이다.
메소드가 호출되면 운영체제는 자동으로 스레드 풀을 생성하고 25개의 스레드가 등록 가능한 상태가 된다.
타이머 큐 타이머와 등록된 대기 작업도 스레드 풀을 사용된다.
타이머 큐 타이머와 등록된 대기 작업의 콜백 함수는 스레드 풀에 대기하게 된다.
스레드 풀에 있는 스레드에서 작업 항목을 처리하도록 요청하려면

public static bool QueueUserWorkItem(WaitCallback callBack); 
public static bool QueueUserWorkItem(WaitCallback callBack, Object state);

[Serializable]
public delegate void WaitCallback(object state);

사용예)
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc,state));

를 호출하여 사용하게된다. 작업항목을 큐에 대기시킨 후에는 취소할 수가 없다.

이하 시스템 프로그래밍 관련 부분...

// 제한 시간(밀리초)에 부호 있는 32비트 정수를 사용하여 WaitHandle을 기다리는 대리자를 등록합니다. 
public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle, WaitOrTimerCallback, object, int, bool);

// WaitHandle 클래스
공유 리소스에 대한 단독 액세스를 기다리는 운영 체제 관련 개체를 캡슐화합니다

PythonPowered EditText of this page (last modified 2008-11-05 11:01:00)
FindPage by browsing, searching, or an index
Or try one of these actions: DeletePageDeleteUploadedFileLikePagesSpellCheckUploadFile

'강좌 > C#' 카테고리의 다른 글

Stream, Dgram 설명  (0) 2012.01.18
Invoke 개념 이해하기  (0) 2012.01.18
델리게이트 선언뒤에 할당  (0) 2012.01.18
Delegate(5) : 람다식  (0) 2012.01.17
C# 람다식  (0) 2012.01.17
posted by 방랑군 2012. 1. 18. 17:37
  • 방랑이 2012/01/17 19:37답글|수정|삭제
    public delegate int FirstDelegate(int x, int y); 델리게이트 선언뒤에 할당에서요..
    FirstDelegate fd = Method; 랑 FirstDelegate fd = new FirstDelegate(Method) 랑 차이점은 먼가요???
  • 린트 2012/01/17 21:20답글
    실제 차이가 없습니다. 'ㅂ' 원래 뒤의 것만 지원을 했었는데
    C# 버전이 올라가면서 앞의 형태도 지원하게 되었답니다.

    그래서 C# 프레임워크들의 이벤트를 연결할 때에 두 가지 형태를 모두 사용하죠 'ㅂ' .. !
    깨끗하게 보이려는 개발자는 앞의 방법을 사용하고
    내장 생성 기능을 이용하면 뒤의 것으로 생성되구요 >ㅁ< ㅎㅎ

'강좌 > C#' 카테고리의 다른 글

Invoke 개념 이해하기  (0) 2012.01.18
TCP/IP Socket with C#  (0) 2012.01.18
Delegate(5) : 람다식  (0) 2012.01.17
C# 람다식  (0) 2012.01.17
람다식(Lambda Expression)  (0) 2012.01.17
posted by 방랑군 2012. 1. 17. 20:56

출처 :  http://blog.naver.com/PostView.nhn?blogId=rintiantta&logNo=40115460090&categoryNo=59&viewDate=&currentPage=2&listtype=0&from=postList&userTopListOpen=true&userTopListCount=5&userTopListManageOpen=false&userTopListCurrentPage=2


어쨌건, 시작합니다. ㅎㅎ

프로젝트를 만들어주세요 ㅇㅅㅇ .. !
 


 

아래가 람다입니다. ㅇㅅㅇ

"헐킈, 먼가요 저게 ?"

『=>』요게 들어가면 람다입니다. 

 

『간단하게 x 라는 인수를 x*x 로 리턴한다는 뜻입니다.』

 

 

실행 한번 해보죠 ㅎㅎ

 

 

100 이 출력 되는 것을 볼 수 있습니다.

 

 

지금.. 제 생각에는..

"아놔, 리턴도 안 했는데, 뭐 위의 결과가 뜹니까?!" 하시는 분이 있을텐데요.

 

아래와 같이 return 을 써주셔도 됩니다 .ㅎㅎ

간단하게 X => Y 로 되어있는 람다식에서는 X 가 파라메터, Y 가 리턴값이 됩니다. >_<

 

 

아래와 같이 여러개를 넣을 수도 있지요 >_<

 

 

결과입니다. ㅎㅎ

 

 

근데 만약 파라메터가 2개라면... >_<

 

 

아래와 같이 해주시면 됩니다 .ㅇㅅㅇ...

무지 짧죠 ?

 

『귀차니즘의 역사』라고 말한 이유가 이것입니다.

점점점점점 짧아지죠... ㅎㅎ

 

 

그럼 이걸 어디다 쓰는지는 봐야죠 ㅇㅅㅇ...

그리고, 가끔 『=>』이게 나오면 "허걱... 이건 머임.." 하고 놀라시는 분이 있으시니, 조금 활용해보도록합시다. ㅎㅎ

 

우선 리스트에 Where 이라는 메서드가 있습니다.

조건에 맞는 녀석들을 뽑아서 다시 배열로 만들어주시는 분입니다.

 

보시면 파라메터로 Func<string, bool> predicate 를 달라고 하는데요.

뭔 개소리일까요 ㅇㅅㅇ ....

<string, bool> 의 뜻은 대충 『string을 박으면 bool 로 나오는 녀석』이라는 뜻입니다.

 

여기에, 리턴값이 bool 이고 파라메터가 string 인 메서드의 이름을 넣으셔도 됩니다.

근데, 길죠 ㅇㅅㅇ... 귀찮구 >_<

 

 

아래와 같이 람다식을 넣었습니다.

data 가 'A' 라는 글자를 가지고 있으면 true 를 리턴하게 되는 식입니다.

"아니, 왜 true 를 리턴하죠 ?!"

『Contains() 메서드는 맞으면 True 를 리턴하니까요 ㅇㅅㅇ』

 

자, 무지 짧아졌죠?

메소드 만들기는 『귀찮』으니까요 ... !

 

 

출력하시면 『1』이 나오겠지요 ~_~ ?

음... 결과 스크린샷을 찍었는데.... 없어졌네엥.. ;ㅁ;

 

 

어쨌건, 이번에는 MSDN 에 나오는 예제를 봅시다.

아래와 같이 썼어요.

 

Count() 메서드의 두번째 오버로딩 되는 녀석입니다.

위와 마찬가지로 해석하면요.

『int 를 박으면 bool 이 나오는 함수를 주렴 >_<』 이라는 뜻입니다.  

 

 

메서드 만들기는 귀찮죠 ... ?

아래와 같이 람다식을 박으시면 됩니다. ㅇㅅㅇ...

 

 

람다식은 일단 연습해보는 것이 제일 좋습니다. ㅇㅅㅇ

많은 연습이 제일 중요하죠.

연습하면, 메소드를 만들어야하는 귀찮은 녀석들을 위와 같이 간단간단 20글자 내외로 끝낼 수 있습니다.

 

자, 그럼 ㅇㅅㅇ ...

C# Basic 강의 종강입니다. ㅎㅎ

괜히 마지막을 Delegate 으로 잡아서 끝을 못 내드리고 똥 안 닦고 나온 기분을 연출시켜드려 죄송합니다. ;ㅁ;

딜리게이트는 Windows Forms 강의에서나 다시 보실 수 있게 될겁니다. ;ㅁ;

 

끝 ㅎㅎ ... !

'강좌 > C#' 카테고리의 다른 글

TCP/IP Socket with C#  (0) 2012.01.18
델리게이트 선언뒤에 할당  (0) 2012.01.18
C# 람다식  (0) 2012.01.17
람다식(Lambda Expression)  (0) 2012.01.17
매소드를 인수로 넘기기..  (0) 2012.01.17
posted by 방랑군 2012. 1. 17. 19:59

출처 : http://blog.naver.com/pray44u?Redirect=Log&logNo=20100066352

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace pLambdaLinQ

{

class cLambda

{

delegate int delObj();

delegate int delObjPara1(int x);

delegate int delObjPara2(int x, int y);

static public int RtnMethod(int pInt)

{

return ++pInt;

}

static public void CallMethod()

{

/* 0. 간단한 람다식 */

/* 0.1. 람다식에서 메서드 호출 */

int x=10, y=20;

delObj doObj= () => { return RtnMethod(x); };

int i = doObj();

Console.WriteLine("(x, y) => return x + y; : {0}", i);

/* 0.2. 람다식에서 메서드 만들기 */

doObj = () => { return x + y; };

Console.WriteLine("return x + y : {0}", doObj());

/* 0.3. 람다식에서 매개변수 1개 있는 메서드 만들기 */

delObjPara1 dopObj1;

dopObj1 = (pX) => { return ++pX; };

Console.WriteLine("return ++pX : {0}", dopObj1(4));

/* 0.4. 람다식에서 매개변수 2개 있는 메서드 만들기 */

delObjPara2 dopObj2;

dopObj2 = (pX, pY) => { return pX + pY; };

Console.WriteLine("return pX + pY : {0}", dopObj2(2, 8));

/* 0.5. 람다식 정의 형식

* 1. 델리게이트 선언 : 매개변수 목록·반환데이터타입이 람다식과 일치

* delegate 반환데이터타입 델리게이트이름(매개변수목록);

* 2. 람다식을 델리게이트 변수에 할당

* 델리게이트이름 변수이름 = (매개변수목록) => {메서드의 body}

*

* 0.6. 람다식 사용

* 변수이름(매개변수목록)

*/

}

}

}

[출처] C# 람다식|작성자

 

'강좌 > C#' 카테고리의 다른 글

델리게이트 선언뒤에 할당  (0) 2012.01.18
Delegate(5) : 람다식  (0) 2012.01.17
람다식(Lambda Expression)  (0) 2012.01.17
매소드를 인수로 넘기기..  (0) 2012.01.17
InvokeRequired  (0) 2012.01.17
posted by 방랑군 2012. 1. 17. 19:55

출처 :  http://www.csharpstudy.com/CSharp/CSharp-lambda.aspx 

C# 3.0부터 지원하는 => 연산자는 C#에서 람다 식(Lambda Expression)을 표현할 때 사용한다. 람다 식은 무명 메서드와 비슷하게 무명 함수(anonymous function)를 표현하는데 사용된다. 람다 식은 아래와 같이 입력 파라미터(0개 ~ N개)를 => 연산자 왼쪽에, 실행 문장들을 오른쪽에 둔다.

람다 Synyax : (입력 파라미터) => { 문장블럭 };

 

예를 들어 하나의 문자열을 받아 들여 메시지 박스를 띄운다면 다음과 같이 간단히 쓸 수 있다.

str => { MessageBox.Show(str); }

입력 파라미터는 하나도 없는 경우부터 여러 개 있는 경우가 있다. 다음 예는 파라미터가 없는 케이스 부터 두개 있는 케이스까지 보여준다. 마지막 예는 입력 파라미터의 타입이 애매한 경우 이를 써줄 수 있음을 보여준다. 일반적으로 입력타입은 컴파일러가 알아서 찾아낸다.

() => Write("No");
(p) => Write(p);
(s, e) => Write(e);
(string s, int i) => Write(s, i);

Lambda Expression을 이용하면 이전 페이지에 소개한 delegate/무명 메서드를 더 간략히 표현할 수 있다. 예를 들어 다음과 같은 Click 이벤트는 이벤트 핸들러 메서드인 button1_Click를 가리키고 있다. 그래서 메서드 button1_Click은 해당 클래스내 어딘가에 정의되어 있어야 한다.

this.button1.Click += new System.EventHandler(button1_Click);

private void button1_Click(object sender, EventArgs e)
{
   ((Button)sender).BackColor = Color.Red;
}


new System.EventHandler(button1_Click)은 간단히 button1_Click 메서드명만 사용하여 아래와 같이 줄일 수 있다.

this.button1.Click += button1_Click;

이를 좀더 간단하려면 아래와 같이 무명 메서드(Anonymous Method)를 써서 표현할 수 있다.

this.button1.Click += delegate(object sender, EventArgs e)
{
   ((Button)sender).BackColor = Color.Red;
};

그리고 람다 식을 사용하면 이를 더 간단히 다음과 같이 바꿀 수 있다. 람다 식의 오른쪽 실행 블럭이 한 문장일 때는 { } 괄호를 생략할 수 있다. 아래 식은 무명 메서드를 쉽게 람다 식으로 변경한 간단한 예이다.
this.button1.Click += (sender, e) => ((Button)sender).BackColor = Color.Red;

람다 식(Lambda Expression)은 .NET 여러 곳에서 사용되지만 특히 LINQ (Language Integrated Query) 에서 많이 사용된다. LINQ는 별도의 주제이지만 아래는 람다식이 LINQ의 Where 쿼리에서 사용되는 간단한 예이다.
var proj = db.Projects.Where(p => p.Name == strName);
 

'강좌 > C#' 카테고리의 다른 글

Delegate(5) : 람다식  (0) 2012.01.17
C# 람다식  (0) 2012.01.17
매소드를 인수로 넘기기..  (0) 2012.01.17
InvokeRequired  (0) 2012.01.17
InvokeRequired & Cross Thread 처리방법  (0) 2012.01.17
posted by 방랑군 2012. 1. 17. 19:35

출처 :  http://rintiantta.blog.me/40121658531 

시작합시다. ㅎㅎ
프로젝트를 만들어주세요 ㅇㅅㅇ ...

 


 

우선 Delegate 만드는 방법입니다.

그냥 아래와 같이 일반 필드 만드는 듯이 하신 뒤에 뒤에 그냥 메소드를 박아주시면 됩니다. ㅎㅎ



 

그리고 아래와 같이 메소드를 넣어주시면 됩니다. ~_~...

정말 뭐에 쓰는지는 모르겠지만, 일단 이해는 쉽죠 ... ?

 

int FirstDelegate(int x, int y) 이니,

리턴타입이 int 고, 파라메터가 int 2개인 메소드를 대입할 수 있는거랍니다. ㅎ

 

 

아래와 같이 하시면요 ㅇㅅㅇ ...

뭐, 쓸 수 있는 겁니다.

 

fd = Method 니까, 10 + 20, 30을 리턴하겠지요.



 

그리고 아래와 같이 메소드를 인수로 넘길 수도 있는 것이지요 ~_~...

 

 

아래와 같이 보시면 메소드의 형태를 볼 수 있는데요.

 

 

아래와 같이 형태를 볼 수 있습니다.

 

 

보신 뒤 나올 예상 반응 100 %

"그래서요 ... ?"

『....인터페이스의 기본적인 것을 Basic 강의에서 다루구요.』

『활용방법 자체는 콘솔에서 볼 수 없습니다. 우선, 인터페이스를 약간 업 시켜서 이벤트는 중급 강의에서 볼겁니다 ~_~ ㅎㅎ』

어쨌거나 끄읏 ~
 

'강좌 > C#' 카테고리의 다른 글

C# 람다식  (0) 2012.01.17
람다식(Lambda Expression)  (0) 2012.01.17
InvokeRequired  (0) 2012.01.17
InvokeRequired & Cross Thread 처리방법  (0) 2012.01.17
인터페이스를 상속한 자신을 파라미터로 호출한 경우.  (0) 2012.01.17
posted by 방랑군 2012. 1. 17. 04:45

출처 :  http://pillblog.tistory.com/entry/CrossThread 

간혹 쓰래드 안에서 혹은! 동적으로 만들어진 컨트롤이 다른 윈도우 컨트롤의 모양(레이아웃)을 건드리려 한다면!!

다음과 같은 에러가 발생한다. 

 System.UnauthorizedAccessException: Invalid cross-thread access

혹은

Cross-thread operation not valid: blah blah blah :p

그럴땐 어찌하는냐!! 

Delegate를 써야한다.

윈도우 컨트롤에게 InvokeRequired라는 걸로 이렇게 물어본다.

"내가 니 몸뚱이 색깔 좀 바꿀려는데 좀 기다릴까?"

그러면 InvokeRequired가  true/false로 대답해 줄 것이다. ㅋㅋ

허나 질문에 주의하라! true면 기다리라는 것이므로 Delegate로 하나 더 날려준다.

그리고 false가 나오면 바꾸면 된다.

InvokeRequired는 바꾸려는 컨트롤의 것을 사용해야한다. 

아래의 경우 패널을 바꾸기 위해 사용한 것. 그러므로 InvokeRequired는 패널에게 물어본다. 

코드는 아래와 같다.

        private Command PopupInvoke(Popup popup, ScreenPanel pnl)
        {
            if (pnl.InvokeRequired)
            {
                InvokeDelegate<PopupScreenPanel> id = new InvokeDelegate<PopupScreenPanel>(PopupInvoke);
                pnl.Invoke(id, new object[]{popup, pnl});
            }
            else
            {
                popup.Size = pnl.Size;
                pnl.Location = new Point(0, 0);
                pnl.Parent = popup.pnlPopup;

                popup.ShowDialog();
            }
            return null;

        } 
posted by 방랑군 2012. 1. 17. 04:39


출처 :  http://sanbj.tistory.com/entry/CCross-Thread 

최근에 소켓프로그램을 개발하던 중에도 Cross Thread 문제를 겪었다.
서버에서 Thread을 생성하여 데이터 수신을 기다리고, 수신된 데이터의 내용을 RichTextBox에 출력하려는 작업중에 
Cross-thread operation not valid: Control 'rtb_console' accessed from a thread other than the thread it was created on. 
이러한 메세지를 내뿜고 뻗어버렸다.

이런 문제를 포스팅하기 위해 샘플소스를 작성하였다.

에러가 발생하는 소스
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace MyServer
{
    public partial class Form1 : Form
    {
        private Thread _thread;
        private RichTextBox rtb_console;

        public Form1()
        {
            InitializeComponent();

            _thread = new Thread(new ThreadStart(ThreadProc));
            _thread.Start();
        }

        private void ThreadProc()
        {
int count = 0;
            while (true)
            {
                SetTextBox(count,"hello");
    count++;
            }
        }
        
        private void SetTextBox(int count, String str)
        {
             rtb_console.Text = count.ToString() +"번째" + str;
         }
    }
}


프로그램은 기본적으로 하나의 Thread(MainThread라 하자)로 시작되고, 그 Thread 내에서 UI를 화면에 그리는 메세지와 이벤트도 처리하게 된다. 하지만 사용자가 추가로 생성한 Thread는 이와 별개에 작업을 하고 있기 때문에 MainThread에 의해 그려진 UI영역(컨트롤)등을 직접 접근하게 되면 MainThread입장에서는 UI관련 메세지와 이벤트를 처리하고 있는데 다른 Thread가 방해를 하는 셈이다. 이런 상황을 Cross Thread라 한다.

위에 소스를 보게되면 프로그램이 실행되면서 시작하는 Thread에서 Form1 객체를 생성하였고, Form1객체가 RichTextBox컨트롤을 생성하였다. 예외를 발생시키기위해 임의로 Thread하나를 생성하였고, 그 Thread 안에서 RichTextBox컨트롤에 직접 접근하였다.
역시 밑줄 친 라인에서 예외를 발생시킨다.

이문제를 해결하는 방법은 Invoke 메소드를 이용하는 것이다.
MSDN에서는 Invoke를 다음과 같이 설명하고 있다.

WPF에서는 DispatcherObject를 만든 스레드만 해당 개체에 액세스할 수 있습니다. 예를 들어 기본 UI 스레드에서 분리된 백그라운드 스레드는 UI 스레드에서 작성된 Button의 콘텐츠를 업데이트할 수 없습니다. 백그라운드 스레드가 Button의 Content 속성에 액세스하려면 백그라운드 스레드에서 UI 스레드에 연결된 Dispatcher에 작업을 위임해야 합니다. 이 작업은 Invoke 또는 BeginInvoke를 사용하여 수행할 수 있습니다. Invoke는 동기적이고 BeginInvoke는 비동기적입니다. 작업은 지정된 DispatcherPriority에서 Dispatcher의 이벤트 큐에 추가됩니다. 

다른 Thread에서는 UIThread(MainThread)에 접근할 수 없기 때문에 
UIThread의 Invoke메소드를 이용하여,
 다른 Thread에서 호출할 메소드를 delegate로 
정의하여 넘겨줘야 한다. 
호출될 메소드의 파라메터가 있다면 이는 object로 넘겨주면된다.
이렇게 해주게 되면 MainThread에게 너 할일 다하고,
이것도 좀 해줘라는 식으로 전달되게 된다.


수정된 소스
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace MyServer
{
    public partial class Form1 : Form
    {
        private Thread _thread;
        private RichTextBox rtb_console;
        private delegate void SetTextBoxCallback(int count, String str);

        public Form1()
        {
            InitializeComponent();

            _thread = new Thread(new ThreadStart(ThreadProc));
            _thread.Start();
        }

        private void ThreadProc()
        {
            int count = 0;
            while (true)
            {
                SetTextBox(count,"hello");
                count++;
            }
        }

        private void SetTextBox(int count, String str)
        {
            if (this.rtb_console.InvokeRequired)
            {
                SetTextBoxCallback setTextBoxCallback =                                           
                                                                      new  SetTextBoxCallback(SetTextBox);
                this.Invoke(setTextBoxCallback, new object[] { count, str });
            }

            else
            {
                rtb_console.Text = count.ToString() +"번째" + str;
            }
        }
    }
}

private delegate void SetTextBoxCallback(int count, String str);
추가 되었고, SetTextBox에서 InvokeRequired라는 속성을 사용하였다.

InvokeRequired는 다른 Thread로부터 호출되어 Invoke가 필요한 상태를 체크하여 true/false를 리턴한다. 따라서 Invoke가 필요한 상태일때는 Invoke메소드에 의해 호출될 SetTextBox를 delegate로 넘겨주면 된다.

위 소스에서 사실은 this.rtb_console.Invoke를 호출해야 하지만 this.Invoke로 호출하였는데, this.rtb_console(TextBox)이나 this(Form)나 MainThread의 영역이기 때문에 this.Invoke로도 같은 MainThread에게 위임하게 된다. 


posted by 방랑군 2012. 1. 17. 03:50
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics;

namespace ConsoleApplication1
{

    public interface IMessengeSeverUI
    {
        void WriteMessage(string message);

        void UpdateClientCount(int count);
    }

    public class MessengServerUI : IMessengeSeverUI
    {

        #region IMessengeSeverUI 멤버

        public void WriteMessage(string message)
        {           
            Debug.Write(message);            
        }

        public void UpdateClientCount(int count)
        {            
            Debug.Write(count);            
        }

        #endregion
    }

    public interface IMessengerServer
    {
        bool Start(IMessengeSeverUI ui);

        bool Stop();
    }

    public class MessengerServer : IMessengerServer
    {
        private string m_ID="황승재";

        public string ID {
            get {
                return m_ID;
            }
        }
        #region IMessengerServer 멤버

        public bool Start(IMessengeSeverUI ui)
        {           
            ui.WriteMessage("IMessengeSeverUI 상속된 자신을 파라미터로 보낸 경우");
            return true;
        }

        public bool Stop()
        {           
            Debug.Write("Stop");
            return true;
        }

        #endregion
    }

    class Program
    {
        static void Main(string[] args)
        {

            // 1
            IMessengeSeverUI m_ui = new MessengServerUI();
            m_ui.WriteMessage("인터페이스가 하위 상속된 클레스 함수 호출.");
           
            // 2
            (new TestClass()).Click();
        }
    }

    class TestClass : IMessengeSeverUI
    {
        private IMessengerServer m_server = null;

        public void Click()
        {
            m_server = new MessengerServer();

            m_server.Start(this);
        }

        #region IMessengeSeverUI 멤버

        public void WriteMessage(string message)
        {
            //throw new NotImplementedException();
            Debug.Write(message);
        }

        public void UpdateClientCount(int count)
        {
            //throw new NotImplementedException();
            Debug.Write(count);
        }

        #endregion
    }
}

posted by 방랑군 2012. 1. 17. 03:23
    public interface IMessengeSeverUI
    {
        void WriteMessage(string message);

        void UpdateClientCount(int count);
    }

    public class MessengServerUI : IMessengeSeverUI
    {

        #region IMessengeSeverUI 멤버

        public void WriteMessage(string message)
        {
           
            Debug.Write(message);            
        }

        public void UpdateClientCount(int count)
        {
           
            Debug.Write(count);            
        }

        #endregion
    }

static void Main(string[] args)
        {

            IMessengeSeverUI m_ui = new MessengServerUI();

            m_ui.WriteMessage("인터페이스가 하위 상속된 클레스 함수 호출.");
        }

결과 :  인터페이스가 하위 상속된 클레스 함수 호출.
posted by 방랑군 2012. 1. 17. 02:54
using System;

using System.Threading;

namespace VTFramework.ThreadEx
{
/// <summary>
/// vS_Thread에 대한 요약 설명입니다.
/// 
///  쓰레드에 인수를 넘기는 방법.
/// 
/// - Example
/// string strMsgInfo = "전달 값";
/// Thread td = vCS_ThreadEx.CreateThread(new vCS_ThreadEx.dg_ThreadProc(ThreadProc),strMsgInfo);
/// td.Start();
/// 
/// private void ThreadProc(object[] p) 
/// {}
/// </summary>
public class vCS_ThreadEx
{
public vCS_ThreadEx()
{
//
// TODO: 여기에 생성자 논리를 추가합니다.
//
}

public delegate void dg_ThreadProc(object[] o); 

private class ParamProc 

public object[] param; 
public dg_ThreadProc proc; 

public void work() 
proc(param);

public void work(object objValue) 
proc(param);
}   

public static Thread CreateThread(dg_ThreadProc proc, params object[] arg) 
ParamProc pp = new ParamProc(); 
pp.param = arg; 
pp.proc = proc; 
Thread t = new Thread(new ThreadStart(pp.work));
return t; 
}
}

posted by 방랑군 2012. 1. 17. 02:52

그것은 C# 프로그래밍의 첫걸음을 내디면서 만난 첫번째 난관이었다.

단지 쓰레드를 생성하고 그 안에서 폼에 붙어있는 레이블 컨트롤의 텍스트 속성만 바꿔주려 했을 뿐이었는데, 난데 없이 튀어나오는 예외메세지에 당황하지 않을 수 없었다. 원인을 찾아보니,

C# 스레드에선 다른 스레드의 컨트롤을 건드는 일은 하지 말아야 한다는 것이었다.

이래서 더더욱 난 C/C++이 좋을 수 밖에. C#은 너무 딱딱하다. 깐깐하다.

그럼 이런 상황을 가능하도록 만들려면? invoke를 통해 교차쓰레드접근을 우회하면 된다고 하는데,

무슨말인지, 나더러 어쩌라는건지 더더욱 당황스러워지고...인터넷 뒤져서 다른분들의 소스도 좀 찾아보고, 책도 보고, MSDN도 뒤져보고 나니 이렇게 하면 되는구나 하는 감이 오기는 하더라.

스포츠 뉴스에 보니, 퍼거슨이 나니를 팔일은 없을거라고 하던데...

어찌됬건간에 본론으로 돌아와서,

위의 예외창이 뜨는 경우의 예를 보면 다음과 같다.

public void threadfunc()

{

m_mainform.button1.Text = i.ToString();

}

보시는데로, 이 함수는 메인폼 버튼의 텍스트를 설정해주는 코드다.

디버그모드로 빌드할 경우, 위 함수를 쓰레드로 돌리면 여지없이 예외박스가 출현해 주신다. 릴리즈 모드로 돌리면 잘 돌아가는것 처럼 보이는데 영 찜찜하다. 나중에 문제가 될지도 모르겠고, 비록 돌긴 돌아도 하지 말라는 짓이니 만큼 예외박스를 없애야 속이 편할 것 같다.

예외를 발생시키는 위 함수를 다음과 같이 고쳐보면,

public void threadfunc()

{

m_mainform.Invoke(new MethodInvoker(delegate()
{
m_mainform.button2.Text = i_1.ToString();
}));

}

어라, 잘 돈다. 쓰레드에서 버튼 텍스트를 직접 변경하는게 아니라, 폼의 Invoke 메소드를 통해서 버튼의 텍스트를 변경한다.

그리고 Invoke메소드를 사용하기 위해선 delegate를 인자로 제공해 주어야 한다. 위 예에선 MethodInvoker delegate가 제공되었는데, Microsoft.JScript 네임스페이스 안의 MethodInvoker 클래스와 혼동하지 말아야 겠다.

MethodInvoker delegate는 인자로 제공된 메소드를 실행하는 delegate를 나타내준다.

여기서 MethodInvoker에 제공된 인자는 바로,

"delegate(){m_mainform.button2.Text = i_1.ToString();}" 요놈이다.

메소드가 통째로 들어가 있다.

보기엔 안좋지만, 결국엔 폼의 Invoke메소드에 delegate를 인자로 제공하여 원하던 것을 할 수 있게 되었다.

한가지 불편한 점은 MethodInvoker delegate는 오로지 파라미터도 없고 리턴값도 없는 메소드만을 인자로 사용할 수 있다는 점인데, delegate 메소드에 파라미터를 넘겨야 할 경우엔 어떡해야 하나?

머리좀 굴려보면 굳이 파라미터가 필요 없더라도 같은 효과를 얻을 수 있을 것이고,

꼭 파라미터를 넘기고 싶다면 넘기면 된다. 어떻게?

다음과 같이 매개변수를 받은 함수의 타입의 delegate를 선언해준다.

delegate void SetObjectText_invoke3(int i);

이 예에선 int형의 변수를 파라미터로 취하는 함수타입으로 delegate를 선언했다.

C에서의 함수포인터의 개념과 비슷해보인다.

타입을 선언하였으면 그 타입으로 변수를 정의한다.

SetObjectText_invoke3 MI_SetText;

그리고, MI_SetText에 우리가 실제로 사용할 함수를 대입해넣는다.

MI_SetText = settext_invoke3;

settext_invoke3함수는 아래처럼 정의되어있다.

public void settext_invoke3(int i)

{

m_mainform.button4.Text = i.ToString();

}

역시, 매개변수로 받는 i를 이용해 버튼컨트롤의 Text를 바꾸는 코드다.

이제 쓰레드 안에서 다음처럼 사용하면 된다.

m_mainform.Invoke(MI_SetText, i);

샘플첨부.

TEST_Delegate_Winform.zip

posted by 방랑군 2012. 1. 17. 02:25

delegate 리턴타입 델리게이트명(파라미터);

 

델리게이트는 자신에게 연결된 함수를 대신 실행시켜주는 역할을 하는 일종의 함수 포인터이다.
즉 자신에게 할당된 함수를 대신 실행시켜준다.

 

델리게이트와 연결된 메서드의 목록을 확인할 때에는 GetInvocationList 메서드를 이용한다.


ex) foreach (var delegate in XXXXDelegate.GetInvocationList()) { // ... }

 

델리게이트는 익명 메서드, 람다 메서드와 궁합이 잘 맞는다.
익명 메서드, 람다 메서드는 표현식을 간결하게, 인라인의 형태로 가져감으로써 코드가 축약되고
경우에 따라 가독성이 향상된다.

 

1. 익명 메서드 (이름이 없는 메서드)
델리게이트에 의해 호출되는 함수가 단순한 작업을 수행하는 '짧은' 메서드라면 굳이 새롭게 메서드의 이름을 정의하고 구현하는 것보다 익명 메서드를 이용하는 것이 낫다.

 

ex)
delegate void SampleDelegate(string message);

SampleDelegate sample = delegate(string message)
{
 Console.WriteLine(message);
};

sample("Hello, World");

 

2. 람다 메서드 (익명 메서드의 소집합)
람다식의 특징은 람다 연산자 '=>'에 있다.

1번의 예제 내용이 다음과 같이 축약됩니다.


ex)
SampleDelegate sample = (string message) => Console.WriteLine(message);

Delegate(대리자) 

 1] 함수의 기능을 대신해 주는 대리자 역할을 한다.

 2] 여기서 대리자라 하면 자신에게 전달된 함수를 대신해 준다고 보면..

 3] Delegate를 선언하려면 함수의 시그너쳐를 일치

  ==> 함수의 매개변수와 데이터 타입.....

 4] 이벤트는 델리게이트 기반으로 만들어 졌다.

   Ex) this.버튼.click += .....();

Delegate 선언

 delegate 리턴타입 델리게이트명(파라미터타입 변수);

Delegate 생성 및 호출

 델리게이트명 델리변수명 = new 델리게이트명(생성함수명);


 public 리턴타입 생성함수명(파라미터타입 변수)

 {

   // 코드

 }

Delegate 간단 예제_1

 delegate void deleTest(string strParam);

 static void Main(string[] args)
 {
    deleTest testDele_1 = new deleTest(dely);
    deleTest testDele_2 = new deleTest(dely);

    testDele_1("도망노비 테스트_1");
    testDele_2("이게 델린가여??");
 }

 public static void dely(string strParam)
 {
    Console.WriteLine(strParam);
 }

Delegate 간단 예제_2

 delegate void deleTest(string strParam);

 class Program
 {
    public static void Main()
    {
        TestDele classDele = new TestDele();
        deleTest dele_1 = new deleTest(classDele.DeleTest_1);
        dele_1("11");

        deleTest dele_2 = new deleTest(classDele.DeleTest_2);
        dele_2("11");
    }
 }

 class TestDele
 {
   public void DeleTest_1(string strParam)
    {
        Console.Write("Static_TEST ==> ");
        Console.WriteLine(strParam);
    }
    public void DeleTest_2(string strParam)
    {
        Console.Write("TEST ==> ");
        Console.WriteLine(strParam);
    }
 }


[출처] [C#] Delegate|작성자 에스이오케이

posted by 방랑군 2012. 1. 16. 21:46

Hashtable, HashSet, Dictionary 세 자료구조 모두 기본 알고리즘은 같다

해시코드를 사용하여 인덱스를 구하며 구조체와 배열에 데이터를 저장한다.


그럼 이 세놈의 차이점은 무엇일까? 간단히 정리해 보겠다. (내공이 부족하여 자세히는 못한다 ㅠㅠ)


Hashtable 과 Dictionary 는 둘 다 IDictionary 를 구현하고 있다.

다만 Hashtable 은 제너릭을 지원하지 않고, Dictionary<TKey, TValue> 는 제너릭을 지원한다.


HashSet<T> 은 제너릭은 지원하지만 키를 사용자에게 입력받지 않고

값을 이용하여 해시코드를 구한다.

이걸 어디에 쓸까 싶었지만, 중복값제거에 용의한듯하다.


참고 : http://chaoskcuf.com/191

[C#] 파일에서 중복을 제거하는 방법

 

 http://chaoskcuf.com/entry/C-파일명이-중복일-때-자동으로-이름을-생성하는-코드#comment3151

김시억님께서 위의 주소에 문의하신 내용입니다.

중복제거에관한건데요 텍스트화일을 불러들여 중복제거를 하려고합니다 
처음인데 막상시작하려하니 희안하게 로직이 잘 안떠오르는군요/// 
제가 1년좀 안되는데요 c#만 ....한번도 안해봐서 텍스트중복은;;; 
aa 
aa 
bb 
bb 
이렇게 있다고 할경우 저 텍스트화일을 중복제거해서 
aa 
bb 
이렇게만 결과물이 나오게하려면 어떻게 해야하나요? 
쉬운거 같으면서도 막상코딩하려니 로직이 감이 잘 안잡히네요 ㅠ.ㅠ 
답변부탁드립니다


가장 쉬운 방법은 .NET Framework 3.5에 HashSet을 사용하는 것입니다. 
HashSet은 .NET Framework 3.5에 처음 추가된 Generic Class로 어셈블리는 System.Core.dll이고, 
Namespace는 System.Collections.Generic 입니다. 
(주의 .NET Framework 3.5가 설치되어 있지 않으면 당연히 위의 클래스를 사용할 수 없습니다.)

Hash 특성상 내부의 hash function으로 현재 collection 안에 들어있는 값을 찾는데 걸리는 시간이 적고, 
Set 특성상 같은 내용을 중복으로 담고 있지 않아서 문의하신 내용에 가장 적합한 클래스입니다.

(그러나 Add 메소드는 상대적으로 다른 Collection보다 느릴 수 있기 때문에, 아주 긴 파일(수 MB가 넘는 파일)에는 적합하지 
않을 수 도 있습니다.)

해당 내용은 아래와 같이 구현할 수 있습니다.


Program.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.IO;

namespace HashSetTest
{
    class Program
    {
        static void Main(string[] args)
        {
            HashSet<string> set = new HashSet<string>();

            using (TextReader reader = File.OpenText("test.txt"))
            {
                string line = reader.ReadLine();
                while (string.IsNullOrEmpty(line) == false)
                {
                    set.Add(line);
                    line = reader.ReadLine();
                }
            }

            foreach (string setItem in set)
            {
                Console.WriteLine(setItem);
            }
        }
    }
}

test.txt

aa
bb
aa
cc
aa
bb

Output

output


샘플 프로젝트 파일도 함께 첨부합니다.

'강좌 > C#' 카테고리의 다른 글

Cross-thread operation not valid  (0) 2012.01.17
Delegate  (0) 2012.01.17
Generic 컬렉션 - List, Dictionary, Queue, Stack  (0) 2012.01.16
DictionaryEntry 구조체  (0) 2012.01.16
[C#] internal, protected internal  (0) 2012.01.16
posted by 방랑군 2012. 1. 16. 21:46

 Generic 형태의 컬렉션 구조를 사용하기

ArrayList

=

List<>

Hashtable

=

Dictionary<>

Queue

=

Queue<>

Stack

=

Stack<>

▼ Generic Collection

using System.Collections;

namespace ConsoleEx2
{
class Ex03_GenericCollection
{
static void Main(string[] args)
{
//ArrayList
ArrayList list1 = new ArrayList();
list1.Add(100); //int -> object
list1.Add(200); //업캐스팅
list1.Add(300); //박싱
int n1 = (int)list1[0]; // object -> int(다운,언박싱)

//ArrayList의 제네릭 버전 -> List<T>
//박싱, 언박싱이 일어나지 않는다
List<int> list2 = new List<int>(); //int[]
list2.Add(100); //int -> int
list2.Add(200);
list2.Add(300);

//언박싱이 일어나지 않는다
int n2 = list2[0];//int -> int

//아래와 같은 상황은 별로 권장x, 하나의 박스엔 같은 자료형의 집합만..
//제네릭 컬렉션에선 서로 다른 자료형을 저장할 순없다
//단지 박싱, 언박싱이 안일어난다는 차이밖에 없음
// 형변환이 일어나면 예상치 못한 에러 발생가능성이 높다!
list1.Add("홍길동");//x


//HashTable
Hashtable ht1 = new Hashtable();
ht1.Add("국어", 100);
ht1.Add("영어", 90);
ht1.Add("수학", 85);
int jumsu1 = (int)ht1["영어"];

//HashTable의 제네릭버전 -> Dictionary
//<키의 자료형, 값의 자료형>
Dictionary<string, int> ht2 = new Dictionary<string,int>();

//박싱, 언박싱 없음
ht2.Add("국어", 100);
ht2.Add("영어", 90);
ht2.Add("수학", 85);
int jumsu2 = ht2["영어"];


//Queue 제네릭
Queue<int> queue = new Queue<int>();
queue.Enqueue(100);
Console.WriteLine(queue.Dequeue() +100);

//Stack 제네릭
Stack<int> stack = new Stack<int>();
stack.Push(200);
Console.WriteLine(stack.Pop() + 100);

}
}
}

'강좌 > C#' 카테고리의 다른 글

Cross-thread operation not valid  (0) 2012.01.17
Delegate  (0) 2012.01.17
Hashtable, HashSet<T>, Dictionary<TKey, TValue>  (0) 2012.01.16
DictionaryEntry 구조체  (0) 2012.01.16
[C#] internal, protected internal  (0) 2012.01.16
posted by 방랑군 2012. 1. 16. 21:43


MSDN 참고로 공부중.... 당체 먼말?? ㅋㅋ

 

DictionaryEntry

==> 설정하거나 검색할 수 있는 사전 키/값 쌍을 정의

http://msdn.microsoft.com/ko-kr/library/system.collections.dictionaryentry(v=VS.100).aspx

언어의 foreach 문에는 컬렉션의 각 요소 형식이 필요합니다.

IDictionary의 각 요소가 키/값 쌍이므로 요소 형식은 키의 형식도, 값의 형식도 아닙니다.

대신 요소 형식은 DictionaryEntry입니다.

http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=KO-KR&k=k(SYSTEM.COLLECTIONS.IDICTIONARY)&rd=true

이곳으로 찾아가 보았다..

IDictionary 인터페이스는 키/값 쌍의 제네릭이 아닌 컬렉션에 대한 기본 인터페이스입니다



MSDN에 있는데 먼말인지는 알겟다..;;

'강좌 > C#' 카테고리의 다른 글

Cross-thread operation not valid  (0) 2012.01.17
Delegate  (0) 2012.01.17
Hashtable, HashSet<T>, Dictionary<TKey, TValue>  (0) 2012.01.16
Generic 컬렉션 - List, Dictionary, Queue, Stack  (0) 2012.01.16
[C#] internal, protected internal  (0) 2012.01.16
posted by 방랑군 2012. 1. 16. 21:04

출처 :  http://blog.naver.com/khagaa?Redirect=Log&logNo=30096456055 


public, private, protected 이외에 internal이라는 접근 제어자가 있다.

액션스크립트에서도 본적이 있는데 거의 쓸 일이 없어서 써본적이 없는것 같다.


internal은 어셈블리를 기준으로 나뉜다고 한다. 어셈블리라는 것은 한 프로젝트가 뽑아내는 결과물이다.

exe가 됐던 dll 이 됐던 한 프로젝트는 결과물을 뽑아내고 이 결과물 내부에서는 public 처럼 접근이 가능하다고 한다.


접근이 불가능한 경우를 예로 들면 dll 프로젝트에서 internal로 선언된 클래스가 있다고 치면 다른 exe프로젝트에서 dll 을 참조하여

사용한다고 할 때 어셈블리가 다르기 때문에 internal 클래스에 접근 하지 못한다.


여기서 중요한 문제점 하나는 dll 을 참조하여 상속을 받으려 할 때이다.

internal로 선언된 클래스는 외부 프로젝트에서 접근을 할 수 없기 때문에 외부 프로젝트에서 dll 을 참조하여 상속 받는 경우에 대한 대비책이 필요할 수 있다.

그래서 나온것이 protected internal이다.

어셈블리가 다르더라도 상속을 받게 된다면 슈퍼클래스의 protected internal속성을 접근 가능하게 된다.

 

'강좌 > C#' 카테고리의 다른 글

Cross-thread operation not valid  (0) 2012.01.17
Delegate  (0) 2012.01.17
Hashtable, HashSet<T>, Dictionary<TKey, TValue>  (0) 2012.01.16
Generic 컬렉션 - List, Dictionary, Queue, Stack  (0) 2012.01.16
DictionaryEntry 구조체  (0) 2012.01.16
posted by 방랑군 2012. 1. 13. 20:30

윈도우 7 기준

C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin 폴더로 이동

AxImp.exe 명령을 이용해 shdocvw.dll 을 등록하면, SHDocVW 와 AxSHDocVW 를 함께 등록한다.

AxImp %Windir%\System32\shdocvw.dll

이렇게 하면 AxSHDocVw.DLL이 생성되는 것을 확인할 수 있다.

posted by 방랑군 2012. 1. 13. 20:01

Blinking in task bar like a MSN messenger

 

이번 강좌의 주제는 “MSN 메신저 스타일의 Task Bar 점멸 기능 넣기입니다.

MSN이나 네이트온 메신저는 새로운 친구가 접속 하거나 새로운 메시지가 도착하면  가지 방법으로 사용자에게 알려주는데  중에 좌측의 시스템 트레이에서 조그만 창이 스르르 올라 오는 기능을 구현 하는 것은 많이 알려져 있습니다만 Task Bar 해당 프로그램의  이름이 점멸하는 기능을 구현 하는 것은 상대적으로  알려져 있지 않은  같아 간단한 강좌를 작성 했습니다.

간단한 기능이므로 필요한 기능만 언급할 것입니다덕분에 강좌의 길이도 작습니다.

 필요한 기능인데 구현 방법을 알지 못해  고생 하는 개발자에게 도움이 되길 바랍니다.

 

[알아두세요]

1.        강좌에서 만들어진 예제는 Windows XP Professional SP2에서 테스트되었습니다.

2.        강좌는 .NET Framework 2.0 Visual Studio 2005 기준으로 제작되었습니다.

3.        강좌에서 사용된 개발 도구와 언어는  Visual Studio 2005 C#입니다.

 

1.       어떤 기능을 구현  것인가?

많은 메신저들이 새로운 메시지가 도착하거나 친구가 접속 했을  사용자에게 알려주기 위해 Task Bar 점멸 기능을 사용합니다 강좌는 메신저를 만들기 위한 강좌는 아니므로 단지 필요한 기능만을 습득하기 위해 샘플 어플리케이션이  활성화되고 일정 시간(5) 경과해도 활성화 되지 않으면 강제로 Task Bar에서 해당 어플리케이션 부분이 점멸하는 기능을 구현  것입니다(그림 1).

 

[그림 1]

 

2.       핵심 API

Task Bar 점멸 기능을 구현하기 위해서는 API 이용하는 방법이 가장 쉽습니다물론  번의 삽질로도 C# 코드만으로 구현이 가능하겠지만 이미 있는 기능 사용하는 것이 수명 연장에도 도움이 되겠죠해당 API 선언부를 알아보겠습니다. MSDN 있는 내용 그대로입니다.

 

FlashWindowEx

Flashes the specified window. It does not change the active state of the window.

BOOL FlashWindowEx)

   PELASHWINFO pfwi

);

Parameters

pfwi

   [in] A pointer to a FLASHWINFO structure.

Return Values

The return value specifies the window’s state before the call to the FlashWindowEx function. If the window caption was drawn as active before the call, the return value is nonzero. Otherwise, the return value is zero.

 

C#에서 사용하기 위한 선언도 알아보죠필요한 상수도 함께 알아보겠습니다.

[StructLayout(LayoutKind.Sequential)]

public struct FLASHWINFO

{

    public UInt16 cbSize;

    public IntPtr hwnd;

    public UInt32 dwFlags;

    public UInt16 uCount;

    public UInt32 dwTimeout;

}

 

private const UInt32 FLASHW_STOP = 0;

private const UInt32 FLASHW_TRAY = 2;

 

[DllImport("user32.dll")]

static extern Int16 FlashWindowEx(ref FLASHWINFO pwfi);

 

3.       간단한 샘플을 만들어보자.

API 모양새를 둘러 보았으니 간단하게 맛보기 샘플을 만들어 보겠습니다. API 어떤 방식으로 동작하는지를 알아보기 위한 것이니 단순하게 API 호출하고 어플리케이션이 어떻게 반응하는지 살펴보겠습니다.

 

Step 1. 윈도우 어플리케이션 프로젝트를 하나 만듭니다.

 

Step 2. 메인  클래스에 위의 API 선언부를 전부 복사  넣습니다.

 

Step 3.  위에 버튼 컨트롤을 하나 배치합니다.

 

Step 4. 버튼의 클릭 이벤트 핸들러에 API 호출하는 코드를 삽입합니다(소스 1).

private void button1_Click(object sender, EventArgs e)

{

    FLASHWINFO fInfo = new FLASHWINFO();

 

    fInfo.cbSize = (ushort)Marshal.SizeOf(fInfo);

    fInfo.hwnd = this.Handle;

    fInfo.dwFlags = FLASHW_TRAY;

    fInfo.uCount = UInt16.MaxValue;

    fInfo.dwTimeout = 0;

 

    FlashWindowEx(ref fInfo);

}

[소스 1]

 

Step 5. 빌드 하고 실행한  버튼을 누르면 Task Bar 해당 어플리케이션 이름 부분이 점멸합니다.

 

4.       어플리케이션 비활성화 알림 기능을 구현  보자.

테스트로 간단한 어플리케이션을 만들어 보았으니 이제 조금  복잡한 기능을 가진 어플리케이션을 만들어 보겠습니다.

제작할 어플리케이션이 가지게  기능은 폼이 비활성화되고 5초가 지나도 활성화 되지 않으면 Task Bar에서 점멸하여 사용자에게 알려주는 기능입니다.

 

Step 1. 윈도우 어플리케이션 프로젝트를 하나 만듭니다.

 

Step 2. 메인  클래스에 위의 API 선언부를 전부 복사  넣습니다.

 

Step 3. Timer 하나 추가하고 Interval 5000으로 설정합니다.

 

Step 4. 폼의 Deactivate 이벤트 핸들러에 타이머를 시작하는 코드를 삽입합니다(소스 2).

private void Form1_Deactivate(object sender, EventArgs e)

{

    this.timer1.Start();

}

[소스 2]

 

Step 5. 타이머의 Tick이벤트 핸들러에 점멸 기능을 활성화 시키는 API 호출 코드를 삽입합니다(소스 3).

private void timer1_Tick(object sender, EventArgs e)

{

    FLASHWINFO fInfo = new FLASHWINFO();

 

    fInfo.cbSize = (ushort)Marshal.SizeOf(fInfo);

    fInfo.hwnd = this.Handle;

    fInfo.dwFlags = FLASHW_TRAY;

    fInfo.uCount = UInt16.MaxValue;

    fInfo.dwTimeout = 0;

 

    FlashWindowEx(ref fInfo);

}

 

Step 6. 폼의 Activate 이벤트 핸들러에 타이머를 정지하고 점멸 기능을 비활성화 시키는 API 호출 코드를 삽입합니다(소스 4).

private void Form1_Activated(object sender, EventArgs e)

{

    this.timer1.Stop();

    FLASHWINFO fInfo = new FLASHWINFO();

 

    fInfo.cbSize = (ushort)Marshal.SizeOf(fInfo);

    fInfo.hwnd = this.Handle;

    fInfo.dwFlags = FLASHW_STOP;

    fInfo.uCount = UInt16.MaxValue;

    fInfo.dwTimeout = 0;

 

    FlashWindowEx(ref fInfo);

}

 

Step 7. 빌드하고 실행합니다테스트  보면 Minimize 버튼을 클릭하거나 다른 어플리케이션을 활성화해서 폼이 비활성화되면 5  Task Bar에서 점멸 기능이 동작 하고 폼을 활성화시키면 점멸 기능이 멈추는 것을   있습니다(그림 2).

 

[그림 2]

 

5.       마무리

FlashWindowEx 동일한 기능을 가진 API로는 FlashWindow 있습니다사용법이 훨씬 간단하므로 단순한 기능을 원한다면 사용에 무리는 없습니다하지만 점멸 간격 등의 정교한 제어를 원한다면 FlashWindowEx API 사용하는 것이 좋습니다파라메터에 해당하는 FLASHWIFO 구조체의 값을 적절히 바꾸어보면서 기능을 살펴보면 정확한 사용법을   있을 것입니다.

 

 

 

 

====================================

서용혁 (hyuki, mvp, c#)

crazy for .NET

Visual Studio 2005, .NET Framework, C#, MSDN Magazine

 

http://www.clrlab.org

====================================

posted by 방랑군 2012. 1. 13. 19:55

참조 :   http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=18&MAEULNo=8&no=1774&ref=1774 

1. WinAPI를 사용한다.

2. ShowWithoutActivation 프로퍼티를 상속받아서 Child Form Class를 구현한다.

 

 

그럼 차례대로 소개하도록 하겠습니다. 그리 어렵지 않습니다.

 

 

 

1. WinAPI - ShowWindow함수 사용

 

[System.Runtime.InteropServices.DllImport("user32.dll")]

public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

 

//RecvMemoWnd가 Form입니다. (쪽지창)

RecvMemoWnd memo = new RecvMemoWnd (strTitle, strContent, strTime, strSender);

ShowWindow(memo .Handle, WM_SHOWNOACTIVATE); //WM_SHOWNOACTIVATE = 4

 

 

 

2. 닷넷 - ShowWithoutActivation를 오버라이드하는 Child Form Class

 

public class Test : Form

{

   bool showWithoutActivation = false;

   protected override bool ShowWithoutActivation

   {

      get

      {

         return showWithoutActivation;

      }
   }

 

   public void Show(bool activate)

   {

      showWithoutActivation = !activate;

      Show();
   }
}

 

--------------------------------------------------------------------

 

Test test = new Test();

test.Show(false); 
posted by 방랑군 2012. 1. 13. 02:19

 iBatis는 여러 솔루션들로부터 최고의 아이디어들을 표출하여 그 시너지를 이끌어낸다.

표는 각각의 접근 방법 중에서 iBatis가 차용한 몇몇 아이디어들을 정리한 것이다.

 방법

 비슷한 이점

해결된 문제점 

 저장 프로시저

 iBatis는 SQL을 캡슐화 하고 외부로 분리하여 애플리케이션 외부에 SQL을 둔다. iBatis는 저장 프로시저와 비슷하지만 객체지향적인 API를 제공한다. iBatis는 또한 저장 프로시저를 직접 호출하는 방법도 지원한다.

 비즈니스 로직이 데이터베이스 밖에 있도록 한다. 배포하기 쉽다. 테스트하기 쉽다. 이식성이 좋다.

 인라인 SQL

 iBatis는 SQL을 원형 그대로 작성할 수 있다. 문자열 연결이나 파라미터 값 설정, 결과값 가져오기 등이 불필요하다.

 iBatis는 애플리케이션 코드내에 삽입되지 않는다. 전처리기가 불필요하다. SQL의 일부가 아닌 모든 기능을 사용할 수 있다.

 동적 SQL

 iBatis는 파라미터를 기반으로 하여 동적으로 쿼리를 구성하는 기능을 제공한다. 쿼리 구성 API는 필요 없다.

 iBatis는 애플리케이션 코드에 섞인 연결된 블록으로 SQL을 작성할 필요가 없다.

 객체/관계 매핑

 iBatis는 ORM도구의 적재 지연, 조인해서 값 가져오기, 실시간 코드 생성, 상속 등과 같은 많은 기능들을 동일하게 제공한다.

 iBatis는 어떤한 데이터 모델과 객체모델의 조합도 사용할 수 있다. 이들을 어떻게 설계 할지에 대한 제약이나 규칙 같은 것은 거의 없다.

 

 

-SQL의 외부 저장과 캡슐화

  이 두 개념이 합쳐져서 iBatis가 이룩한 많은 가치와 고급기능들을 제공해준다.

 

외부로 뺀 SQL

  사용자 인터페이스 설계, 애플리케이션 프로그래밍 그리고 데이터베이스 관리 같은 서로 다른 프로그래밍 역할에 따라 다뤄지는 사항들은 서로 분리하는 것이 좋다. 이렇게 분리하면 시스템의 특정 부분에 집중할 수 있는 잘 계층화된 설계를 얻을 수 있다. 만약 SQL을 자바 소스 코드에 집어넣으면 데이터베이스 관리자나 혹은 .NET개발자들이 동일한 데이터베이스에서 작업할 경우 불편하게 된다. 외부로 뺀 SQL을 애플리케이션 소스 코드로부터 분리하여 둘 모두를 명확하게 유지할 수 있게 한다. 그렇게 함으로써 SQL은 상대적으로 특정 언어나 플랫폼에 독립적인 상태가 된다.

 

SELECT

   PRODUCTID,

   NAME,

   DESCRIPTION,

   CATEGORY

FROM PRODUCT

WHERECATEGORY = #categoryId#

 

기존 SQL과 가장 다른 점은 파라미터 #categoryId#의 형태가 특정 언어에 종속적인 세부사항에 따라 달라지는 점이다. iBatis는 이를 더 이식성 좋고 읽기 쉽게 해준다.

 

캡슐화된 SQL

  오래된 개념 중 하나로 모듈화가 있다. 캡슐화는 코드를 응집성 있는 모듈로 조직하는 것뿐만 아니라 또한 세부적인 구현을 숨기고 호출하는 코드에게 인터페이스만을 노출시키는 모듈화의 한 형태이다.

  이러한 개념은 퍼시스턴스 계층으로도 확대 적용할 수 있다. SQL의 입력과 출력을 정의하고 애플리케이션의 다른 부분으로부터 SQL 코드를 숨겨서 SQL을 캡슐화할 수 있다. 객체지향 소프트웨어 개발자라면 인터페이스를 구현으로부터 분리하는 것과 같은 방식이라고 보면 된다. SQL 개발자라면 저장 프로시저 안에 SQL을 숨기는 것이 캡슐화라고 생각하면 된다.

  iBatis는 SQL을 캡슐화하기 위해 XML을 사용한다. XML을 선택한 까닭은 일반적으로 모든 플랫폼에 이식가능하고, 산업 전반에 걸쳐 채용되었기 때문이다.

  iBatis는 XML을 사용해서 SQL 구문의 입력과 출력을 정의한다. iBatis는 파라미터와 실행 결과를 객체의 프로퍼티로 매핑하게 한다.

 

<select id="categoryById"

   parameterClass="string" resultClass="category">

          SELEC PRODUCTID, NAME, DESCRIPTION, CATEGORY

          FROM PRODUCT

          WHERE CATEGORY = #categoryId#

</select>

 

  SQL이 XML 요소로 둘러싸여 있다. 이것이 SQL을 캡슐화한 것으로 단순한 <select>요소가 SQL 구문의 이름과 파라미터 입력 타입과 결과 출력 타입을 정의하고 있다. SQL을 외부로 빼고 캡슐화함으로써 간결함과 일관성을 모두 이루어냈다.

posted by 방랑군 2012. 1. 13. 01:07


참조 : http://msdn.microsoft.com/ko-kr/library/ms228112.aspx

'PP > SPRING.NET' 카테고리의 다른 글

Spring .Net 활용 가이드  (0) 2012.01.12
DI  (0) 2012.01.10
서론.  (0) 2012.01.10
posted by 방랑군 2012. 1. 12. 20:06



Spring .Net 프레임워크 활용 가이드입니다

  • 애플리케이션 프레임워크
  • Spring.NET 프레임워크
  • IoC 컨테이너와 DI
  • AOP (Aspect-Oriented Programming)

좋은 자료들을 틈나는대로 업데이트 할 예정입니다

많은 관심 부탁드립니다^^

'PP > SPRING.NET' 카테고리의 다른 글

일반 구성 설정(ASP.NET)  (0) 2012.01.13
DI  (0) 2012.01.10
서론.  (0) 2012.01.10
posted by 방랑군 2012. 1. 10. 03:15

안녕하세요. 코난 김대우 입니다.

이 개발 프레임워크 관련 내용은 http://kingcrap.com 블로그 장현춘님의 허락을 받고 가져온 글이며, 모든 저작권은 장현춘님께 있습니다. 
원본 출처 : http://kingcrap.com/entry/Framework-based-Development-오픈-소스-적용-사례

 

 

 

지난 시간에는 프레임웍 기반 개발이란 어떤 의미가 있으며 이때 사용할 수 있는 범용 개발 프레임웍은 어떤 것들이 시장에 나와 있는지에 대해 살펴보았다.

 

Framework-based Development – 개념 
Framework-based Development – 적용 
Framework-based Development – 종류 
Framework-based Development - 오픈 소스 적용 사례

 

이번 시간에는 주제를 좁혀서 범용 개발 프레임웍 중에서 닷넷 개발자 생태계 내에서 성장하고 발전해가는 오픈 소스 프레임웍의 적용 사례를 살펴보고자 한다. 오픈 소스 프레임웍들을 실제 적용하여 비지니스를 하고 있는 사례들을 살펴봄으로해서 닷넷 개발자 생태계의 건강성을 확인하고 닷넷 오픈 소스 프레임웍의 현재와 미래를 가늠해보는 시간이 되었으면 한다.

 

대부분의 SI 업체들은 SI 프로젝트에서 공통적으로 사용할 표준 개발 프레임웍이라는 것을 가지고 있는데, 대부분 오픈 소스 기반의 범용 프레임웍들을 조합하여 기본적인 기능을 제공하고 이를 기반으로 자사가 가지고 있는 도메인 특화된 노하우를 올리거나 일반 범용 프로임웍이 제공하지 못하는 모니터링이나 툴 최적화 기능 등을 묶어 제공하는 경우가 일반적이다. 아래 그림에서 보듯이 범용 프레임웍들의 조합을 통해 시장에서 입증된 QoS (품질요소)를 제공하고 그 기반위에 고객에게 전달할 가치를 부가적으로 제공하게 된다. 고객에게 전달할 가치가 범용 프레임웍만으로 끝난다면, 이것만 가지고 고객에게 어필(별도의 비용 청구 등)할 수 없을 것이다. 혹은 특정 회사가 개발하여 제공하는 프레임웍이라는 것이 시장의 범용 프레임웍들의 조합만으로도 제공할 수 있는 가치를 전달한다면 고객에게 외면 받을 것은 자명하다.

 

clip_image001_c2817416-14d2-4e0f-94bc-53c5ddef6b13.png

 

국내외를 막론하고 기업이 오픈 소스 기반의 프레임웍을 사용한 경우 일반적으로 그 사실을 외부에 알리는 것을 꺼려하는 경우가 있다. 이런 이유로 Spring.NET의 해외 적용 사례를 보더라도 유럽의 굴지의 온라인 여행사라든가, 어떤 도메인의 유명한 기업 정도만 밝히는 경우가 허다하다. 국내에도 마찬가지여서 외부로 드러나는 적용사례는 극히 드물다. 오늘 소개할 사례를 이전 DevDays 2008과 Open & Interop Day에서 적용 사례 발표를 했던 기업들이다.

 

아래는 이랜드 시스템의 표준 개발 프레임웍에 적용되어 있는 오픈 소스 프레임웍에 대한 내용이며, 황용호 팀장께서 발표하신 슬라이드에서 가져왔다. 이랜드 시스템의 Formular#은 Spring.NET + NHibernate의 조합을 통해 기본 기능을 제공하고 비지니스 특성상 북경 개발 센터와의 원활한 협업을 위해 사용되는 툴들에 대한 add-on 모듈이라든지, 테스팅 자동화와 같은 기능을 제공하고 있다.

 

clip_image002_e70f020d-4f20-4d06-aef8-756326b48961.png

 

예스24에서는 iBATIS.net을 현재 운영 중인 쇼핑몰에 적용한 사례를 최만석 팀장께서 발표하였으며 아래는 그 일부이다.

 

clip_image003_b1d95859-6b74-4086-958a-db9cf23d6b44.png

 

SK C&C에서는 개발 프레임웍인 NEXCORE.NET에 적용된 오픈 소스 프레임웍에 대해 설명을 하셨으며 아래는 이진우 대리께서 발표한 자료에서 발췌했다. NEXCORE.NET은 ASP.NET MVC + Spring.NET + iBATIS.NET 혹은 Entity Framework 등으로 구성되어 있으며 Visual Studio에 통합된 자동화가 인상 깊은 프레임웍이다.

 

clip_image004_bac7af48-1151-41ed-a3ad-9a1cdb315909.png

위에서 언급된 세가지 사례는 발표 자료와 함께 동영상 자료가 DevDays 2008에 게시되어 있으므로 좀 더 상세한 내용을 원하시는 분은 DevDays 2008 싸이트를 참고하시길...

 

그 밖에도 국내 또 다른 SI 업체의 개발 프레임웍에 ASP.NET MVC가 사용되고 있으며, 닷넷 솔루션 파트너사의 일부 솔루션에도 NHibernate가 사용되고 있음을 확인할 수 있었다.

 

마지막으로, 오픈 소스 프레임웍들을 적용하여 사업을 할 경우에 이들에 강제하고 있는 라이선스 정책에 대해서도 관심을 가질 필요가 있다. 오픈 소스는 곧 무료이기에 내 맘대로 가져다 고쳐 쓰면 되지..라는 잘못된 생각에 빠져 마냥 가져다 쓰다가 저작권 위반으로 소송으로 가는 경우가 심심찮게 보고되고 있다. 위에서 언급된 것들 중 가장 많이 사용되는 프레임웍의 라이선스 정책을 보면, Spring.NET과 iBATIS.NET이 Apache License 2.0을 채택하고 있으며, NHibernate가 LGPL을 채택하고 있다. 즉, 이것들 모두 상용 제품 만드는데 사용할 수 있으나 NHibernate가 채택한 LGPL의 경우에는 좀 더 엄격하여, 가져다가 소스를 수정했으면 NHibernate 관련 부분의 소스를 공개하여야 하는 제약이 있다. 물론 이 보다 더 엄격하여 GPL의 경우, 오픈 소스 소프트웨어를 가져다 쓰면, 이와 링크 형태로 엮일 지라도 메모리 상의 영역을 함께 쓰는 모든 소프트웨어의 소스 코드 전부를 공개해야하며 상용 소프트웨어와의 결합을 금지하고 있다. 다만, 이 경우에도 배포만 하지 않고 웹 싸이트에서 서비스 형태로 제공된다면 설령 GPL일 지라도 공개할 의무는 없다.

각종 오픈 소스 소프트웨어의 라이선스 정책에 대한 간단한 설명과 비교자료를 원하시는 분은 예전 정통부와 컴퓨터프로그램보호위원회가 함께 펴 낸 오픈 소스 SW 라이선스 가이드를 참고하시길...

 

아울러 알려지지 않았지만, 시장에서 오픈 소스 프레임웍을 적용하여 프로젝트가 진행되었거나 진행되고 있는 곳이 있다면 가능한 선에서 그 정보를 댓글이나 메일을 통해 공유하여, 오픈 소스를 적용하고자하나 용기가 없어 주저하고 계신 분들께 힘을 실어주시길....

 

[참고링크]

Framework-based Development - 개념

Framework-based Development - 적용

Framework-based Development - 종류

Framework-based Development - 오픈 소스 적용 사례

'PP > Framework' 카테고리의 다른 글

Framework-based Development - 종류  (0) 2012.01.10
Framework-based Development - 적용  (0) 2012.01.10
Framework-based Development - 개념  (0) 2012.01.10
닷넷 관련 개발 프레임워크  (0) 2012.01.10
자료들..  (0) 2012.01.04
posted by 방랑군 2012. 1. 10. 03:14

지난 포스팅에서는 프레임웍 기반 개발이란 무엇인가 살펴보고, 닷넷 개발 생태계 측면에서 오픈 소스 기반의 프레임웍이 제공하는 가치에 대해 알아 보았다. 또한 이후 포스팅에서는 프레임웍 기반 개발의 장점과 적용시 유의사항에 대해 살펴보았다.

Framework-based Development – 개념
Framework-based Development – 적용
Framework-based Development – 종류
Framework-based Development - 오픈 소스 적용 사례

이번 시간에는 프레임웍 기반 개발시 적용할 수 있는 오픈 및 오픈 소스 프레임웍들에는 어떠한 것들이 있는지 살펴보기로 한다.

통상적인 웹 기반 애플리케이션을 개발할 경우 Presentation-Business-Data Tier라는, 일반적으로 얘기하는 3 Tier 아키텍처를 갖게 되는데, 각 Tier에서 사용할 수 있는 범용 개발 프레임웍으로는 아래와 같은 것들이 있다. (필자는 logical은 layer, physical은 tier라는 구분을 좋아하지 않는다. 인접하는 컴포넌트 사이에 수평적으로 서비스를 제공하고 주고 받는 사이를 tier, 한 tier내에서 수직적인 구조를 layer라고 구분하는 것을 좋아한다.)

전통적으로 프리젠테이션 티어 구현 기술로는 ASP.NET이 있었고 이는 널리 알려진 바대로 Page Controller를 구현한 일종의 MVC 프레임웍이다. 프리젠테이션 티어의 가장 큰 관심사는 사용자의 요구를 어떻게 효과적으로 처리할 것인가에 관한 것으로 MVC가 그 핵심이다. 엔터프라이즈 솔루션 아키텍처 디자인 패턴이라는 p&p가 제공하는 가이드에 Page Controller의 최상위 클래스가 복잡한 분기문으로 가득차게 되면 Front Controller를 고민하라고 했듯이, 일반적으로 Front Controller는 Page Controller 보다 좀더 복잡한 애플리케이션 구축에 활용하는 패턴이다. Front Controller 패턴으로 구현한 ASP.NET 기반의 MVC 프레임웍이 바로 ASP.NET MVC 프레임웍이며 현재 베타상태이다. ASP.NET MVC 이전에 시장에는 Castle Project에서 오픈 소스 기반으로 MonoRail을 만들어 배포하고 있었으며, 자바에서 포팅된 Maverick.NET이라는 것도 있었다. 그외 MVC의 View 부분을 치환할 수 있는 다양한 엔진들이 등장하였는데, 그 중에는 자바 template engine인 Velocity의 닷넷 버전인 NVelocity, MonoRail을 위한 View Engine인 Brail, Rails의 view engine으로 널리 쓰이는 Haml의 닷넷 버전인 NHaml 등이 있다. 향후 프리젠테이션 티어의 구현 기술로 가장 주목을 받을 것은 ASP.NET MVC임에는 이론의 여지가 없어 보인다.

비지니스 티어의 가장 큰 관심사는 객체 라이프싸이클 관리이다. 사용자의 요구에 대해서 비지니스 도메인을 구현한 객체들을 얼마나 효과적으로 생성하고 관리하며, 프레임웍과 도메인 구현 엔티티 사이의 의존성을 줄여 향후 유지보수 측면에서 IT 부서의 부담을 줄여 줄 수 있는지가 관심사이며 여기에 핵심 키워드는 Dependency Injection, AOP 라 볼 수 있다. 전통적으로 일반 닷넷 객체 (PONO – Plain Old NET Object)로 원하는 비지니스 기능을 구현했으며 이때는 닷넷 프레임웍이 제공하는 기본적인 객체 생성 및 GC 매커니즘에 의존하고 개발자의 스킬에 의해 메모리 관리 등을 처리하였다. 하지만, 근래에 DI 기능을 기본으로 탑재한 컨테이너들이 수도 없이 출현하였고 가장 대표적인 것들이 위 그림에 나와 있다. Unity는 마이크로소프트 P&P 팀이 제공하는 것으로 Enterprise Library의 객체 라이프싸이클 관리 기능을 제공하는 ObjectBuilder의 Wrapper 클래스들로 구현되어 있으며 현재 Silverlight 지원하는 버전까지 제공되고 있다. 그 밖에 자바 진영의 대표적인 DI 컨테이너인 Spring이 포팅된 Spring.NET이 많은 개발자의 관심을 끌고 있으며 실제로 국내외 닷넷 프로젝트에 적극 활용되고 있다. DI 기본 기능에 프리젠테이션 티어와 데이타 티어의 각종 프레임웍에 대한 편리한 Wrapper 기능을 제공하는 것으로 정평이 나 있다. StructureMap은 Jeremy D. Miller가  만들어 유지보수하고 있으며 닷넷 진영의 가장 오래된 DI 컨테이너이다. Windsor 컨테이너는 Castle Project의 산물이며, NInject는 빠른 DI 기능 제공을 내세우며 근래에 등장한 컨테이너로서 실버라이트 및 .NET Compact Framework 지원 버전까지 제공한다. 많은 비지니스 티어 관련 프레임웍 중에서 전 세계적으로 가장 많이 사용되고 있는 것은 StructureMap으로 알려져 있으나 향후에는 Unity 아니면 Spring.NET이 대세가 되지 않을까하고 조심스럽게 점쳐 본다. 이유는 Enterprise Library의 DI 기능은 위에서 언급된 DI 컨테이너들 거의 모두를 사용하여 제공될 수 있으나 기본적으로는 Unity가 포함되어 있다. 따라서 이전부터 Enterprise Library를 써 오면서 ObjectBuilder에 익숙한 사람들은 많은 고민없이 Unity를 선택할 것으로 판단된다. Unity (Objectbuilder)는 시장에서 어느 정도 검증되었고 피드백도 좋다. Spring.NET은 자바 진영의 de-facto standard DI 컨테이너이며 이의 명성을 바탕으로 닷넷 진영에서도 빠르게 세를 넓히고 있다.

데이터 티어는 기본적으로 ORM (Object-Relational Mapping)을 제공하며 이 티어에는 Object와 RDB 사이의 Impedence Mismatch를 해결하는 다양한 기술들이 제공되고 있다. 전통적으로는 ADO.NET API를 사용하여 DAO 패턴을 적용하여 ORM을 처리하였고 지금도 가장 많이 쓰이고 있는 방식이 아닐까 생각된다. .NET Framework 3.5부터 등장한 LINQ (to SQL, Entity, Object, XML)는 개발자들로 하여금 SQL에 대한 두려움을 어느 정도 상쇄시켜 줄 수 있는 획기적인 DSL 방식의 해결책이다. Entity Framework은 마이크로소프트가 내놓은 진정한 의미의 ORM이라고 할 수 있으며 객체와 데이터베이스 테이블 사이의 다대다 매핑을 지원하며 둘 사이에 의존성을 갖지 않고 독자적으로 변경을 가할 수 있도록 고안된 추상화 레벨을 한 단계 끌어올린 ORM이다. NHibernate와 iBATIS.net은 자바 진영의 ORM 시장을 양분하고 있는 오픈 소스 기반의 대표적인 ORM 툴이며 이것들의 닷넷 버전이다. NHibernate와 iBATIS.net은 현재 국내외 프로젝트에서 주목을 받으며 적용된 사례가 있으며, 각각 고유한 장점이 있기에 적용하고자 하는 필요에 가장 잘 부합하는 것을 선택하는 것이 필요하다. SubSonic은 Rob Coney가 만든 오픈 소스 ORM으로 Rails의 scaffolding 기능까지 제공하고 있다. 또한 SubSonic은 .NET Framework 3.5 SP1에서 제공되는 ASP.NET Dynamic Data에 Scaffolding 기능을 제공하며 .NET Framework에 포함되었다. 하지만 여전히 SubSonic은 오픈 소스로 제공되고 있다. ActiveRecord는 역시 Castle Project의 ORM 관련 산물이다. ORM  툴들은 여기 예시된 것 이외에 상용을 포함하여 훨씬 많은 것들이 시장에 나와 있으며, 이 많은 프레임웍 중에서 LINQ, NHibernate, iBATIS.NET이 가장 주목을 많이 받을 것으로 기대된다. LINQ to NHibernate도 이미 시장에 나와 있듯이 핵심 기능은 NHibernate나 iBATIS.net을 쓰되 개발자 인터페이스 부분은 LINQ가 담당할 가능성도 있다.

참고로, Castle Project를 진행했던 Hamilton Verissimo와 SubSonic을 만든 Rob Conery 모두 마이크로소프트 직원이 되었지만, 현재 이들은 여전히 오픈 소스 프레임웍 개발에 전념하고 있다.

정리하면 다음과 같은 조합이 가장 현실적인 대안이 되지 않을까 한다. 
ASP.NET MVC + { Unity, Spring.NET } + { LINQ, NHibernate, iBATIS.net }

이들 프레임웍들은 서로의 존재를 모르고 시장에 나온 경우가 많다. 따라서 이들 프레임웍들을 효과적으로 연동하기 위한 약간의 수고 필요한데, 대부분의 경우 특히 유명한 프레임웍들은 서로에 대해 연동하는 Wrapper를 자체적으로 제공하거나 MVCContrib과 같이 별도의 프로젝트에서 인접 티어의 프레임웍 연동 기능을 제공하고 있다. 예를 들면, MVCContrib은 ASP.NET MVC와 인접한 다양한 DI 컨테이너들 즉, Unity, Spring.NET, Windsor 등에 대한 연동 모듈을 제공하고 있으며, Spring.NET 자체적으로는 NHibernate에 대한 Wrapper를 제공하고 있다.

시장에서 가장 많이 사용하는 범용 프레임웍들을 조합하여 하나의 예로서 Billy McCafferty가 제시하는 것으로 Sharp Architecture가 있다.

그림에서 보듯이 Billy는 다양한 시도를 진행했다. 처음에 ASP.NET MVC + Spring.NET + NHibernate의 조합을 제시했다가 DI 기능만을 위해 Spring.NET을 쓰는 것이 비효율적이라 생각되어 Spring.NET을 빼고 DI 기능을 손수 PONO 형태로 구현했었다. 이후 NInject라는 가벼운 DI 컨테이너를 다시 도입한 빌드를 선보였고, 근래에는 NInject 대신에 Windsor 컨테이너를 조합한 빌드를 제시하고 있다. 따라서 Sharp Architecture 싸이트에 가보면 다양한 조합의 빌드를 다운받아 테스트해 볼 수 있다.

여기에서 언급된 다양한 프레임웍 및 관련 자료에 대한 링크는 다음과 같다.

마이크로소프트 제공

Patterns & Practices

http://www.microsoft.com/practices

CodePlex

http://www.codeplex.com

ASP.NET MVC

http://www.asp.net/mvc

ASP.NET MVC Contrib

http://www.codeplex.com/MVCContrib

Scott Guthrie Blog

http://weblogs.asp.net/scottgu/

Unity Application Block

http://www.codeplex.com/unity

Enterprise Library

http://www.codeplex.com/entlib

 오픈 소스 프레임워크

Sharp Architecture

http://code.google.com/p/sharp-architecture/

Castle Project (MonoRail, Windsor, ActiveRecord)

http://www.castleproject.org/

Ninject (DI)

http://ninject.org

Spring.NET (DI)

http://springframework.net

StructureMap (DI)

http://structuremap.sourceforge.net/Default.htm

S2Container.NET (DI)

http://s2container.net.seasar.org/en/index.html

autofac (DI)

http://code.google.com/p/autofac/

compactcontainer (DI for compact framework)

http://code.google.com/p/compactcontainer/

LinFu (DI)

http://code.google.com/p/linfu/

NHibernate (ORM)

http://www.hibernate.org/343.html

iBATIS.NET (ORM)

http://ibatis.apache.org/

SubSonic (ORM)

http://subsonicproject.com

'PP > Framework' 카테고리의 다른 글

Framework-based Development - 오픈 소스 적용 사례  (0) 2012.01.10
Framework-based Development - 적용  (0) 2012.01.10
Framework-based Development - 개념  (0) 2012.01.10
닷넷 관련 개발 프레임워크  (0) 2012.01.10
자료들..  (0) 2012.01.04