'공부합시다/소켓프로그래밍'에 해당되는 글 9건

  1. 2009.09.18 TCP 헤더 분석 3
  2. 2009.09.16 ether_header 구조체와 iphdr 구조체를 이용한 패킷 분석
  3. 2009.09.16 바이트별로 노가다 작업을 통한 패킷 분석-ㅅ-; 1
  4. 2009.09.15 패킷 캡쳐로 살펴본 이더넷 프레임
  5. 2009.09.15 이더넷 프레임의 구조
  6. 2009.08.21 pcap_t, pcap_pkthdr 구조체
  7. 2009.07.09 [소켓프로그래밍] WSAAsyncSelect
  8. 2009.07.08 [소켓프로그래밍] setsockopt
  9. 2009.07.03 [소켓프로그래밍] winsock.h 중복 재정의 에러, error C2011: 'fd_set' : 'struct' type redefinition
2009. 9. 18. 09:03

TCP 헤더 분석

참고 : http://www.ktword.co.kr/abbr_view.php?mgid=125&m_temp1=347


TCP에 대한 자세한 내용은 위의 링크를 참조하면 된다.



1. TCP의 구조



TCP 프레임은 IP datagram에 캡슐화되어 저장되어 있다.

다른 프로토콜과 마찬가지로 헤더와 데이터부분으로 이루어져 있으며, 헤더의 길이는 옵션이 없을 경우 20바이트로 이루어져 있다.



2. 헤더 분석


    ㅇ Sourse/Destination Port number
    ㅇ Sequence Number (일련번호) 및 Acknowledgement Number (확인번호)
       - TCP에 의해 전송되는 데이터 세그먼트의 순서제어를 위해 사용됨
       - 32비트이므로 4기가 바이트 크기의 송신 데이터를 위해 일련번호를 붙일 수 있
         다. 예를들면, 일련번호 100에 100바이트를 전송하면 다음 패킷의 일련번호는
         200이 된다.
                      |      SEQ j (데이터 20)    |
                      |    -------------------->         |     
                      |           ACK j+20           |
                      |    <-------------------          |     
                      |           SEQ j+20           |
                      |    ------------------->          |          

    ㅇ Header length
       - 4 바이트 단위로 표시. 4 비트 길이. 따라서, TCP 헤더 길이는 총 60 바이트
         이하임
    ㅇ 6 개의 Flag bits     ☞ TCP 제어 플래그 참조
       - TCP 세그먼트 전달과 관련된 제어 기능을 함. TCP 회선 및 데이터 관리를 위해
         사용되는 제어용 플래그
    ㅇ window size
       - 흐름제어를 위해 사용하는 16 비트 필드 (65,535 bytes까지 가능)
       - TCP 흐름제어를 위해서  통신의 상대편에게 자신의 버퍼 크기를 지속적으로 통
         보하여주는 기능을 함. 수신측에 의해 능동적으로 흐름제어를 수행하게 됨.
    ㅇ Urgent pointer
       - TCP 세그먼트에 포함된 긴급 데이터의 마지막 바이트에 대한 일련번호
       - urgent pointer는 현재 일련번호(sequence number)로부터 긴급 데이터까지의
         바이트 오프셑(offset)을 말한다.
       - 해당 세그먼트의 일련번호에 urgent point 값을 더해 긴급 데이터의 끝을 알수
         있음



3. TCP 옵션


주요 옵션의 종류
   Type                              옵 션 내 용
   ----    -------------------------------------------------------------------
    0      End of Option          : 옵션 필드의 끝에 패딩(Padding)을 위해 사용 
    1      No Operatin            : 옵션 사이를 채우기 위함
    2      Maximum Segment Size (MSS)
            : 전송되는 TCP 데이터 세그먼트의 최대 길이, 이더넷은 1460
    3      Window Scale factor
    4      Selective Acknowledgment Permitted (Selective Reject)
            : 여러 패킷 중 손실된 패킷 만 선택적으로 재전송하기 위한 TCP 연결 설정
              시의 협상 옵션
    5      Selective Acknowledgment Data
    8      Timestamp
2009. 9. 16. 19:39

ether_header 구조체와 iphdr 구조체를 이용한 패킷 분석

이전 포스팅에서는 바이트별로 직접 골라냈었는데(-ㅅ-;) 이번엔 라이브러리에 있는 구조체를 활용해서 더 쉽게 패킷을 분석해보자.


ether_header 구조체

/usr/include/net/ethernet.h 에 위치한다. (gcc의 버전에 따라 다를 수 있다.)

/* 10Mb/s ethernet header */
struct ether_header
{
    u_int8_t  ether_dhost[ETH_ALEN];        /* destination eth addr */
    u_int8_t  ether_shost[ETH_ALEN];        /* source ether addr    */
    u_int16_t ether_type;                          /* packet type ID field */
} __attribute__ ((__packed__));



iphdr 구조체

/usr/include/netinet/ip.h 에 위치한다. (역시 gcc의 버전에 따라 다를 수 있다.)

   struct iphdr
      {
   #if __BYTE_ORDER == __LITTLE_ENDIAN
        unsigned int ihl:4;                             /* header length */
        unsigned int version:4;                       /* version */
   #elif __BYTE_ORDER == __BIG_ENDIAN
        unsigned int version:4;                       /* version */
        unsigned int ihl:4;                             /* header length */
   #else
   # error "Please fix <bits/endian.h>"
   #endif
        u_int8_t tos;                                     /* type of service */
        u_int16_t tot_len;                               /* total length */
        u_int16_t id;                                     /* identification */
        u_int16_t frag_off;                              /* fragment offset field */
        u_int8_t ttl;                                       /* time to live */
        u_int8_t protocol;                              /* protocol */
        u_int16_t check;                               /* checksum */
        u_int32_t saddr;                               /* source address */
        u_int32_t daddr;                               /* dest address */
        /*The options start here. */
      };


소스 코드



실행 화면


2009. 9. 16. 10:32

바이트별로 노가다 작업을 통한 패킷 분석-ㅅ-;

말그대로 제목과 같음

400바이트 이상의 패킷을 캡쳐했을때만 정보 표시 하도록 구현


실행화면


2009. 9. 15. 21:13

패킷 캡쳐로 살펴본 이더넷 프레임

이번 포스팅에서는 실제 패킷을 캡쳐하여 바이트 별로 분석을 해보자한다.

소스는 아래를 펼쳐보면 알 수 있다.

이전 포스팅에서 얘기했듯이 이더넷 프로토콜 타입에 대한 정보가 패킷내에 존재한다. 이것은 아래의 그림과 같은 모습의 구조를 띄고 있다.


위의 소스를 실제로 돌려봤을 때 다음과 같은 화면을 볼 수 있다. (리눅스에서 구동한 것이다.)


위의 프레임 구조에서 봤듯이 붉은 색으로 표시된 부분은 도착지의 MAC address이며, 파란색으로 표시된 부분은 출발지의 MAC address이다. 이를 ifconfig 명령어로 확인해보면 출발지의 값이 다음과 같이 동일하다는 것을 알 수 있다.


도착지 다음의 2바이트는 이더넷 타입을 나타내는 0x0800은 IP를 뜻한다.
(2009/09/15 - [소켓프로그래밍] - 이더넷 프레임의 구조 를 참조하자.)

그럼 IP 헤더에 대해 다시 알아보자.


< IP 헤더 >

1. IP 헤더 사이즈

- 헤더 사이즈는 만일 옵션 미지정시 20바이트이다.
- 즉, 최소 20 바이트란 말이며, 최대는 24바이트이다.
- IPv6의 경우는 40바이트가 최소이다.


2. IP 헤더 구성



이를 캡쳐한 화면과 비교해보자.



1. Version (4 bits)
- 0x0800 다음에 오는 4비트는 IP 버전을 뜻한다. 4 이므로 IPv4임을 알 수 있다.

2. Header Length (4 bits)
- 다음 4비트는 헤더의 길이를 뜻한다. 최소 5부터 15까지의 값을 가진다.
- 이 값을 왼쪽으로 비트 쉬프트를 2번 해준 값이 헤더의 길이가 된다.
- 위의 경우에는 5이므로 5<<2 의 값은 20 이므로 20이 된다.
- 헤더의 최대 값은 15<<2 즉, 60이 된다. 따라서 헤더의 크기 범위는 20~60.

3. Type of Service (ToS) Flag (8 bits)
- 요구되는 서비스 품질을 나타내나, 현재 대부분의 시스템에서는 이 필드를 무시한다.

4. Total Packet Length (16 bits)
- IP 패킷 전체의 길이를 바이트의 수로 나타낸다. 최대 값은 0xFFFF = 65535.
- 위의 그림에서는 0x004C = 76 bytes.

5. Fragment Identifier (16 bits)
- 각 조각이 동일한 데이터그램에 속하면 같은 일련번호를 공유한다.

6. Fragmentation Flag (3 bits) : 분열의 특성을 나타내는 플래그
- 첫번 째 bit : 미사용 (항상 0)
- 두번 째 bit : DF bit, Don't Fragment
                    분열(조각) 0, 미분열 1
                    1로 셋팅되면 목적지 컴퓨터가 조각들을 다시 모을 능력이 없기 때문에
                    라우터로 하여금 데이터그램을 단편화하지 말라는 뜻이다.
                    0으로 셋팅되면 라우터에서 분열(조각, 단편)이 가능함을 뜻한다.
- 세번 째 bit : MF bit, More Fragment
                    현재의 조각이 마지막이면 0, 더 많은 조각이 뒤에 계속 있으면 1
- 위의 그림에서는 0x4 = 0100(2) 에서 상위 3비트 이므로 010이다.
- 첫번 째 비트는 위에서 설명한바와 같이 0이고, 두번째는 1이므로 미분열이란 뜻이고,
   세번 째는 0이므로 뒤에는 더이상의 조각이 없다는 뜻이다..

7. Fragmentation offset (13 bits)
- 조각나기 전 원래의 데이터그램의 8 바이트 단위의 위치
- 위의 경우에서는 0x40 = 0100 0000(2) 에서 6번에 얘기한 상위 3비트 뒤에 오는 5비트와 그 이후의 8비트 0x00 이므로, 0 이다. (조각나지 않은 데이터 이므로 0)
※ 위 3개의 필드 (Fragment Identifier,Fragmentation Flag,Fragmentation Offset)(5, 6, 7)는 조각(분열)과 
재배열과 관련된 필드이다. 각 조각들은 최종 목적지 시스템에 전달되기 전에는 재배열되지 않고, 최종 목적지에
전달되면  목적지 시스템의 IP 소프트웨어가 원래의 데이터그램으로 재배열한다.

8. TTL, Time To Live (8 bits) - IP 패킷의 수명 - 즉, 네트워크 상에서 패킷이 남아있을 시간이다. 점점 감소하게 된다. - 위의 그림에서는 0x40 이므로 64이다.
9. Protocol Identifier (8 bits) - 어느 상위 계층 프로토콜이 데이터 내에 포함되었는가를 나타낸다. - 프로토콜의 번호들은 다음의 사이트에서 확인할 수 있다. http://www.iana.org/assignments/protocol-numbers/ - 위의 경우는 0x06 이므로,
TCP 프로토콜이란 것을 알 수 있다.

10. 헤더 검사합 checksum (16 bits) - 헤더에 대한 오류 검사를 위한 비트이다. - 위의 그림에서는 0x4540 이다.

11. source IP Address (32 bits) - 송신처 주소

12. Destination IP Address (32 bits) - 수신처 주소

13. IP 헤더 옵션 (선택옵션, 가변 길이 bits)

14. Padding (필요한 경우에만 사용, 가변 길이 bits)
2009. 9. 15. 20:05

이더넷 프레임의 구조

자료 출처 : http://www.ktword.co.kr/abbr_view.php?mgid=014&m_temp1=2965


1. 개요

Ethernet Protocol Type 이란 이더넷 패킷내의 데이터부분에서 캡슐화된 데이터가 어느 프로토콜에 해당하는지를 나타내고자,  13~14번째 바이트에 이를 표시하는 영역을 말한다.


2. 이더넷 프레임의 통상적인 형식 : IEEE 802.3 또는 DIX 2.0


- Preamble(10101...) 및 SFD(10101011) : 10101......10101011
- D A : Destination Address, S A : Source Address    ☞  MAC 주소
- Len/Type : 0x 600 이상이면 Type (DIX 2.0), 이하이면 Length (802.3) 로 해석
         . Length : 길이(3~1500 바이트)를 나타냄           ☞  MTU
         . Type   : Data에 담겨있는 상위 프로토콜          ☞  Ethertype


 - Type의 대표적인 값들 (0x600 이상의 값 만이 가능함)

     0600h                Xerox XNS IDP
    0800h            IP
     0805                  X.25
     0806h                ARP
     0835h                RARP
     6003h                DEC DECnet Phase Ⅳ
     8137h                Novell Netware IPX
     8191h                NetBIOS
     8847h                MPLS 
     8863h                PPPoE Discovery Stage
     8864h                PPPoE PPP Session Stage


4. 각 프레임의 형태



5. 참고사항

   ㅇ 위 프레임들 간의 주요한 차이는,
      MAC 부계층 바로 상위계층인 LLC 부계층 관련 부분(3 바이트)의 포함 여부에 있
      다.  (DIX Ethernet frame : 미포함, IEEE 802.3 frame : 포함)

  ㅇ 각 바이트별 비트들의 송신 순서
     - 이더넷 프레임의 각 바이트의 비트들은 FCS를 제외하고, 모두 LSB 부터 송신된다.
2009. 8. 21. 17:44

pcap_t, pcap_pkthdr 구조체

struct pcap {
    int fd;
    int snapshot;
    int linktype;
    int tzoff;              /* timezone offset */
    int offset;             /* offset for proper alignment */
   
    struct pcap_sf sf; /* save file 관련 구조체 */
    struct pcap_md md; /* 패킷의 상태에 대한 정보 */
   
                       /*
                       * Read buffer.
    */
    int bufsize;
    u_char *buffer;
    u_char *bp;
    int cc;
   
    /*
    * Place holder for pcap_next().
    */
    u_char *pkt;
   
   
    /*
    * Placeholder for filter code if bpf not in kernel.
    */
    struct bpf_program fcode;
   
    char errbuf[PCAP_ERRBUF_SIZE];
};

typedef struct pcap pcap_t;
=======================================================================================================

Each packet in the dump file is prepended with this generic header.
This gets around the problem of different headers for different
packet interfaces.

struct pcap_pkthdr {
    struct timeval ts;      /* time stamp */
    bpf_u_int32 caplen;  /* length of portion present */
    bpf_u_int32 len;       /* length this packet (off wire) */
};

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

2009. 7. 9. 11:27

[소켓프로그래밍] WSAAsyncSelect

WSAAsyncSelect

WSAAsyncSelect 함수는 지정된 소켓에 대해서 특정한 네트웍 이벤트에 대한 윈도즈 통지 메시지를 받을 것인지 않받을 것인지 설정하는 함수입니다.    - 좀 양이 많네요^^ 그래두 중요한 부분이니까 꼭 숙지 하도록 해요.

int WSAAsyncSelect (
            SOCKET   
s,
            HWND   
hWnd,
            unsigned int   
wMsg,
            long   
lEvent
);

 

Parameters

s
[입력] 이벤트 통지를 설정할 대상 소켓 기술자

hWnd
[입력] 네트웍 이벤트가 발생 했을 때 메시지를 수신할 윈도즈 핸들

wMsg
[입력] 네트웍 이벤트가 발생했을 때 수신될 메시지

lEvent
[입력] 어플리케이션이 수신할 네트웍 이벤트의 비트조합

 

Remarks

WSAAsyncSelect 함수는 WS2_32.DLL 이 IEvent 매개변수에 의해 지정된 네트웍 이벤트가 발생했을대 마다 hWnd 윈도우로 메시지를 보내야 할 때 사용됩니다. 보내야 하는 메시지는 wMsg 매개변수로 지정한 메시지가 되고, 통지받는 대상 소켓은 s 매개변수에 의해서 지정된 소켓입니다.

WSAAsyncSelect 함수는 IEvent 값에 개의치 않고, 소켓 s 를 비동기 모드로 자동적으로 설정합니다. 동기모드에서 비동기 모드로 설정하는 방법에 대해 알고 싶으면, ioctlsocket 함수를 참조하도록 하세요.

IEvent 매개변수는 아래 리스트에 보이는 값들을 OR 비트연산 해서 구성된 값으로 이루어 집니다.

Value Meaning
FD_READ 데이터 수신 통지메시지를 수신 하려할때.
FD_WRITE 데이터 전송이 준비가 되었다는 통지메시지를 수신하려 할 때
FD_OOB out-of-band 데이터가 수신되었다는 통지메시지를 수신하려 할때
FD_ACCEPT 접속 요구가 들어왔다는 통지메시지를 수신하려 할때
FD_CONNECT 접속이나, multi-point join 작업이 완료되었다는 통지메시지를 수신하려 할 때
FD_CLOSE 연결된 상대방 소켓이 종료되었다는 통지메시지를 수신하려 할 때
FD_QOS Socket Quality of Service(QOS)가 변경되었다는 메시지를 수신하려 할 때
FD_GROUP_QOS Socket Group Quality of Service(QOS)가 변경되었다는 메시지를 수신하려 할 때
FD_ROUTING
_INTERFACE_CHANGE
지정된 목적지(들)에 대해서 경로배정 인터페이스가 변경되었다는 메시지를
수신하려 할 때.
FD_ADDRESS_LIST
_CHANGE
소켓의 프로토콜 집합에 대한 로컬 어드레스 리스트가 변경되었다는 메시지를
수신하려 할 때.

간단한 사용법을 한번 알아 볼까요? 데이터의 수신과 송신 통지 메시지를 수신하기 위해서는 FD_READ 그리고, FD_WRITE를 비트연산자로 합쳐서 WSAAsyncSelect 함수를 호출하게 됩니다. 아래와 같이 말이죠.

rc = WSAAsyncSelect(s, hWnd, wMsg, FD_READ|FD_WRITE);

WSAAsyncSelect 함수를 지정된 IEvent 를 가지고 호출하게 되면, 이전에 동일한 소켓에 대해서 WSAAsyncSelect 함수나 WSAEventSelect 함수로 설정되어 있던 메시지 이벤트는 무시됩니다. 새로 설정된 메시지 이벤트가 영향력을 미치게 되는 것이죠. 아래의 코드에서는 s 소켓에 대해서 wMsg2 메시지로 FD_WRITE 이벤트만이 발생하게 됩니다.

rc = WSAAsyncSelect(s, hWnd, wMsg1, FD_READ);
rc = WSAAsyncSelect(s, hWnd, wMsg2, FD_WRITE);

소켓에 대해 설정되어있는 모든 네트웍 통지메세지를 앞으로 수신받지 않으려면, IEvent 를 0으로 셋팅하면, 됩니다.

rc = WSAAsyncSelect(s, hWnd, 0, 0);

WSAAsyncSelect 함수가 소켓에 대한 이벤트 메시지를 바로 비활성화 시킬지라도, 메시지가 어플리케이션의 메시지 큐에서 대기하도록 하는 것은 가능합니다. 따라서 어플리케이션은 취소작업후에 네트웍 이벤트 메시지를 수신받기위해 준비해야 합니다. closesocket 함수로 소켓을 닫는겨우 역시 WSAAsyncSelect 메시지 전송을 취소 하지만, 큐에 남아있던 메시지는 없어지지 않고 여전히 남아있게 됩니다.

accept 함수로 생성된 소켓은 이 소켓을 접속허용 하는데 사용되었던 리슨소켓(listen socket)과 같은 속성을 가지게 됩니다. 그 결과로 리슨소켓에 WSAAsyncSelect 로 셋팅되었던 이벤트는 접속허용된 소켓에 그대로 적용됩니다.  한 예로, 리슨소켓이 FD_ACCEPT, FD_READ, 그리고 FD_WRITE 이벤트를 가지고 있다면, 이 리슨소켓으로 접속허용된 소켓 또한 역시 FD_ACCEPT, FD_READ, 그리고 FD_WRITE 속성을 가지게 된다는 거져. 만약 새로 접속허용된 소켓에 대해서 다른 wMsg 와 이벤트를 적용시키고 싶다면, WSAAsyncSelect 함수를 사용해서 새로운 적절한 이벤트 정보를 다시 설정해야 합니다.

등록된 네트웍 이벤트가 지정된 소켓 s 에 대해서 발생했을 때, 어플리케이션 윈도우 hWnd wMsg 메시지를 수신받게 됩니다. wParam 매개변수는 네트웍 이벤트가 발생한 소켓을 의미합니다. lParam의 하위 16비트는 발생된 네트웍 이벤트를 의미하고, lParam의 상위 16비트는 특정한 에러코드를 의미합니다. 에러코드는 WINSOCK(2).H에 정의된 에러코드 입니다.

이벤트 통지 메시지를 사용하는 방법에서, WSAGetLastError 함수는 에러값을 체크하는데 사용할 수 없습니다. 왜냐믄, WSAGetLastError 함수의 리턴값이 lParam의 상위 16비트와 다를 수 있기 때문입니다.

에러코드와 이벤트코드는 WSAGETSELECTERROR와 WSAGETSELECTEVENT 매크로를 사용해서 lParam으로부터 뽑아낼 수 있습니다. 이 매크로는 WINSOCK(2).H에 정의되어 있습니다.

#define WSAGETSELECTERROR(lParam) HIWORD(lParam)
#define WSAGETSELECTEVENT(lParam) LOWORD(lParam)

이러한 매크로를 사용하면, 코드의 이식성이 좋아진다고 합니다.

사용가능한 네트웍 이벤트 코드를 아래에 리스팅 했습니다. 참고 하세요.

Value Meaning
FD_READ 데이터를 수신 할 수 있는 상태가 되었당.
FD_WRITE 데이터를 전송 할 수 있는 상태가 되었당.
FD_OOB out-of-band 데이터를 수신할 수 있는 상태가 되었당.
FD_ACCEPT 새로운 접속 요구를 허용할 준비가 되었당.
FD_CONNECT 접속 또는 multi-point join 연산이 완료되었당.
FD_CLOSE 연결된 상태방 소켓이 종료 했당.
FD_QOS Socket Quality Of Service 가 변경되었당.
FD_GROUP_QOS Socket Group Quality of Service(QOS)가 변경되었당
FD_ROUTING
_INTERFACE_CHANGE
지정된 목적지(들)에 대해서 경로배정 인터페이스가 변경되었당.
FD_ADDRESS_LIST
_CHANGE
소켓의 프로토콜 집합에 대한 로컬 어드레스 리스트가 변경되었당.

WSAAsyncSelect 함수는 데이터 전송/수신 연산(send 또는 recv) 을 성공적으로 할 수 있을 때를 결정하는데 주로 사용됩니다. 이렇게 사용되는 경우, 튼튼한 어플리케이션을 만들려면, WSAEWOULDBLOCK이 발생될 수 있는 상황에 대해서 철저한 준비를 해야합니다. 한 예로 아래와 같은 상황이 일어 날 수도 있습니다.

1. 데이터가 소켓 s 에 도착했습니다... 삐리비리빅~
    이때 윈속은 메시지를 포스팅 합니다.

2. 이때 어플리케이션은 몇몇 다른 메시지를 처리하고 있습니다.

3. 처리하고 있는동안에 어플리케이션이 ioctlsocket( s, FIONREAD...)를 호출하고, 소켓에 읽을 수 있는 데이터가 있다는 것을 알아냅니다.

4. 어플리케이션은 recv( s, ...)를 호출하여 데이터를 읽습니다.

5. 1.에서 이미 수신된 메시지를 post했었죠? 즉, 메시지 큐에 메시지는 남아있게 되므로 데이터를 읽을 수 있는상태라는 FD_READ 가 발생합니다.

6. 어플리케이션은 recv( s, ... )를 호출하겠죠? 하지만, WSAEWOULDBLOCK 에러가 발생하게 됩니다.

윈도즈 소켓은 특정한 네트웍 이벤트 메시지로 어플리케이션 윈도우로 항상 메시지를 주지 않습니다. 이러한 어플리케이션 윈도우로의 이벤트의 통보는, 어플리케이션이 네트웍 이벤트의 암시적 재활성화(re-enables) 통지를 위한 함수를 호출하기 전까지 (메시지큐에) 붙여지지 않습니다.
영문을 고대루 해석하려 하니까 좀 어색한 것 같죠? 쉽게 예를 들어서 설명하자면, 소켓으로부터 읽혀질 데이터가 있다고 가정 합시다. 어플리케이션이 FD_READ 이벤트의 통보를 활성화 했었다면, 윈도우즈 소켓은 지정된 윈도우로 하나의 FD_READ 이벤트를 전송할 것입니다. 이때 어플리케이션이
recv함수나 recvfrom 함수를 호출해야만 FD_READ 이벤트 생성이 다시 활성화 된다는 것입니다. 동시다발적으로 메시지가 도착하지는 않는다는 말입니다. recv 함수나 recvfrom 함수가 호출된 후에 소켓의 수신큐에 더 많은 데이터가 기다리고 있을 경우 다른 FD_READ 이벤트가 지정된 윈도우로 전송될 것입니다. 이렇게 메시지를 재 활성화 하는 함수를 "re-enabling function" 이라고 합니다. 아래의 표에 이러한 함수들을 나열해 보았습니다.

Event Re-enabling function
FD_READ recv, recvfrom, WSARecv, or WSARecvFrom
FD_WRITE send, sendto, WSASend, or WSASendTo
FD_OOB recv, recvfrom, WSARecv, or WSARecvFrom
FD_ACCEPT accept or WSAAccept ( condition 함수가 CF_DEFER인 WSATRY_AGAIN 일 경우는 제외)
FD_CONNECT 없음
FD_CLOSE 없음
FD_QOS SIO_GET_QOS 컴맨드로 호출된 WSAIoctl
FD_GROUP_QOS SIO_GET_GROUP_QOS 컴맨드로 호출된 WSAIoctl
FD_ROUTING
_INTERFACE_CHANGE
SIO_ROUTING_INTERFACE_CHANGE 컴맨드로 호출된 WSAIoctl
FD_ADDRESS_LIST
_CHANGE
SIO_ADDRESS_LIST_CHANGE 컴맨드로 호출된 WSAIoctl

FD_READ, FD_OOB, 그리고 FD_ACCEPT 이벤트에 대한 메시지 포스팅 방법은 "level-triggered"라고 합니다. 무슨 말이냐구요? 만약 재활성화 루틴이 호출되고, 이와 관련된 상태가 루틴 호출후에도 여전히 남아 있다면, 메시지는 어플리케이션에 다시 포스팅 됩니다. 이러한 포스팅 방법으로 어플리케이션은 메시지 구동 방식이 됩니다. 아직 이해가 잘 않되시죠? 아래의 순서를 한번 생각해 보세요... 지금 제가 한말을 쉽게 이해 할 수 있을 겁니다.

1. 네트웍 전송/수신 스택이 소켓에 100 바이트의 데이터를 수신받고, 윈속이 FD_READ 메시지를 포스팅 했습니다.

2. 어플리케이션이 recv( s, buffer, 50, 0 )을 호출해서 50바이트를 수신했습니다.

3. 읽을 데이터가 여전히 남아있는 동안 FD_READ 메시지가 포스팅 됩니다.

이러한 구조상에서, 어플리케이션은 하나의 FD_READ 메시지에 대해서 모든 가능한 데이터를 읽어낼 필요가 없습니다. 각각의 FD_READ 메시지에 대해서 하나의 recv를 사용하는 것이 적절한 방법입니다. 만약 어플리케이션이 하나의 FD_READ에 대해서 여러개의 recv를 호출할 경우, 여러개의 FD_READ 메시지를 수신할 수도 있습니다. 이러한 어플리케이션에서는 recv함수를 호출하기전에 WSAAsyncSelect 함수를 FD_READ 이벤트를 셋팅하지 않는 방법으로 FD_READ 메시지 수신을 막아 버려야할 필요가 있습니다.

FD_QOS 그리고 FD_GROUP_QOS 이벤트는 "edge triggered"로 간주됩니다. 메시지는 Quality Of Service가 바뀌었을 때 정확히 한번 포스팅 됩니다. 게다가 메시지는 프로바이더가 QOS에서의 다른 변화를 감지하든지, 아니면, 어플리케이션이 소켓에 대한 QOS를 재설정할 때 까지 메시지는 수신되지 않습니다.

FD_ROUTING_INTERFACE_CHANGE 메시지는 SIO_ROUTING_INTERFACE_CHANGE 컴맨드로 WSAIoctl 함수가 호출될 때 포스팅 됩니다.

FD_ADDRESS_LIST_CHANGE 메시지는 SIO_ADDRESS_LIST_CHANGE 컴맨드로 WSAIoctl 함수가 호출될 때 포스팅 됩니다.

만약 어플리케이션이 WSAAsyncSelect함수를 호출하거나 재활성(re-enabling)함수를 호출했을 때 이미 이벤트가 발생되었다면, 이때의 메시지는 적절히 포스팅 됩니다. 이해를 쉽게 하기위해 아래의 일어날 수 순서 상황을 생각해 보세요.

1. 어플리케이션이 생성한 소켓에 대해서 listen 함수를 호출 했습니다.

2. 접속 요구가 들어왔으나 아직 accept 하지 않았습니다.

3. 어플리케이션은 이 소켓에 대해서 FD_ACCEPT로 WSAAsyncSelect 함수를 호출하였습니다. 이때 이벤트의 영속성이 보존되기 때문에 윈속은 FD_ACCEPT 메시지를 바로 포스팅 하게 됩니다.

FD_WRITE 이벤트는 약간 다르게 운용됩니다. 즉, 소켓이 connect/WSAConnect 함수의 호출로 첨 접속되었을 때, 또는 accept/WSAAccept 함수로 접속허용되었을 때, 또는, send 연산이 WSAEWOULDBLOCK 으로 실패(비동기소켓은 항상 WSAEWOULDBLOCK입니다)한 상태에서 전송용 버퍼공간이 사용 가능해졌을 때 FD_WRITE 메시지는 포스팅 됩니다. 따라서 어플리케이션은 FD_WRITE 메시지를 받았을 때 데이터의 전송이 가능하다고 판단 할 수 있습니다.

FD_OOB 이벤트는 소켓이 out-of-band 데이터를 부분적으로 수신하여 나열할 때만 사용됩니다. 만약 소켓이 out-of-band 데이터를 인라인 방식으로 수신하도록 나열 한다면, out-of-band데이터는 일반 데이터와 같이 다루어 지게 됩니다. 이때, 어플리케이션은 interest(?)를 등록해야 합니다. 일반데이터와 같이 다루어 진다는 의미는 뭔지 아시겠죠? FD_OOB 이벤트가 아닌 FD_READ이벤트가 발생했을 때 데이터를 수신한다는 의미입니다. 어플리케이션은 out-of-band 데이터가 SO_OOBINLINE 옵션으로 setsockopt함수나 getsockopt함수를 사용하여 핸들링 하는 방법을 설정할 수 있습니다.

FD_CLOSE 메시지의 에러코드는 소켓이 우아한닫힘(graceful close)인지 그렇지 않은 닫힘인지를 나타냅니다. 만약 에러코드가 0이라면, 소켓은 우아한닫힘 이라는 의미이고, 에러코드가 WSAECONNRESET 이라면, 소켓의 가상 회선망이 리셋되었다는 것을 의미합니다. 이러한 것들은 SOCK_STREAM과 같은 접속 지향형 소켓일 경우에만 사용될 수 있는 것들입니다.

FD_CLOSE 메시지는 소켓과 연관된 가상 회선망이 끊겼다는 것을 수신받았을 때 포스팅 됩니다. TCP 의 경우는 접속이 TIME WAIT 나 CLOSE WAIT 상태로 들어갔을 때 FD_CLOSE 메시지가 포스팅 되게 됩니다. 즉, 원격 종단점에서의 전송영역의 셧다운이나 closesocket 함수의 호출의 결과로 일어날 수 있는 메시지입니다. FD_CLOSE 메시지는 모든 데이터가 소켓으로부터 읽혀지고 난 후에 포스팅 됩니다 그렇긴 해도, 최대한 손실된 데이터들을 피하고자 한다면, FD_CLOSE 메시지를 받은 후에 남아있는 데이터들을 체크하는 것이 좋겠죠?

아래에 지금까지 설명했던 비동기 통지 메시지에 대한 이벤트와 상태들을 간략히 요약해 놓았습니다.

FD_READ:  

    WSAAsyncSelect 함수가 호출 되고나서, 수신할수 있는 데이터가 있을 때

    데이터가 로컬 호스트로 도착하고, FD_READ 가 아직 포스팅되지 않았을 때

    recv 함수나 recvfrom 함수가 호출된 후에, 수신가능한 데이터가 여전히 남아 있을 때

    SO_OOBINLINE 옵션으로 setsockopt 함수를 호출하여 out-of-band 데이터가 일반데이터 처럼 취급되었을 때 수신할 out-of-band 데이터가 있을 때

FD_WRITE:

    WSAAsyncSelect 함수가 호출 되고, send sendto 함수가 사용가능 할 때

    connectaccept함수가 호출되고 나서, 접속이 완료 되었을 때

    sendsendto 함수가 WSAEWOULDBLOCK으로 실패한후, 전송용 버퍼가 사용 가능 할 때

    비접속 지향 소켓의 바인드 후에, FD_WRITE가 현재 발생되었거나 그렇지 않았을 때. (이러한 비접속 지향형 소켓은 바인드 후에 바로 데이터를 전송할 수 있는 상태의 소켓입니다.)

FD_OOB: setsockopt 함수를 이용해서 SO_OOBINLINE 옵션을 무시한 경우에만. (기본 설정값 이죠)

    WSAAsyncSelect 함수가 호출 되고나서, OOB 데이터를 MSG_OOB 플래그로 수신할수 있을 때.

    OOB데이터가 로컬 호스트로 도착하고, FD_OOB 가 아직 포스팅 되지 않았을 때.

    recv 또는 recvfrom 함수가 호출되고나서, 수신가능한 OOB 데이터가 남아 있을때.

FD_ACCEPT:

    WSAAsyncSelect 함수가 호출되고 나서, 허용가능한 접속 요청이 있을 때.

    접속 요청이 로컬호스트로 도착하고, FD_ACCEPT 가 아직 포스팅 되지 않았을 때.

    accept 함수의 호출 후에, 허용가능한 다른 접속 요청이 있을때.

FD_CONNECT:

    WSAAsyncSelect 함수가 호출되고 나서, 접속요청 작업의 완료가 있었던 경우.

    connect 함수가 호출되고 난 후에 접속 작업이 완료되었을 때.

    WSAJoinLeaf 함수의 호출 후에 join 연산이 완료 되었을 때.

    connect, WSAConnect 또는 WSAJoinLeaf 함수가 접속지향형 비동기 모드에서 호출된 후에, 연산이 시작되고 나면, WSAEWOULDBLOCK 이라는 에러코드가 발생될 것입니다. 왜냐구? 비동기기 때문이쥐... 암튼 이러한 비동기 함수의 호출후에 접속이 성공했든 실패했든간에 FD_CONNECT 메시지가 발생합니다. 클라이언트는 FD_CONNECT 메시지가 발생되었을 때 정말로 접속이 성공적으로 일어났는지 아닌지를 판단하기 위해서 에러코드를 체크 해 보아야 합니다.

FD_CLOSE: SOCK_STREAM과 같은 접속 지향형 소켓에서만 발생합니다.

    WSAAsyncSelect 함수가 호출되고, 접속이 종료 되었을때.

    원격지 소켓시스템이 우아한 종료(graceful close)를 한후에, 수신큐에 아무런 데이터가 없는 경우.

    로컬 시스템이 shutdown함수로 우아한 종료(graceful close)를 하고, 원격지 시스템이 이에 End of Data 로 응답하고(TCP 프로토콜의 FIN 이 1로셋팅된 경우), 수신가능한 데이터가 남아있지 않은 경우 발생.

    원격지 시스템이 접속을 리셋 시켰을 때(TCP 프로토콜의 RST가 1로 셋팅된 경우) 발생되는데, 이때 lParam은 WSAECONNRESET 에러 코드를 가지고 있게 됩니다.

    Note : closesocket 함수를 호출하고 난 후에 FD_CLOSE 메시지는 포스팅 되지 않습니다.

FD_QOS:

    WSAAsyncSelect 함수가 호출되고 나서, 소켓과 연관된 Quality Of Service가 변경된 경우

    SIO_GET_QOS 컴맨드로 WSAIoctl 함수가 호출된후, QOS가 변경된 경우

FD_GROUP_QOS (소켓 그룹으로 미래에 사용하기 위해 예약 되었다고 하네요?):

    WSAAsyncSelect 함수가 호출되고 나서, 소켓과 연관된 Group Quality Of Service가 변경된 경우

    SIO_GET_GROUP_QOS 컴맨드로 WSAIoctl 함수가 호출된후, GROUP QOS가 변경된 경우

FD_ROUTING_INTERFACE_CHANGE:

    SIO_ROUTING_INTERFACE_CHANGE 컴맨드로 WSAIoctl 함수가 호출되고 나서 로컬 인터페이스가 변경되었을 때

FD_ADDRESS_LIST_CHANGE

    SIO_ADDRESS_LIST_CHANGE 컴맨드로 WSAIoctl 함수가 호출되고 나서 로컬어드레스의 리스트가 변경되었을 때.

     

Return Values

에러가 없을 경우 WSAAsyncSelect 함수는 0을 반환 합니다. 에러가 있을 경우 SOCKET_ERROR를 반환 하며, WSAGetLastError 함수를 호출하여 지정된 에러코드를 검색할 수 있습니다.

 

Error Codes

WSANOTINITIALISED

이 함수를 사용하기 이전에 WSAStartup 함수를 성공적으로 호출해야 합니다.

WSAENETDOWN

네트웍 서브 시스템에서 에러가 났습니다.

WSAEINVAL 함수의 매개변수가 잘못 사용되었습니다.
WSAEINPROGRESS

블럭킹 윈속 v1.1이 진행상태에 있거나, 서비스 프로바이더가 아직 콜백함수를 진행하고 있습니다.

WSAENOTSOCK 지정된 소켓기술자가 소켓이 아닙니다.

추가적인 에러코드는 어플리케이션 윈도우가 메시지를 수신할 때 얻어낼 수 있습니다. 지금부터 나열하게될 에러코드는 lParam 매개변수를 WSAGETSELECTERROR 매크로를 이용하여 뽑아낸 에러코드 값입니다.

Event: FD_CONNECT

Error Code Meaning
WSAEAFNOSUPPORT 지정된 주소 패밀리가 본 소켓에서 사용될 수 없는 형태 입니다.
WSAECONNREFUSED 접속시도가 강제로 종료되었습니다.
WSAENETUNREACH 네트웍이 현재 지정된 호스트로 연결될 수 없습니다.
WSAEFAULT name 또는 namelen 매개변수가 올바른 형태가 아닙니다.
WSAEINVAL 소켓이 이미 특정한 어드레스와 바이드 되어 있습니다.
WSAEISCONN 소켓이 이미 접속상태에 있습니다.
WSAEMFILE 더 이상 소켓 기술자를 할당 할 수 없습니다.
WSAENOBUFS 사용할 수 있는 버퍼 공간이 없어서 접속할 수 없습니다.
WSAENOTCONN 소켓이 접속되어지지 못했습니다.
WSAETIMEDOUT 접속을 완료하지 못하고 타임아웃 되었습니다.

Event: FD_CLOSE

Error Code Meaning
WSAENETDOWN

네트웍 서브 시스템에서 에러가 났습니다.

WSAECONNRESET 접속이 원격지 로부터 리셋 되었습니다.
WSAECONNABORTED 접속이 타임아웃이나 다른 에러로 인하여 터미네이트 되었습니다.

Event: FD_READ
Event: FD_WRITE
Event: FD_OOB
Event: FD_ACCEPT
Event: FD_QOS
Event: FD_GROUP_QOS
Event: FD_ADDRESS_LIST_CHANGE

Error Code Meaning
WSAENETDOWN 네트웍 서브시스템에서 에러가 났습니다.

Event: FD_ROUTING_INTERFACE_CHANGE

Error Code Meaning
WSAENETUNREACH 지정한 목적지에 더 이상 도달 할 수 없습니다.
WSAENETDOWN 네트웍 서브시스템에서 에러가 났습니다.
 

QuickInfo

Windows NT : 사용가능
Windows : 사용가능
Windows CE : 지원되지 않음
Header :
          Win16/32 : winsock.h
          Win32-II : winsock2.h
Import Library :
          Win16 : winsock.lib
          Win32 : wsock32.lib
          Win32-II : ws2_32.lib

See Also

2009. 7. 8. 20:44

[소켓프로그래밍] setsockopt

setsockopt

 

setsockopt 함수는 소켓 옵션을 설정하는 함수입니다.

 

int setsockopt (
        SOCKET
 s,
        int  
level,
        int
 optname,
        const char FAR *
  optval,
        int  
optlen
);

 

Parameters

s
[입력] 대상 소켓의 기술자(descriptor)를 명시합니다.

level
[입력] 소켓 옵션 레벨이 정의 되며, SOL_SOCKET 과 IPPROTO_TCP 중 하나가 될 수 있습니다.

optname
[입력] 셋팅 하기위한 소켓옵션 값입니다.

optval
[입력] 검색된 옵션을 저장 하기위한 데이터 버퍼를 포인트 합니다.

optlen
[입력]
optval 매개변수 버퍼의 크기

 

Remarks

setsockopt 함수는 지정된 소켓의 옵션을 특정한 형태(type), 상태(state)로 결합하기 위해서 지정한 소켓옵션 값을 셋팅하는 함수입니다. 비록 여러 프로토콜 레벨로의 소켓 옵션이 존재 할 순 있다 하더라도, 가장 우선하는 레벨이 존재하게 됩니다. 옵션은 소켓 연산에 영향을 미칩니다. 예를들어 발송된 데이터(OOB 데이터를 예로 들 수 있습니다) 를 일반 데이터 스트림으로 수신했을때나, 소켓으로 브로드캐스트(broadcast) 메세지를 전송할때 영향을 미치게 됩니다.

이 함수에서 사용 할 수 있는 두가지 형태의 소켓옵션이 있습니다. 활성화 또는 비활성화 시키기 위한 Boolean 옵션과, 정수형(integer) 또는 구조체를 요구하는 옵션이 있습니다. Boolean 옵션을 활성화 하기 위해서 optval 매개변수는 0이 아닌 정수를 포인트 하고, 비활성화 하기 위해서  0을 포인트 합니다. optlen 매개변수는 Boolean 옵션에 대해서는 sizeof(int)로 설정 해야만 합니다. 다른 옵션들에 대해서, optval 매개변수는 설정하고자 하는 정수(integer)나 구조체를 넘겨야 하고, optlen 매개변수는 정수(integer) 또는 구조체의 길이를 넘겨야 합니다. 아래에 setsockopt 함수에서 사용 할 수 있는 옵션들을 간단한 설명과 함께 나열 해 보았습니다.

 

level = SOL_SOCKET

옵션값(Value)

데이터형태(Type)

의미(Meaning)

SO_BROADCAST
 

BOOL
 

소켓으로 브로드캐스트(broadcast) 메세지를 전송 할 수 있도록 합니다.

SO_DEBUG

BOOL

디버깅 정보를 레코딩 합니다.

SO_DONTLINGER



BOOL


 

소켓을 종료(close)할때 보내지지 않은 데이터를 보내기위해서 블럭킹 상태가 되지 않도록 합니다. 이 옵션을 셋팅하는 것은 LINGER 구조체의 멤버중 l_onoff를 0으로 셋팅하고 SO_LINGER 옵션을 셋팅 했을때와 같은 효력을 발휘합니다.

SO_DONTROUTE

BOOL

라우팅 하지 않고, 직접 인터페이스로 보냅니다.

SO_GROUP_PRIORITY




 

int




 

차후에 소켓그룹을 사용하기위해 예약 되어있습니다. 소켓그룹의 일부분인 소켓을 만들기위해 관련된 우선사항을 명시합니다.
(우아아아악~ 정말 해석 못해 먹겠네여... 원문을 첨가 하겠습니다)
Reserved for future use with socket groups. Specify the relative priority to be established for sockets that are part of a socket group.

SO_KEEPALIVE

BOOL

Keepalives를 전송합니다.

SO_LINGER
 

struct LINGER
 

아직 전송되지 않은 데이터가 있을 경우 소켓을 종료(close)할때 대기합니다.

SO_OOBINLINE
 

BOOL
 

일반 데이터 스트림(normal data stream)으로 out-of-band 데이터를 수신합니다.

SO_RCVBUF

 

int

 

데이터를 수신하기 위한 총 버퍼공간을 명시합니다. 요놈은 SO_MAX_MSG_SIZE 나, TCP Window의 크기와 상관이 없습니다.

SO_REUSEADDR
 

BOOL
 

이미 사용되고 있는 어드레스를 바인드 할 수 있도록 합니다.

SO_SNDBUF

 

int

 

데이터를 전송하기 위한 총 버퍼공간을 명시합니다. 요놈은 SO_MAX_MSG_SIZE 나, TCP Window의 크기와는 상관이 없습니다.

level = IPPROTO_TCP

TCP_NODELAY
 
BOOL
 

전송합병(send coalescing)을 위해 Nagle 알고리즘을 비 활성화 합니다.

Return Values

에러가 발생하지 않으면, setsockopt 함수는 0을 반환합니다. 에러가 발생하면, SOCKET_ERROR을 반환하면 WSAGetLastError 함수를 이용해서 특정 에러 코드를 얻어낼 수 있습니다.

Error Codes

WSANOTINITIALISED

이 함수를 사용하기 전에 성공적인 WSAStartup 함수의 호출이 없었습니다.

WSAENETDOWN 네트웍 서브 시스템에 에러가 발생했습니다.
WSAEFAULT

optval 또는 optlen 매개변수 중의 하나가 올바르지 않거나 optlen 매개변수의 값이 너무 작습니다.

WSAEINPROGRESS

블럭킹 윈속 v1.1 이 현재 진행 중이거나, 서비스 프로바이더가 콜백 함수를 여전히 처리하고 있습니다.

WSAEINVAL

level 매개변수가 잘못됐거나, 알 수 없는 값 입니다.

WSAENETRESET

SO_KEEPALIVE가 설정 되었을때, 접속이 타입아웃 되었습니다.

WSAENOPROTOOPT

알 수 없는 옵션이거나, 지원지지 않는 옵션을 사용 했습니다.

WSAENOTCONN

SO_KEEPALIVE가 설정 되었을때, 접속이 리셋 되었습니다.

WSAENOTSOCK

기술자(descriptor)가 소켓 기술자가 아닙니다.

 

QuickInfo

Windows NT : 사용가능
Windows : 사용가능
Windows CE : 버젼 1.0 그리고 그이후의 버젼에서 사용가능
Header :
          Win16/32 : winsock.h
          Win32-II : winsock2.h
Import Library :
          Win16 : winsock.lib
          Win32 : wsock32.lib
          Win32-II : ws2_32.lib

See Also

overview, bind, getsockopt, ioctlsocket, socket, WSAAsyncSelect, WSAEventSelect

 

 

출처 : DEVPIA 곽용진님 style  APIs

[출처] [Socket] setsockopt|작성자 임종배

2009. 7. 3. 10:38

[소켓프로그래밍] winsock.h 중복 재정의 에러, error C2011: 'fd_set' : 'struct' type redefinition

원인
windows.h 가 구버전의 winsock.h 를 포함하고 있기 때문이다.

해결방법
#include <windows.h > 문장 위에 아래 문장을 추가해 준다.
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI
#endif