본문 바로가기
네트워크

윈도우 네트워크 프로그래밍

by 상레알 2010. 12. 28.

출처 : http://cafe.naver.com/pplus
출처 : http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/win_network_prog/doc/winsock_basic

종단 시스템 (end-system) : 최종 사용자(end-user)를 위한 애플리케이션ㅇ르 수행하는 주체

라우터 (router) : 종단 시스템이 속한 네트워크와 다른 네트워크를 연결함으로써 서로  다른 네트워크에 속한 종단 시스템
                       끼리 상호 데이터를 교환 할 수 있도록 하는 장비

프로토콜(protocol) : 종단 시스템과 라우터간, 라우터와 라우터간, 그리고 종단 시스템과 종단 시스템간 통신을 수행하기 
                             위해  정해진 절차와 방법

TCP/IP 프로토콜 : 인터넷의 핵심 프로토콜인 TCP 와 IP를 포함한 각종 프로토콜 
                          운영체제에서 구현을 제공하며, 이반 애플리케이션은 운영체제가 제공하는 TCP/IP 프로토콜의 서비스를
                          사용하여 통신을 수행

TCP/IP 프로토콜 구조

-  계층적 구조 

  ---------------------
 |   애플리케이션 계층   | 
  ---------------------
 |          전송 계층        |       TCP, UDP
  ---------------------
 |        인터넷 계층       |       IP
  ---------------------
 | 네트워크 액세스 계층 |       디바이스 드라이버, 네트워크 하드웨어
  ---------------------

ㅁ 네트워크 액세스 계층 (network access layer)

   - 역할 : 물리적 네트워크를 통한 실제적인 데이터 전소
   - 구성 요소 : 네트워크 +  디바이스 드라이버
   - 주소 지정 방식 : 물리 주소(physical address)
   ex) 이더넷 (Ethernet)

ㅁ인터넷 계층(Internet layer)
   - 역할 : 네트워크 액세스 계층의 도움을 받아, 전송 계층이 내려 보낸 데이터를 종단 시스템 까지 전달
   - 구성 요소 : 논리 주소 + 라우팅
   - 주소 지정 방식 : IP 주소 (Internet Protocol address)
   - 라우팅 : 목적지까지 데이터를 전달하기 위한 일련의 작업   
                   - 라우팅을 위한 정보 획득
                   - 라우팅 정보를 기초로 실제 데이터 전달 (forward)
ㅁ 전송계층 ( transport layer)
    - 역할 : 최종적인 토신 목적지(프로세스)를 지정하고, 오류없이 데이터를 전송
    - 주소 지정 방식 : 포트 번호 (port number)
    ex) TCP (Transmission Control Protocol) , UDP( User Datagram Protocol)

 TCP  UDP
 연결형(connection-oriented)프로토콜 : 연결이 성공해야 통신 가능  비연결형(connectionless) 프로토콜
: 연결없이 통신가능
 데이터 경계를 구분하지 않음
- 바이트 스트림(byte-stream) 서비스
데이터 경계를 구분함
- 데이터그램(datagram) 서비스
 신뢰성있는 데이터 전송
 -데이터를 재전송함
비신뢰적인 데이터 전송
- 데이터를 재전송하지 않음 
  1 대 1 통신 (unicast)  1 대 1 통신 (unicast),
 1 대 다 통신 (broadcast)
 다 대 다 통신 ( munticast) 

ㅁ 애플리케이션 계층 (application layer)
    - 역할 : 전송 계층을 기반으로 한 다수의 프로토콜과 이 프로토콜을 이용하는 애플리케이션을 포괄
                다양한 애플리케이션 서비스 제공
     ex) Telnet, FTP, HTTP, SMTP 등


윈도우 소켓 (Windows Sockets, Winsock)

윈속(winsock)은 Windows Socket API의 줄임말로 윈도 운영체제 기반의 네트워크 프로그램을 개발하기 위한 기술요소들이 적용된 개발 도구이다.
버클리 유닉스(Berkeley Software Distribution UNIX)에서 개발한 네트워크 프로그래밍 인터페이스를 윈도우 환경에
서 사용할 수 있도록 한것으로 소크 코드 수준에서 호환성이 높고 리눅스 환경에서 네트워크 프로그래밍 경험이 있다면 어렵지 않게 적응 할 수 있다. 대부분의 소켓 함수도 그대로 사용하고 있다.

윈도웅 95버전부터 API(Application Programming Interface)에 정식으로 포함되어 제공

유닉스 소켓과의 차이점

-  dll 로드 :  winsock.dll을 로딩해야 한다. 윈도우 소켓은 DLL을 통해 대부분의 기능이 제공되므로 DLL 초기화와 종료
                  작업을 위한 함수가 필요
- 윈도우 애플리케이션은 대개 그래픽 사용자 인터페이스(GUI, Graphical User Interface)를 기반으로 하며,
   메시지 구동 방시으로 동작하므로 이를 위한 확장 함수가 존재
- 윈도우는 운영체제 차원에서 멀티스레드(multithread)를 지원하므로 멀티 스레드 환경에서 안정적으로 동작하기
  위한 구조와 이를 위한 함수가 필요
- 소켓 지정번호 : 리눅스 소켓 지정 번호가 곧 파일 지정 번호로 자료형은 signed int이다. 윈속은 소켓을 가리키기
                         위해서 SOCKET을 사용하는데, insigned int 형이다. 그러므로 포팅 작업을 쉽게 하기 위해서 int
                         형을 사용해도 큰 문제는 없다.  대신 컴파일러의 종류에 따라서 경로 메시지를 출력할 수도 있다.
                         가능하면 SOCKET 자료형을 그대로 사용하는게 좋을 것 같다.

장점

- 유닉스 소켓과 소스 코드 수준에서호환성이 높으므로 기존 프로그램을 포팅하기가 쉬움
- 가장 널리쓰이는 네트워크 프로그래밍 인터페이스이므로 한번 배워두면 여러 환경(윈도우, 유닉스...)에서 사용할
   수 있음
- TCP/IP 이외에도 다양한 종류의 프로토콜을 지원하므로 최소한의 코드 수정으로 애플리케이션에서 사용할 프로토콜ㅇ르
  변경 가능
- 비교적 저수준(low-level 혹은 mid-level)의 프로그래밍 인터페이스로, 세부적인 제어가 가능하므로 고성능의 네트
  워크 애플리케이션을 개발할 수 있음

단점

- 애플리케이션 수준의 프로토콜을 프로그래머가 직접 설계해야 함
   -데이터 포맷이나 전송 절차 등을 고려하여 프로그래밍해야 하므로 프로토콜을 변경할 경우 코드 수정 불가피
- 서로 다른 바이트 정렬(byte ordering) 방식으 사용하거나 데이터 처리 단위(32비트, 64 비트,....)가 서로 다른
   종단 시스템간 통신을 할 경우 애플리케이션 수준에서 데이터 변환을 처리해야함


구조

  


윈속 라이브러리 추가




윈속 네트워크 프로그래밍

BSD 소켓고 동일해서 리눅스 네트워크 프로그래밍과 만은 부분에서 중복되긴 하지만 그래도 다루어볼 생각이다.
소켓 api 문서는 BSD 소켓 API문서를 참고하면 된다. 윈속 레퍼런스 문서는 아니지만 문제 없을 것이다.
윈속 함수 대부분이 BSD 소켓함수와 동일하므로, 함수 설명은 비교적 간단히 넘어갈 것이다.

참고
) http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/Network_Programing/Documents/SocketAPI



소켓에 대해서


소켓은 인터넷(인터 네트워크) 상에서 물리적으로 떨어져 있는 소프트웨어와 소프트웨어 간의 통신을
담당하는 " 소프트웨어 통신 도구"다. 때때로 소켓은 컴퓨터와 컴퓨터를 연결하는 통신 도구라고 설명되
기도 하지만, 정확히 언급할 필요가 있을거 같다. 소켓은 인터넷의 종단에 위치하면서 소프트웨어와
소프트웨어를 연결한다.
웹 서비스를 예로 들어보자면, 소켓은 클라이언트인 firefox와 서버인 apache가 서로 통신을 할 수 있도록
연결을 한다. 소켓을 이용해서 firefox는 apache서버로 웹페이즈 요청을 하고 apache 서버는 요청을 분석
해서 웹페이지를 firefox에 전송한다.

소켓 자료형

유닉스는 모든 자원을 파일로 다룬다. 소켓 역시 예외는 아니라 파일로 다룬다. 그리고 모든 파일 관련
하수는 signed int 형 파일 지정번호를 이용해서 파일을 제어한다.

이러한 유닉스의 특성으로 BSD 소켓도 int형의 파일 지정 번호를 이용해서 소켓을 다룬다. 유닉스에서
파일 지정 번호는 소켓을 지시한다.

윈도는 자원을 파일이 아닌 각각의 독립된 커널 객체로 보며, 이들 객체는 파일 지정 번호가 아닌
HANDLE (핸들)이용 해서 제어한다. 예컨데, 객체의 인스턴스를 다루는 방식이다. 이런 윈도의 특성
상 소켓 역시 소켓 핸들로 다루어야 겠지만, BSD 소켓과의 호환을 유지하기 위해 unsigned int를 재
정의한(소켓 지정 번호) SOCK으로 소켓을 다룬다. 소켓을 지시하기 때문에 소켓 지시자라고 부른다.
기본적으로 윈도는 파일과 소켓을 다른 객체로 본다.


유닉스와 비교하면 signed 지정에 대한 차이만 있기 때문에, 유닉스로의 포팅을 고려한다면 SOCK
대신 signed int 를 써도 무방하다고 할 수 있다. 다만 윈도에서 컴파일 할 경우, 컴파일러의 종류에 따라서
경고 메시지가 발생할 수는 있다.

소켓 만들기

통신을 하기 위해서는 socket 함수로 인터넷의 종단에 위치할 소켓을 만들어야 한다.
--------------------------------
    SOCKET WSAAPI socket (
    __in int af,
    __in int type,
    __in int protocol
);
--------------------------------

af :주소 영역을 지정하기 위해서 사용한다. 예컨데, 일반 인터넷 영역에서 통신하 건지, 아니면 IPX영역에서 통신
할건지 등을 결정하기 위해서 사용한다. 유별난 경우가 아니라면 인터넷 영역 통신을 위해서 AF_INET를 사용한다.
유닉스는 소켓을 IPC(내부 프로세스 통신) 용도로 사용을 할 수 있다.이를 위해서 AF_UNIX를 사용할 수 있는데,
윈속은 내부 통신 매커니즘을 지원하지 않는다.

type :소켓 타입을 지정하기 위해서 사용한다. 연결 지향의 TCP 통신이라면 SOCK_STREAM, UDP
를 이용한 데이터 그램 중심의 비 연결지향 통신이라면 SOCK_DGRAM을 사용한다.

protocol : 소켓이 사용할 프로토콜을 지정한다. type와 함께 쌍으로 지정한다. SOCK_STREAM 이라면
IPPROTO_TCP, SOCK_DGRAM이라면 IPPROTO_UDP를 사용한다.
연결지향의 인터넷 소켓을 사용할 거라면 다음과 같이 지정한다.
즉, 연결지향의 인터넷 소켓을 사용할 거람녀 다음과 같이 지정한다.

------------------------------------------------------------
    SOCKET sock;
    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
------------------------------------------------------------

소켓 특성 묶기

socket함수로 만든 소켓이 접점에 놓이기 전에, 소켓 특성을 묶어야 한다. 즉, 어떤 인터넷 주소 영역에 대해서 어떤
포트로 기다릴건지에 대해 정보를 소켓에 지정해 줘야한다. bind함수로 이 일을 할 수 있다.
------------------------------------------------------------
int bind(
  __in SOCKET s,
  __in const struct sockaddr *name,
  __in int namelen
);
------------------------------------------------------------
소켓 지시자 s에 sockaddr 구조체의 정보를 지정한다. struct sockaddr 구조체는 다음과 같다.
------------------------------------------------------------
struct  sockaddr {
              ushort  sa_family;
              char      sa_data[14];
};

struct sockaddr_in {
                short   sin_family;
                u_short  sin_port;
                struct in_addr sin_addr;
                char sin_zero[8];
};
------------------------------------------------------------
sin_family오 sa_family는 서로 도일한 값으로 어떤 주소체계를 사용할 것인지를 명시한다.  sa_family의 값에
따라서 sa_data에 대한 해석이 달라진다. 만약 sa_family가 AF_INET라면 인터넷 주소 체계 정보를 
가지는 sockaddr_in 구조체가 사용된다. sockaddr_in 구조체에는 포트, 사용할 인터넷 주소 정보를 포함학 있다.
------------------------------------------------------------
struct sockaddr_in SockInfo;
    ....          .....
SockInfo.sin_family = AF_INET;
SockInfo.sin_port = htons( 1234 );
SockInfo.sin_addr.s_addr = htonl( INADDR_ANY );

status = bind( EndpointSocket, (struct sockaddr*)&SockInfo, sizeof( struct sockaddr_in) ); 
if( status == SOCKT_ERROR )
{
    printf("Bind Error \n");
    return 0;
}
------------------------------------------------------------
INADDR_ANY는 0.0.0.0 주소를 가리키는데, 모든 사용 가능한 주소로부터 기다리겠다는 의미다
주로 모든 인터넷 주로 기다려야하는 서버에서 사용하는 값이다.

bind함수는 서버 프로그래메서 주로 사용된다. 서비스 포트가 고정되어야 하는 서버 프로그램과
달리 클라이언트는포트가 임의로 할당되기 때문에 굳이 bind함수로 주소 정보를 묶어 주 필요가
없기 때문이다. bind함수를 사용하지 않을 경우, 운영체제가 알아서 포트를 할당한다.

연결 대기열 만들기

커널이 클라이언트로 부터 요청으 받으면, 우선 연결 댁열 (listen queue)로 연결이 들어간다. 프로그램은
클라이언트 연결을 직접 받는 것이 아니라, 연결 대기열의 가장 앞 부분에 있는 연결요청을 가져와서
클라이언트와이 연결을 만든다.

연결 대기열은 listen 함수로 만든다.
------------------------------------------------------------
int listen(
   __in SOCKET s,
   __in int backlog
);
------------------------------------------------------------
대기열의 크기는 backlog로 정한다. 경험값으로 정해진값은 없다. 일반적을 5정보면 무난한거 같다.

원격 소켓에 연결 하기

클라이언트는 connect 함수로 원격 소켓에 연결할 수 있다.
------------------------------------------------------------
int connect (
   __in SOCKET s,
   __in const struct sockaddr *name
   __in int namelen
);
------------------------------------------------------------
sockdr의 인터넷 주소/포트로의 연결을 시도한다.
------------------------------------------------------------
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr =inet_addr("111.111.111.111");
serveraddr.sin_port = htons(12345);

if(connect(ClientSocket, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == SOCKET_ERROR)
{
     WSACleanup();
     return 1;
}
------------------------------------------------------------
인터넷주소 111.111.111.111의 포트번호 12345에 연결된 프로그램에 연결을 시도하고 있다. 이 함수는
클라이언틍서 사용한다.

연결 요청 가져오기




윈속 함수 오류 처리

1. 오류 처리할 필요가 없는 경우
   - 리턴값이 없거나 호출시 항상 성공하는 일부 소켓 함수
2. 리턴값만으로 오류를 처리하는 경우
   - WSASartup() 함수
3. 리턴값으로 오류 발생을 확인하고, 구체적인 내용은 오류 코드를 이용하여 확인하는 경우
  - 대부분의 소켓 함수