본문 바로가기

지식/Network

socket

1.소켓(Socket)
(1) socket이란?
    정규 유닉스 파일 기술자를 이용하여 다른 프로그램과 정보를 교환하는 방법을 의미한다.
(2) 소켓의 종류
   1) 스트림소켓(Stream socket) : 양측을 신뢰성있게 연결해 주는 소켓이다. 보통 SOCK_STREAM이라
                                지칭되며 이 소켓을 통할 경우 전송순서를 정확히 유지하고 에러
                                까지 교정된다. TCP 프로토콜을 이용한다. 대표적으로 이 소켓을
                                이용하는 것이 텔넷이다. 입력한 모든 글자는 순서대로 전달이
                                되어야 하는 경우에 사용된다.
   2) 데이타그램소켓(Datagram socket) : 비연결소켓이라고도 하고 SOCK_DGRAM이라 지칭된다. 신뢰도
                                     가 떨어지며 패킷들의 순서가 바뀌어서 도착할 수 있다. UDP
                                     프로토콜을 이용한다.
   3) raw소켓(raw socket) : IP와 ICMP 프로세스의 액세스를 제공하는 소켓이다.
(3)소켓주소
   1)소켓주소 : 소켓주소(socket address)를 구성하는 정보를 포함한 데이터 구조에의 포인터를
              참조하고 소켓주소는 통신 링크의 한 종단점을 규정한다.
   2)include문장에 의해 이들 식별자와 데이터 구조가 응용프로그램에 유용해진다.
     #include<sys/types.h>
     #include<sys/sockets.h>
   3)소켓주소의 데이터구조
     ㄱ. 패밀리(family) : 사용되는 프로토콜 패밀리를 식별하는 16비트 정수값이고 AF_INET식별자
     ㄴ. 포트(port) : 프로세스에 할당된 포트 번호를 식별하는 16비트 정수값
     ㄷ. 주소(address) : 프로세스가 실행되고 있는 호스트의 이진 형식 인터넷주소를 포함하는
                       32비트 정수값
   4)소켓 주소 데이터 형 정의 및 데이터 구조 형식
     ㄱ. 무부호 정수 데이터형
        u_char : 캐릭터 형식의 무부호(unsigned)정수
        u_short : 이진 형식의 16비트 무부호정수
        u_long : 이진 형식의 32비트 무부호정수
     ㄴ. 인터넷 주소의 데이터 구조 정의
        struct in_addr {
          u_long s_addr;
        }
     ㄷ. 소켓 주소의 데이터 구조 정의
        struct sockaddr_in {
          short sin_family; /* AF_INET */
          u_short sin_port; /* 16-bit port number */
                            /* network byter order */
          struct in_addr sin_addr; /* 32-bit internet address */
                                   /* network byte ordered */
          char sin_zero[8]; /* unused */
   5)종단점 식별자
     ㄱ. 로컬 호스트 프로세스의 종단점 주소를 정의
        로컬 인터넷주소
        로컬 포트번호
     ㄴ. 원격 호스트 프로세스의 종단점 주소를 정의
        원격 인터넷 주소
        원격 포트 번호
(4) 소켓 시스템 호출
   1) accept : TCP를 이용한 연결형 응용 프로토콜에서 서버가 클라이언트로부터 연결 요청이 들어
             오기를 기다리게 하기 위하여 서버 프로세스에 의해 이용
   2) bind : 종단점 식별자(인터넷 주소 및 포트번호)를 개방 소켓의 정수 기술자와 관련짓는데
            이용
   3) close : 파일 기술자를 닫는 것과 유사한 방법으로 소켓 기술자를 닫는데 이용
   4) connect : TCP를 이용한 연결형 응용 프로토콜에서 서버와의 TCP연결을 설정하기 위해 클라이
             언트 프로세스에 의해 이용
   5) gethostbyaddr : 인터넷 주소가 주어진 호스트에 대한 정보를 얻는데 이용
   6) gethostbyname : 호스트이름이 주어진 원격 호스트의 인터넷 주소를 얻는데 이용
   7) gethostid : 로컬 호스트의 인터넷 주소를 얻는데 이용
   8) gethostname : 로컬호스트의 이름을 얻는데 이용
   9) getpeername : 원격 프로세스와 관련된 종단점 주소(인터넷 주소 및 포트번호)를 얻는데 이용
   10)getsockname : 주어진 소켓과 관련된 이름을 얻는데 이용
   11)getprotobyname : 프로토콜의 이름이 주어진 프로토콜과 관련된 정수값을 얻는데 이용
   12)getservbyname : 서비스의 이름이 널리 알려진 TCP/IP응용계층 서비스와 관련된 정수값을
                     얻는데 이용
   13)getsockpot : 소켓과 관련된 파라미터의 목록을 얻는데 이용
   14)listen : TCP를 이용한 연결형 응용 프로토콜에서 서버가 TCP연결을 설정하기 위해 클라이언트
              로부터 요청을 기꺼이 허락한다는 것을 나타내기 위하여 서버 프로세스에 의해 이용.
   15)read : 소켓으로부터 들어오는 데이터를 받아들이는데 이용
   16)ready: 연속적인 버퍼로 데이터를 받아들이는데 read의 대안으로 이용
   17)recv : 추가의 flag인자를 사용하며, 소켓으로부터 들어오는 데이터를 받아들이는데 read 대안
            으로 이용
   18)recvfrom : UDP를 이용한 비연결형 응용 프로토콜에서 데이터그램 소켓으로부터 사용자 데이터
                그램을 수신하고 송신측의 소켓 주소를 얻는데도 이용
   19)recvmsg : UDP를 이용한 비연결형 응용 프로토콜에서 데이터그램소켓으로부터 사용자의 데이터
               그램을 수신하는데 이용한다. 버퍼는 사용자 데이터그램의 헤더뿐만 아니라 사용자
               데이터그램의 데이터 부분도 포함한다.
   20)select : 프로세스가 다중 I/O이벤트중의 어떤 하나가 완료되기를 기다리게 하는데 이용한다.
   21)send : 추가의 flag인자를 사용하며, 소켓을 통해 데이터를 보내기 위해 write대안으로 이용
   22)sendmsg : 사용자 데이터그램을 데이터그램 소켓을 통해 보내는데 UDP를 이용한 비연결형 응용
               프로토콜에서 이용한다. 버퍼는 사용자 데이터그램의 헤더뿐만 아니라 사용자 데이터
               그램의 데이터부분도 포함한다.
   23)sendto : 데이터그램 소켓을 통해 데이터를 보내고, UDP를 이용한 비연결형 응용 프로토콜에서
              원격 프로세스의 소켓주소를 규정하는데 이용한다. sentto시스템 호출은 데이터가
              전달될 때 연관이 설정되게 한다.
   24)setsockopt : 소켓과 관련된 파라미터들의 값을 설정하는 데 이용
   25)shutdown : TCP를 이용한 연겷형 응용프로토콜에서 설정된 TCP연결의 한 종단을 닫는데 이용
   26)socket : 소켓 데이터구조를 초기화하고, 통신에 사용되는 전달계층의 프로토콜을 식별하며,
              후속 socket시스템 호출이 소켓을 참조하는데 사용할 수 있는 정수 기술자를 얻는
              데 이용한다.
   27)write : 소켓을 통해 데이터를 보내는데 이용한다.
   28)writev : 불연속적인 버퍼로부터 데이터를 보내기 위해 write의 대안으로 이용한다.
(5)응용프로토콜
   1)비연결형 응용프로토콜
    ㄱ. 서버시스템호출
       a. 서버는 클라언트 프로세스와 서버 프로세스간의 통신을 지원하기 위해 생성되어야 하는
         5튜플(tuple)연관에 프로토콜 정보를 채우기 위해 socket시스템호출을 수행한다. 비연결
         응용에서는 socket시스템 호출이 UDP의 이용을 지정한다.
       b. 서버는 서버의 반연관을 형성하는 정보의 나머지 부분을 채우기 위해 서버의 소켓주소를
         지정하는 bind시스템 호출을 수행한다. 서버의 반연관은 프로토콜 식별자, 서버가 동작하는
         호스트의 인터넷 주소, 서버가 이용하는 포트 번호로 구성된다.
       c. 서버는 클라이언트로부터 들어오는 데이터를 서버가 받도록 하는 recvfrom시스템호출을
         수행한다. recvfrom시스템 호출은 서버 프로세스의 서비스를 이용하고자 원하는 클라이언
         트로부터 데이터가 도착할 때까지 서버를 대기시킨다.
       d. 클라이언트로부터 사용자 데이터그램이 들어오면 서버는 필요한 절차를 수행한다. 들어
         오는 사용자 데이터그램에 포함된 헤더정보는 클라이언트 호스트에서 클라이언트 프로세스
         에 의해 이용되는 클라이언트 호스트의 인터넷 주소와 포트번호를 포함한다. 이 정보는
         클라이언트 프로세스와 서버 프로세스 사이의 5튜플 연관을 완성하는데 필요한 최종 2가지
         정보를 서버에게 제공한다.
       e. 서버는 각기 사용자 데이터그램을 클라이언트로 되돌려 보내는 하나 혹은 그 이상의
         sendto시스템 호출을 실행하는 것으로 응답한다. sendto시스템 호출은 위 단계 4에서
         결정된 클라이언트 소켓주소를 참조한다.
    ㄴ. 클라이언트 시스템호출
       a. 클라이언트는 자체의 반연관을 위한 프로토콜 정보를 채우기 위해 socket시스템호출을
         수행한다. 비연결 응용에서는 socket시스템 호출은 UDP의 이용을 지정한다.
       b. 클라이언트는 클라이언트의 반연관을 형성하는 정보의 나머지 부분을 채우기 위해 클라이
         언트의 소켓주소를 지정하는 bind시스템호출을 수행한다. 클라이언트 프로세스는 일반적
         으로 클라이언트의 인터넷주소와 사용되고 있지 않는 임시포트번호를 제공하도록 네트워킹
         소프트웨어에게 요청한다.
       c. 그리고 나서 클라이언트는 사용자의 데이터그램을 서버에 송신하기 위해 sendto시스템
         호출을 수행한다. 클라이언트는 서버의 소켓 주소를 알아야 하고, 이 소켓주소는 sendto
         시스템 호출에 참조되어야 한다.
       d. 만약 클라이언트가 서버로부터 데이터를 수신하기를 원한다면 데이터가 서버로부터 도착할
         때까지 클라이언트를 대기하게 하는 recvfrom시스템 호출을 수행한다.
       e. 클라이언트가 서버와의 대화를 끝낸 후, 이 과정을 종료한다.
(참고) 튜플(tuple)
   LISP,Python, Linda등과 같은 프로그래밍 언어에서의 튜플은 정렬된 값들의 세트이다. 각 값들의
  구분자로는 특정 언어의 규칙에 따라 다소 다르지만, 대체로 콤마가 쓰인다. 데이터 형식으로서
  사용되는 튜플은 한 프로그램에서 다른 프로그램으로 스트링 매개변수를 전달하거나 관계형 데이타
  베이스 내의 일련의 속성 값들을 표현하는 것이 보통이다. 일부 언어에서는 튜플이 괄호나 꺽쇠
  묶음 또는 다른 구분자 내에 들어있는 다른 튜플로 포개어질 수 있다. 튜플은 서로 다른 데이터
  형식을 갖는 속성들을 포함할 수 있다. 아래의 서로 다른 데이터 형식을 갖는 튜플의 예이다.
    17,*,2.29,Seven
  위의 예는 네개의 값을 가지고 있기 때문에 4-튜플이라고도 불린다. n-튜플은 확정되지 않았거나,
  값의 개수는 정의되지 않는 튜플을 말한다.
                   
   2)연결형 응용 프로토콜
    ㄱ. 서버 시스템 호출
       a. 서버는 클라이언트 프로세스와 서버 프로세스간의 통신을 지원하기 위해 생성되어야 하는
         5튜플(tuple)연관에 프로토콜 정보를 채우기 위해 socket시스템 호출을 수행한다. 연결형
        응용에서는 socket시스템호출이 TCP의 사용을 지정한다.
       b. 서버는 서버의 반연관을 형성하는 정보의 나머지 부분을 채우기 위해 서버의 소켓주소를
         지정하는 bind시스템 호출을 수행한다. 서버의 반연관은 프로토콜 식별자, 서버가 동작하는
         호스트의 인터넷 주소, 서버가 이용하는 포트번호로 구성된다.
       c. 서버는 TCP연결의 설정을 위해 클라이언트로부터의 요청을 기꺼이 허락한다는 것을 지시
         하는 listen시스템 호출을 수행한다.
       d. 서버는 TCP연결의 설정을 위한 요청이 클라이언트로부터 수신될 때까지 서버를 대기하도록
         하는 accept시스템 호출을 수행한다. 이 accept시스템 호출은 연결 설정의 요청이 실제
         수신될 때 클라이언트 프로세스의 소켓 주소로 채워진소켓주소의 데이터 구조를 참조한다.
       e. 서버는 설정된 TCP연결을 통해 클라이언트로부터 들어오는 데이타를 받아들이는 read 혹은
         recv시스템 호출을 수행한다.
       f. 서버는 필요한 절차를 수행하여 서버가 클라이언트에게 신뢰성있게 데이터를 보낼 수 있게
         해주는 write혹은 send시스템 호출을 수행한다.
    ㄴ. 클라이언트 시스템 호출
       a. 클라이언트는 자체의 반연관을 위한 프로토콜의 정보를 채우기 위해 socket시스템 호출을
         수행한다. 연결형 응용에서 socket시스템 호출은 TCP의 이용을 지정한다.
       b. 클라이언트는 서버와의 TCP연결을 설정하기 위하여 클라이언트의 소켓주소를 지정하는
         connect시스템 호출을 수행한다. 클라이언트는 connect 시스템 호출은 연결을 설정하는데
         필요한 3중 핸드쉐이크 절차를 완성하기 위해 클라이언트 프로세스와 서버 프로세스간의
         데이터가 오고 갈 수 있게 한다. 이 핸드쉐이크 과정중에 교환된 데이터는 클라이언트와
         서버의 연관을 완성하는데 필요한 정보를 포함한다. 클라이언트는 connect시스템 호출을
         수행하기 위해 서버프로세스의 소켓주소를 알아야 하므로, 일반적으로 클라이언트는 연관에
         사용될 클라이언트 자신의 인터넷주소와 사용되고 있지 않는 임시 포트 번호를 제공하도록
         통신 소프트웨어에게 요청한다.
       c. 클라이언트는 설정된 TCP연결을 통해 서버에게 데이터를 송신하기 위해 write혹은 send
         시스템 호출을 실행한다.
       d. 클라이언트는 또한 서버로부터 들어오는 데이터를 받기위해 read 혹은 recv시스템 호출을
         실행한다.
       e. 클라이언트가 서버와의 대화를 끝낸 후 이 과정을 종료한다.
(6) 소켓 프로그래밍의 구조
   1) 소켓 기술자의 데이터형 : UNIX에서 파일을 새로 열면 INT형, 즉 정수형으로 리턴한다. open문
                           이 리턴한 정수를 파일 기술자(File Descriptor)라고 하며 프로그램에서
                           open된 파일을 액세스할 때 이 파일 기술자를 사용하게 된다. 이 기술자테
                           이블에는 open되어 있는 파일의 각종 정보를 포함하고 있는 구조체를 가리
                           키고 포인터들로 구성된 테이블이다. 이런 소켓 기술자를 소켓번호라 할
                           수 있다.
   2) 구조체 : 프로그램 작성시 필요한 일종의 미리정의된 형식이다.
    * 예
     1. struct sockaddr {
           unsigned short    sa_family;      /* address family,AF_xxx */
           char              sa_data[14];    /* 14bytes of protocol address */
        };
          => sa_family는 여러가지가 될 수 있는데, 보통 "AF_INET"이 많이 사용되고, sa_data는
            목적지 주소와 포트번호를 가지게 된다.
     2. struct sockaddr_in {
           short int          sin_family;   /* address family */
           unsigned short int sin_port;     /* Port number */
           struct in_addr     sin_addr;     /* Internet address */
           unsigned char      sin_zero[8];  /* Same size as struct sockaddr */
        };
          => sin_zero배열은 sockaddr과 구조체의 크기를 맞추기 위해서 넣어진 것이므로 bzero()나
            memset()함수를 이용하여 모두 0으로 채워야 한다. 이 구조체는 sockaddr의 포인터를
            이용하여 참조될 수 있고 그 반대도 가능하다. 따라서 socket()가 struct sockaddr *를
            원하더라도 struct sockaddr_in을 사용할 수 있고 바로 참조할 수도 있다. 또한
            sin_family는 sa_family에 대응되는 것이며 물론 "AF_INET"로 지정되어야 하며,
            sin_port, sin_addr은 네트워크 바이트 순서로 되어야 한다.
   3) 사용법
     int socket(
         domain,                    // 프로토콜 체계
         type,                      // 서비스 타입
         protocol                   // 소켓에 사용될 프로토콜
        );

2.서버-클라이언트관련 시스템호출
(1)socket시스템 호출 : 소켓을 초기화하는 데 이용
   1)인자
     ㄱ. 패밀리(family) : 프로토콜 패밀리를 설명하는 정수, 주로 AF_INET 또는 PF_INET이다.
     ㄴ. 유형(type) : 초기화되는 소켓의 유형을 설명하는 정수이다. 주로 SOCK_STREAM 또는
                    SOCK_DGRAM이다.
     ㄷ. 프로토콜(protocol) : 프로토콜을 설명하는 정수, 보통 0값으로 세팅한다.
   2)결과 : 파일 기술자(file descriptor)와 유사한 정수값을 반환
   3)예
     stream_sockfd=socket(
                           AF_INET,
                           SOCK_STREAM,
                           0
                         );
     dgram_sockfd=socket(
                          AF_INET,
                          SOCK_DGREAM,
                          0
                         );
(2)bind시스템호출
   1)특징
     ㄱ. 5튜플 연관에 로컬 프로세스에 대한 종단점 식별자(인터넷주소와 포트번호)를 채운다.
     ㄴ. 이름이 없는 소켓에 이름을 지정한다.
     ㄷ. 주소를 지정하여 이 주소로 메시지가 오면 전달한다.
   2)인자
     ㄱ. 소켓 : 소켓을 식별하는 정수 기술자
     ㄴ. 로컬주소(local address) : 소켓 주소 데이트 구조의 포인터
     ㄷ. 주소길이(address length) : 로컬 주소 인자의 길이를 제공하는 정수
   3)예
     #include<sys/types.h>
     #include<sys/socket.h>

     bind (
            sockfd,
            (struct sockaddr *)&own_addr,
            sizeof(own_addr)
          );
(3)connect시스템 호출
   1)특징
     ㄱ. 연결형 응용 프로토콜의 구현시 클라이언트 프로세스에 의해 수행된다.
     ㄴ. 다른 소켓을 연결을 요청하는 역할이다.
     ㄷ. 클라이언트 프로세스와 서버 프로세스간 연관을 형성하는 5튜플을 완성하는데 필요한 모든
        정보를 운반하는 IP 데이터그램이 자동적으로 교환되는 3중 핸드쉐이크 절차를 시작한다.
   2)인자
     ㄱ. 소켓(socket) : 소켓을 식별하는 정수 기술자
     ㄴ. 원격주소(remote length) : 소켓 주소 데이터 구조의 포인터, 실제연결하고자 하는 곳의
                                 주소값이 온다.
     ㄷ. 주소길이(address length) : 원격 주소 인자의 길이를 제공하는 정수
   3)예
     connect(
             sockfd,
             (struct sockaddr *)&serv_addr,
             sizeof(serv_addr)
            );
(4)listen시스템호출
   1)특징
     ㄱ. 연결형 응용 프로토콜에서 서버 프로세스에 의해 수행한다. 즉 SOCK_STREAM과 SOCK_SEQPA
        CKET소켓에서만 사용된다.
     ㄴ. 서버 프로세스가 TCP연결 설정을 위해 클라이언트 프로세스로부터의 요청을 기꺼이 허락한
        다는 것을 알려준다. 즉 연결 요청을 받아들이겠다고 선언한다.
   2)인자
     ㄱ. 소켓(socket) : 소켓을 식별하는 정수 기술자
     ㄴ. 큐길이(queue length) : 서버가 accept시스템 호출의 수행을 기다리는 동안 연결요청을
                              시스템이 대기시킬 수 있는 지를 지시하는 정수. backlog라고 하며
                              얼마나 많은 요청을 받아들일 것인가를 지정한다.
   3)사용법
     #include<sys/types.h>
     #include<sys/socket.h>

     int listen(int s, int backlog);
   4)예
     listen(
            sockfd,
            5
            );
(5)accept시스템 호출 : 연결형 응용 프로토콜에서 서버프로세스가 클라이언트로부터 도착하는 연결
                      요청을 기다리게 하기 위해 서버프로세스에 의해 수행
   1)특징
     ㄱ. 큐에서 대기중인 연결요청에 대해서 새로운 소켓을 만든다.
     ㄴ. 서버가 여러 클라이언트에게 서비스를 하려고 소켓을 만들 때 사용한다.
     ㄷ. 큐가 비어있다면 연결요청이 올 때까지 기다린다.
     ㄹ. 새로운 소켓은 소켓기술자의 속성을 따라간다.
   2)인자
     ㄱ. 소켓(socket) : 소켓을 식별하는 정수 기술자
     ㄴ. 클라이언트 주소(client address) : 소켓주소 데이터 구조의 포인터
     ㄷ. 주소 길이(address length) : 클라이언트 주소 인자의 길이를 제공하는 정수
   3)결과 : 새로운 소켓 데이터 구조를 만들며, 소켓기술자 정수값을 반환
   4)예
     newsockfd=accept(
                      sockfd,
                      (struct sockaddr *)&cli_addr,
                      &clilen
                     );
(6)write 및 send시스템 호출 : 데이터를 원격 프로세스에 보내는데 이용
   1)인자
     ㄱ. 소켓(socket) : 소켓을 인식하는 정수 기술자
     ㄴ. 버퍼(buffer) : 보낼 데이터를 포함하는 버퍼의 포인터
     ㄷ. 길이(length) : 보낼 옥텟의 수를 지정하는 정수
     ㄹ. 플래그(flag) : send시스템 호출에만 적용
   2)결과 : write와 send 모두 실제로 전송된 옥텟수를 포함한 정수값을 반환
   3)예
     nwritten = write(
                      sockfd,
                      buffer,
                      noctets
                     );
     nwritten = send(
                     sockfd,
                     buffer,
                     noctets,
                     sendflag
                    );
(참고) 옥텟(octet)
   보통 컴퓨터에서의 옥텟은 8비트의 배열을 말한다. 따라서 옥텟 한개는 일반적으로 8비트로 구성
  되어 있는 한 바이트와 같다. 그러나 모든 컴퓨터 시스템이 8비트를 1바이트를 사용하지 않기
  때문에, 8비트 한셋을 일컫는 분명한 의미제공을 위해 옥텟이라는 용어가 사용된다.

(7)sendto시스템호출 : send와 유사, UDP를 사용한 비연결형 응용 프로토콜에서 클라이언트 프로세
                     스와 서버프로세스 모두에 의해 write또는 send대신에 사용된다.
   1)인자
     ㄱ. 소켓(socket) : 소켓을 식별하는 정수 기술자
     ㄴ. 버퍼(buffer) : 보낼 데이터를 포함하는 버퍼의 포인터
     ㄷ. 길이(length) : 보낼 옥텟의 수를 지정하는 정수
     ㄹ. 플래그(flag) : sendto동작을 위한 특수한 선택사항을 지정
     ㅁ. 원격 주소(remote address) : 소켓 주소 데이터 구조의 포인터
     ㅂ. 주소 길이(address length) : 원격 주소 인자의 길이를 제공하는 정수
   2)결과 : 실제로 전송된 옥텟수를 포함한 정수값을 반환한다.
   3)예
     nwritten = sendto(
                       sockfd,
                       buffer,
                       noctets,
                       sendflag,
                       (struct sockaddr *)&serv_addr,
                        sizeof(serv_addr)
                       );
(8)read 및 recv 시스템 호출 : 원격 프로세스로부터 들어오는 데이터를 받는데 이용
   1)인자
     ㄱ. 소켓(socket) : 소켓을 식별하는 정수 기술자
     ㄴ. 버퍼(buffer) : 수신된 데이터가 들어간 버퍼의 포인터
     ㄷ. 길이(length) : 수신 버퍼의 길이를 지정하는 정수
     ㄹ. 플래그(flag) : recv 시스템 호출에만 적용
   2)결과 : 실제로 전송된 옥텟수를 포함한 정수값을 반환
   3)예
     nread = read(
                   sockfd,
                   buffer,
                   bufferlength,
                 );
     nread = recv(
                   sockfd,
                   buffer,
                   bufferlength,
                   recvflag,
                 );
(9)recvfrom시스템 호출 : recv와 유사하며 UDP를 이용한 비연결형 응용 프로토콜에서 클라이언트
                        프로세스와 서버 프로세스에 의하여 recv 또는 read시스템 호출 대신사용
                        한다.
   1)인자
     ㄱ. 소켓(socket) : 소켓을 식별하는 정수 기술자이다.
     ㄴ. 버퍼(buffer) : 수신된 데이터가 들어갈 버퍼의 포인터이다.
     ㄷ. 길이(length) : 수신 버퍼의 길이를 지정하는 정수이다.
     ㄹ. 플래그(flag) : recvfrom동작을 위한 특수한 선택사항을 지정한다.
     ㅁ. 원격주소(remote address) : 소켓 주소 데이터 구조의 포인터
     ㅂ. 주소 길이(address length) : 원격 주소 인자의 길이를 제공하는 정수이다.
   2)결과 : 실제로 읽은 옥텟수를 포함한 정수값을 반환한다.
   3)예
      nwritten = recvfrom(
                           sockfd,
                           buffer,
                           bufferlength,
                           recvflag,
                           (struct sockaddr *)&serv_addr,
                           &length
                          );
(10)close시스템 호출 : 통신이 완료된 수 소켓을 닫기 위해 파일 기술자와 동일한 방법으로 소켓
                      기술자를 참조
   1)인자
     ㄱ. 소켓(socket) : 소켓을 식별하는 정수기술자
   2)예
     close(
            sockfd
          );

3. 소켓프로그래밍 관련 함수정리
(1) socket() : 연결의 끝점(소켓)을 생성하고 파일 기술자(File Descriptor)인 정수값을 리턴하며
               에러시에는 -1을 리턴한다. 전역변수인 errno에 에러값이 기록된다.
   1) 기본구조
     #include <sys/types.h>
     #include <sys/socket.h>

      int socket( int domain, int type, int protocol);
   2) 옵션
     -domain : 프로토콜체계를 정의한다. 소켓은 본래 TCP/IP,즉 인터넷만을 위하여 정의된 것이
             아니라, UNIX네트웍, XEROX네트웍 등에서도 사용할 수 있도록 정의되어 있다. 따라서
             가질 수 있는 값이 많다. 대표적은 것은 다음과 같다.
       PF_INET : 인터넷 프로토콜 체계 사용할 경우로 요즘은 AF_INET으로 선언해야 한다.
       PF_UNIX : UNIX방식의 프로토콜 체계를 사용할 경우
       PF_NS : XEROX 네트웍 시스템의 프로토콜을 사용할 경우
     -type : 서비스 타입을 말한다.
      SOCK_STREAM : 스트림 방식의 소켓생성
      SOCK_DGRAM : 데이타 그램 방식의 소켓생성
      SOCK_RAW : raw소켓
      SOCK_SEQPACKET : sequenced packet
      SOCK_RDM : reliably delivered message
     -protocol : 0으로 지정한다.
(2) open() : 파일을 열어서 사용하는 함수이다. 파일을 여는 데 성공하면 음수가 아닌 파일 기술자
             를 반환하며 실패하면 -1값을 반환한다.
   1)사용법
     변수 = open("파일이름",읽기타입,허가권)
   2)읽기타입(flags)
     ㄱ. O_RDONLY : 읽기 전용 액세스를 위해 파일을 연다.
     ㄴ. O_WRONLY : 쓰기 전용 액세스를 위해 파일을 연다.
     ㄷ. O_RDWR   : 읽기와 쓰기 액세스를 위해 파일을 연다.
     ㄹ. 0_CREAT  : 파일이 존재하지 않으면 생성한다.
     ㅁ. O_EXCL   : 파일이 이미 존재하면 실패한다.
     ㅂ. O_NOCTTY : 열고자 하는 tty와 프로세스가 제어하는 tty를 가지지 않았다면 즉 같은 제어권
                   이 제어하는 tty가 되지 못한다.
     ㅅ. O_TRUNC  : 파일이 존재하면 길이를 0으로 줄인다.
     ㅇ. O_APPEND : 현재 파일의 포인터를 파일의 마지막에 위치하게 한다.
     ㅈ. O_NONBLOCK : 프로세스가 지연없이 완료되지 않으면 완료되기 전에 반환한다.
     ㅊ. O_NODELAY  : 0_NONBLOCK과 같다.
     ㅋ. O_SYNC     : 기록이 완료되는 경우 반환
     ㅍ. O_DIRECTORY : 디렉토리가 아니면 open함수는 실패된다.
     ㅌ. O_LARGEFILE : 32bit시스템의 매우 큰파일 형태
   3) 사용예
      fd = open("/etc/passwd",O_RDONLY)
      => /etc/passwd파일을 읽기전용으로 연다.
(3) close() : 소켓을 닫을 때 사용한다. 정규 파일 기술자에 관한 close()를 사용하면 된다.
   1) 사용법
     #include<unistd.h>
     int close(int sockfd);
   2)특징
     ㄱ. 신뢰성 기반의 TCP를 이용한 연결에서 사용된다.
     ㄴ. 데이터 송수신 작업이 모두 끝난 소켓은 파일에서와 마찬가지로 close()시스템 호출을 이용
        하여 닫기 작업을 수행한다.
     ㄷ. 소켓을 닫을 때는 언제나 양 종단에서 소켓을 닫아야 한다.
   3) 사용예
     close(sockfd);
      => 더 이상의 입출력은 불가능 해지며 누구든지 원격지에서 이 소켓에 읽고 쓰려고 할 경우에는
        에러가 발생한다.
(예제1) 리턴되는 파일 기술자(file descriptor)출력해보기 (open_socket.c)
  1)설명 : /etc/passwd와 /etc/hosts라는 파일을 열어서 리턴되는 파일 기술자값을 십진수형태로
         출력하고 tcp소켓과 udp소켓을 열어 리턴되는 파일기술자값도 알아보도록 한다.
  2)프로그램
   #include<stdio.h>
   #include<sys/types.h>
   #include<sys/stat.h>
   #include<fcntl.h>
   #include<sys/socket.h>
   int main()
   {
     int fd1,fd2,sd1,sd2;
     fd1=open("/etc/passwd",O_RDONLY,0);
     printf("/etc/passwd's file descriptor=%d\n",fd1);
     sd1=socket(AF_INET,SOCK_STREAM,0);
     printf("stream socket desciptor=%d\n",sd1);
     sd2=socket(AF_INET,SOCK_DGRAM,0);
     printf("datagram socket descriptor=%d\n",sd2);
     fd2=open("/etc/hosts",O_RDONLY,0);
     printf("/etc/hosts's file descriptor=%d\n",fd2);
     close(fd2);
     close(fd1);
     close(sd2);
     close(sd1);
   }
  3)사용예
   [posein@www posein]$ ./a.out
   /etc/passwd's file descriptor=3
   stream socket desciptor=4
   datagram socket descriptor=5
   /etc/hosts's file descriptor=6
(4)fork() : 프로세스를 복사하는 함수이다. 이 함수를 이용하면 부모 프로세스를 복제한 자식 프로
            세스를 생성한다. 즉, 자식 프로세스는 부모 프로세스의 코드, 자료, 스택, 열린 파일
            기술자, 시그널 테이블의 복사본을 상속받는다.
   1) 사용예
     pid=fork()
      => 현재 프로세스를 복사하여 pid에 값을 넘겨준다. 만약 fork()가 성공하면 부모프로세스에게
        는 자식의 PID를 반환하며, 자식 프로세스에게는 0을 반환한다. 만약 실패한다면 부모프로
        세스에게 -1을 반환하며, 자식 프로세스는 생성되지 않는다.
(5) getpid() : 현재 프로세스의 PID를 얻는다.
(6) getppid() : 부모 프로세스의 PID를 얻는다.
(예제2) 현재 프로세스값 출력하기1
  1)설명 : 현재 실행되는 프로세스아이디값을 출력하고 fork()수행후 프로세스 아이디값을 출력
  2)프로그램
   #include<stdio.h>
   main()
   {
     printf("Current process[%d]\n",getpid());
     fork();
     printf("New Current process[%d]\n",getpid());
   }
(예제3) 현재 프로세스값 출력하기2
  1) 설명 : 현재 실행되는 프로세스아이디값을 출력하고 복제되는 자식프로세스의 아이디값도
          출력해본다.
  2) 프로그램
   #include<stdio.h>
   main()
   {
     int pid;
     printf("I'm original process[%d]\n",getpid());
     pid=fork();
     printf("I'm parent of client process[%d]\n",getpid());
     if(pid==0)            //비교해서 0이면 자식프로세스이다.
     {
        printf("I'm child process[%d]\n",getpid());
     }
     else                 //그렇지 않으면 부모프로세스이다.
     {
        printf("I'm parent process[%d]\n",getpid());
     }
     printf("[%d]process terminate\n",getpid());
   }
  3) 사용예
    [posein@www posein]$ ./a.out
    I'm original process[10057]            // 원래의 프로세스아이디를 출력한다.
    I'm parent of client process[10057]    // 복제를 했으므로 getpid()함수를 사용하면 두개의
    I'm parent of client process[10058]    //프로세스값을 출력해낸다.
    I'm child process[10058]
    [10058]process terminate
    I'm parent process[10057]
    [10057]process terminate
(예제4) 현재 프로세스값 출력하기3
  1)설명 : getpid(),getppid()함수를 이용한 간단한 출력
  2)프로그램
   #include<stdio.h>
   main()
   {
    int pid;
    printf("I'm the original process with PID %d and PPID %d.\n",getpid(),getppid());
   }
  3)사용예
   [posein@www posein]$ ./a.out
   I'm the original process with PID 10381 and PPID 1172.
(예제5) 현재 프로세스값 출력하기4
  1)설명 : fork(),getpid(),getppid()함수를 이용한 출력
  2)프로그램
   #include<stdio.h>
   main()
   {
    int pid;
    printf("I'm the original process with PID %d and PPID %d.\n",getpid(),getppid());
    pid=fork();         // 프로세스 복제
    if (pid!=0)         // pid가 0이 아니므로 부모프로세스를 나타냄
   {
    printf("I'm the parent process with PID %d and PPID %d.\n",getpid(),getppid());
    printf("My child's PID %d\n",pid);
   }
    else               // pid가 0인 경우이므로 자식 프로세스
    {
      printf("I'm the child process with PID %d and PPID %d,\n",getpid(),getppid());
    }
    printf("PID %d terminates.\n",getpid());         // 양 프로세스 모두 이를 실행
   }
  3)사용예
   [posein@www posein]$ ./a.out
   I'm the original process with PID 10405 and PPID 1172.
   I'm the parent process with PID 10405 and PPID 1172.
   My child's PID 10406
   I'm the child process with PID 10406 and PPID 10405,
   PID 10406 terminates.
   PID 10405 terminates.
(7) 순서바꾸기 관련 함수 : 이기종컴퓨터 및 네트워크 프로토콜간에 바이트순서를 변환시켜 준다.
   1) 바이트순서 : 바이트 순서에는 호스트 바이트순서와 네트웍 바이트순서가 있다.
    ㄱ. 호스트 바이트순서 : 말 그대로 컴퓨터가 내부 메모리에 숫자를 저장하는 순서를 말한다.
                         이것은 컴퓨터 CPU종류에 따라 다르다 .예를 들면 두 바이트로 구성된
                         십진수 50146의 경우 hexa로 0xC3E2이며 이것을 일반PC계열, 즉 80x86
                         계열의 CPU에서는 E2,C3의 순서(즉 하위 바이트부터)메모리에 저장된다.
                         그러나 MC68000계열의 CPU에서는 C3,E2의 순서로 메로리에 저장된다.
    ㄴ. 네트웍 바이트순서 : 네트웍에서 바이트 단위로 데이타가 전달되는 순서를 말한다. 2바이트
                         의 수 0xC3E2인 경우 C3,E2의 순서로 즉 상위바이트부터 전송된다. 즉
                         80x86계열의 CPU가 사용하는 호스트 바이트순서는 네트웍바이트순서와
                         다르다. 따라서, 80x86계열의 컴퓨터에서 네트웍을 통하여 전송한 데이터
                         를 68000계열의 컴퓨터가 수신하면 바이트 순서가 바뀌게 된다.
   2) 순서바꾸기 함수 : 컴퓨터 내부에서 만들어진 호스트 바이트 순서의 데이터를 네트웍으로 전송
                     하기 전에 htons() (host to network short의 약자)함수를 사용하여 자신에게
                     맞는 호스트 바이트순서로 항상 바꾸어야 한다. 반대로 네트웍에서 수신한
                     데이타는 ntohs()함수를 사용하여 자신의 호스트 바이트순서로 바꿔야 한다.
                     즉 네트웍 바이트순서를 지켜 데이터를 전송함으로써 수신한 데이터가 어떤
                     종류의 컴퓨터에서 만들어진 것인지를 알 필요가 없도록 하는 것이다. 참고로
                     MC68000계열의 CPU에서는 호스트 바이트순서와 네트웍바이트순서가 같은 것을
                     알 수 있는데 호스트에서의 htons()와 ntohs()함수는 아무 일도 하지 않는다.
   3) 함수의 종류 : Short은 2바이트체계이고 Long 4바이트체계이다.
     htons() : Host to Network Short
     htonl() : Host to Network Long
     ntohs() : Network to Host Short
     ntohl() : Network to Host Long
(8) getservbyname() : 포트주소를 구하는 함수이다.
   1) 사용법
      변수=getservbyname(서비스,연결형태)
   2) 사용예
      pservent=getservbyname("telnet","tcp")
   3) 관련구조
     <netdb.h>
     struct servent {
       char    *s_name;
       char    **s_aliases;           /* alias list*/
       int     s_port;                /* port # */
       char    *s_proto;              /* protocol to use */
      };
(예제6) 특정서비스의 포트번호 출력하기
  1)설명 :  순서바꾸기함수와 getservbyname()함수이용한 간단한 예제
  2)프로그램
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netdb.h>
    main()
    {
     struct servent *pmyservent;
     pmyservent=getservbyname("ftp","tcp");
     if(pmyservent==NULL)
     {
      printf("서비스정보를 얻을 수 없음.\n");
     }
      printf("port number of with ntohs():%d\n",ntohs(pmyservent->s_port));
      printf("port number of without ntohs():%d\n",(pmyservent->s_port));
    }
  3)사용예
    [posein@www posein]$ ./a.out
    port number of with ntohs():21
    port number of without ntohs():5376
(9) gethostbyname() : 도메인 네임으로부터 IP주소를 알기위해 사용하는 함수이다.
   1) 사용법
     gethostbyname(변수);
   2) 관련구조
      <netdb.h>
      struct hostent {
        char    *h_name;              /* alias list */
        char    **h_aliases;          /* address type */
        int     h_addrtype;           /* length of address */
        int     h_length;             /* list of address */
        char    **h_addr_list[0]      /* for backward compatiblity */
      };
(예제7) 도메인 주소를 IP로 변환하기
  1)설명 : 32비트의 IP주소는 편의에 따라 www.yahoo.com과 같은 도메인 네임, 192.168.0.1과 같은
          Dotted decimal 표시법, 이진수 IP표기법 등으로 바꾸어 널리 사용하고 있다. 여기서는
          hostent구조체와 gethostbyname()함수를 이용하여 도메인네임을 Dotted decimal표기법으로
          변환하여 표시한다.
  2)프로그램
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h>

    main(int argc, char *argv[])
    {
        struct hostent *myhost;
        struct in_addr myinaddr;       /* IP주소를 저장할 구조체 */
        int i;

        if (argc < 2)
        {
                printf("사용법 : %s host_name(도메인 네임)\n",argv[0]);
                exit(1);
        }

        myhost=gethostbyname(argv[1]);        /* hostent 구하기 */
        if(myhost==0)
        {
                printf("error occurs...at 'gethostbyname'.\n\n\n");
                exit(1);
        }

        printf("official host name : \t\t%s\n",myhost->h_name);  /* 호스트 이름 출력 */

        i=0;                                     /* 호스트 별명 출력 */
        while(myhost->h_aliases[i]!=NULL)
        {
                printf("aliases name : \t\t%s\n",myhost->h_aliases[i]);
                i++;
        }

        printf("host address type : \t\t%d\n",myhost->h_addrtype);  /* 호스트 주소 체계 출력*/
        printf("length of host address : \t%d\n",myhost->h_length);  /*호스트 주소길이출력*/
                                                                     
        i=0;                         /* 호스트 주소를 dotted decimail형태로 출력 */
        while(myhost->h_addr_list[i] !=NULL)
        {
                myinaddr.s_addr = *((u_long *)(myhost->h_addr_list[i]));
                printf("address for host:\t\t%s\n",inet_ntoa(myinaddr));
                i++;
        }
    }
  3)사용예
    [posein@www posein]$ ./a.out yahoo.co.kr
    official host name :            yahoo.co.kr
    host address type :             2
    length of host address :        4
    address for host:               211.32.119.151
(예제8) 주소변환 예제 프로그램
  1)설명 : 호스트의 도메인 네임과 Dotted decimal주소 두가지 방식으로 입력받아 다음과 같이
          4가지 형태로 출력한다.
    ㄱ. 입력된 목적지 도메인 네임으로부터 IP주소를 찾아 화면에 출력(gethostbyname()이용)
    ㄴ. 입력받은 IP주소를 Dotted deciaml주소로 변경하여 출력 (inet_ntoa()이용)
    ㄷ. 입력된 Dotted Decimal주소로부터 IP주소를 찾아 화면에 출력 (gethostbyaddr()이용)
    ㄹ. 세번째구한 hostent구조체내에 있는 목적지 도메인 네임을 화면에 출력
  2)프로그램
    #include<netdb.h>
    #include<stdio.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    int main(int argc, char *argv[])
    {
      struct hostent *ptr_he;     /* 호스트의 정보를 저장하는 구조체 */
      struct in_addr in;          /* 인터넷 주소를 저장하는 구조체 */
      char *hname;                /* 호스트 이름 */
      char *haddr;                /* Dotted decimal IP 문자열 */
      if(argc!=3)
      {
       printf("usage:hostname ip_address\n",argv[0]);
       return -1;
      }
     hname=argv[1];
     haddr=argv[2];

     if((ptr_he=gethostbyname(hname)))  /* 호스트 도메인네임으로부터 호스트정보구하기 */
      {
        memcpy(&in.s_addr,ptr_he->h_addr_list[0],sizeof(in.s_addr));
        printf("%s's binary ip address(hexa) : 0x%x\n",hname,in.s_addr);
        printf("%s's dotted decimal IP address : %s\n",hname,inet_ntoa(in));
            /* IP주소를 Dotted decimal형태의 IP로 변환 */
      }
     else
      {
       printf("gethostbyname error.\n");
      }

     if((in.s_addr=inet_addr(haddr))>0) /* 입력된 Dotted decimal IP주소에서 bianry IP구하기 */
      {
        ptr_he=gethostbyaddr((char *)&(in.s_addr),sizeof(in.s_addr),AF_INET);
        printf("%s's binary IP address(hexa) : 0x%x\n",haddr,in.s_addr);
        printf("%s's hostname : %s\n",haddr,ptr_he->h_name);
      }
     else
      {
        printf("inet_addr error.\n");
      }
    }
  3)사용예
    [posein@www posein]$ ./a.out yahoo.com 203.247.40.252
    yahoo.com's binary ip address(hexa) : 0xf56c73d8
    yahoo.com's dotted decimal IP address : 216.115.108.245
    203.247.40.252's binary IP address(hexa) : 0xfc28f7cb
    203.247.40.252's hostname : mybestone.com
(10) perror() : 시스템 호출은 어떤 경우에 실패할 수가 있다. 예를 들면 존재하지 않는 파일을
                읽기 위해 open()으로 파일을 열려고 한다면 그 호출은 실패하고 시스템 호출은
                에러가 발생하면 -1값을 반환한다. 그러나 이 값은 에러가 발생한 이유를 충분히
                설명하지 못한다. 체계적인 에러 호출을 위해 사용되는 함수가 perror()함수이다.
                즉 시스템 호출 에러를 서술하는 서브루틴(Subroutine)이다. 이 함수는 에러 호출
                관련변수인 errno와 같이 사용하여 에러 처리한다.
   1)에러호출관련변수(errno)
     가장 최근의 시스템 호출 에러의 숫자코드를 저장하고 있는 전역변수이다. 모든 프로세스는 이
    전역변수를 포함하고 있다. "errno"는 프로세스가 생성될 때 처음에는 0으로 설정된다. 그 후
    시스템 호출 에러가 발생하면, errno는 그 원인과 관련된 숫자 코드로 설정된다. 가령 존재하지
    않는 파일을 읽으려고 열면 "errno"는 2로 설정된다.
   2)사용법
     perror("문자열");
      => 에러를 표시하기 위한 문자열을 넣는다.
   3)사용예
     ㄱ.프로그램
       #include<stdio.h>
       #include<sys/file.h>
       #include<errno.h>
       main()
       {
         int fd;
         fd=open("nonexist.txt",O_RDONLY);  /* 에러를 발생을 위해 존재하지 않는 파일 열기 */
         if (fd==-1)                   /* open()함수가 에러이므로 -1값을 리턴함으로 수행 */
         {
           printf("errno=%d\n",errno);
           perror("main");
         }
         fd=open("/",O_WRONLY);      /* 다른 에러 발생 유도 */
         if(fd==-1)                  /* 역시 open()함수가 -1값을 리턴함으로 수행 */
         {
           printf("errno=%d\n",errno);
           perror("main");
         }
         fd=open("nonexist.txt",O_RDONLY|O_CREAT, 0644);  /* 시스템 호출을 성공적으로 수행 */
          /* nonexist.txt라는 파일을 읽기전용으로 열고 만약 없으면 생성한다. 허가권값은 644 */
         printf("errno=%d\n",errno);
         perror("main");
         errno=0;                           /* 인위적으로 변수 error를 0으로 설정 */
         perror("main");
       }
     ㄴ.실행
       [posein@www posein]$ ./a.out            // 프로그램 실행
       errno=2
       main: No such file or directory
       errno=21                                // 성공적 호출 뒤에도 그 값이 유지
       main: Is a directory
       errno=21
       main: Is a directory
       main: Success
(예제9) 간단한 스트림 서버
  1)설명 : 이 서버가 하는 일은 오직 스트림 접속을 하게 되는 모든 클라이언트에게 "Hello,World!"
          라는 문구열을 출력해준다.
  2)프로그램(server.c)
    #include<stdio.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<string.h>
    #include<sys/types.h>
    #include<netinet/in.h>
    #include<sys/socket.h>
    #include<sys/wait.h>

    #define MYPORT 3490          /* 접속에 사용할 포트를 정의 */
    #define BACKLOG 10           /* 접속을 위해 설정되는 컨넥션의 수 정의 */

    main()
    {
      int sockfd, new_fd;
      struct sockaddr_in my_addr;
      struct sockaddr_in their_addr;
      int sin_size;

      if ((sockfd=socket(AF_INET, SOCK_STREAM,0))==-1)
       // socket()함수는 파일기술자(File Descriptor)를 리턴하며 에러시에는 -1을 리턴한다. //
       //즉, 여기서는 에러인 경우에는 socket에서 에러가 발생했다는 메시지를 표시한다. //
      {
        perror("socket");
        exit(1);
      }

      my_addr.sin_family=AF_INET; /* 보통 어떠한 프로토콜을 사용할 지를 정한다.*/
      my_addr.sin_port=htons(MYPORT); /* 사용하는 포트를 네트워크 바이트순서함수로 값 변환 */
      my_addr.sin_addr.s_addr=INADDR_ANY; /* Internet address, 즉 IP값을 저장한다. */
      bzero(&(my_addr.sin_zero),8);

      if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))==-1)
      {
        perror("bind");
        exit(1);
      }

      if (listen(sockfd, BACKLOG)==-1)
      {
        perror("listen");
        exit(1);
      }

      while(1)
      {
        sin_size=sizeof(struct sockaddr_in);
        if ((new_fd=accept(sockfd,(struct sockaddr *)&their_addr, &sin_size))==-1)
        {
          perror("accept");
          continue;
        }
        printf("server: got connection from %s\n",inet_ntoa(their_addr.sin_addr));

        if (!fork())
        {
          if (send(new_fd, "Hello, world!\n",14,0)==-1)
          perror("send");
          close(new_fd);
          exit(0);
        }
        close(new_fd);

        while(waitpid(-1,NULL,WNOHANG)>0);
      }
    }
   3)사용예
     ㄱ.서버프로그램실행시키기
       [posein@www posein]$ ./a.out                    // 실행시킴
       server: got connection from 210.123.193.194     // 접속시 접속한 곳을 정보를 출력
       server: got connection from 203.247.40.252     
     ㄴ.외부클라이언트에서 접속하기
       [posein@www posein]$ telnet 203.247.40.252 3490
       Trying 203.247.40.252...
       Connected to mybestone.com (203.247.40.252).
       Escape character is '^]'.
       Hello, world!                                   // 문구열이 출력되었다.
       Connection closed by foreign host.
(예제10)간단한 스트림 클라이언트
  1)설명 : 서버스트림이 작동할 경우 해당서버를 명기해주면 해당서버로부터 문자열을 입력받아 출력
         한다.
  2)프로그램(client.c)
    #include<stdio.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<netdb.h>
    #include<sys/types.h>
    #include<netinet/in.h>
    #include<sys/socket.h>

    #define PORT 3490
    #define MAXDATASIZE 100

    int main(int argc, char *argv[])
    {
      int sockfd, numbytes;
      char buf[MAXDATASIZE];
      struct hostent *he;
      struct sockaddr_in their_addr;

      if ( argc !=2)
      {
        fprintf(stderr, "usage: client hostname\n");
        exit(1);
      }

      if ((he=gethostbyname(argv[1])) == NULL)
      {
        herror("gethostbyname");
        exit(1);
      }

      if ((sockfd=socket(AF_INET, SOCK_STREAM, 0)) == -1)
      {
        perror("socket");
        exit(1);
      }

      their_addr.sin_family=AF_INET;
      their_addr.sin_port=htons(PORT);
      their_addr.sin_addr=*((struct in_addr *)he->h_addr);
      bzero(&(their_addr.sin_zero),8);

      if(connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) ==-1)
      {
        perror("connect");
        exit(1);
      }

      if ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) ==-1)
      {
        perror("recv");
        exit(1);
      }

      buf[numbytes] ='\0';
      printf("Received: %s",buf);
      close(sockfd);
      return 0;
    }
  3)사용예
    [posein@www posein]$ ./client localhost
    Received: Hello, world!
(11) sockpair() : pipe()시스템 호출과 유사하게 동작하는데, 이 시스템콜은 2개의 소켓설명자를
                  반환한다. 2개의 소켓설명자는 파이프에서와 같이 통신 통로의 양극단을 나타내지
                  만 파이프처럼 단방향이 아니고 양방향이다. 소켓설명자 0번과 1번에서 각각 데이
                  터를 읽고 쓸 수 있다.
   1)사용법
     #include<sys/type.h>
     #include<sys/socket.h>

     int socketpair(int domain, int type, int protocol, int sockfd[2]);
   2)사용예
     int rc, sockfd[2]

     rc=socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd);
(12) system() : 시스템 내부의 명령어를 실행시킬 때 사용한다.
   1)사용법
     system("command");
   2)사용예
     system("ping -c 5 localhost");
(예제11) 프로세스 상태 출력하기
  1) 설명 : socketpair()를 사용하여 소켓기술자값도 출력하고 프로세스상태도 출력한다.
  2) 프로그램 (sockpair.c)
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<errno.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/socket.h>

    int main(int argc, char **argv)
   {
      int z;
      int s[2];

      z=socketpair(AF_LOCAL, SOCK_STREAM,0,s);

     if (z==-1)
     {
       fprintf(stderr,"%s: socketpair(AF_LOCAL,SOCK_STREAM,0)\n", strerror(errno));
       return 1;
     }

     printf("s[0]=%d;\n",s[0]);
     printf("s[1]=%d;\n",s[1]);

     system("netstat --unix -p");
     return 0;
     }
  3)사용예
    [posein@www posein]$ ./a.out
    s[0]=3;                       // 소켓기술자1의 값을 출력 (위 예제1 참조)
    s[1]=4;                       // 소켓기술자2의 값을 출력
    (Not all processes could be identified, non-owned process info
     will not be shown, you would have to be root to see it all.)
    Active UNIX domain sockets (w/o servers)
    Proto RefCnt Flags       Type       State         I-Node PID/Program name    Path
    unix  16     [ ]         DGRAM                    892    -                   /dev/log
    unix  3      [ ]         STREAM     CONNECTED     87133  8540/a.out
    unix  3      [ ]         STREAM     CONNECTED     87132  8540/a.out
    unix  2      [ ]         DGRAM                    84704  -
    unix  2      [ ]         DGRAM                    84642  -
    unix  2      [ ]         DGRAM                    43272  -
    unix  2      [ ]         DGRAM                    14241  -
    unix  2      [ ]         DGRAM                    12194  -
    unix  2      [ ]         DGRAM                    4606   -
    unix  2      [ ]         DGRAM                    4016   -
    unix  2      [ ]         DGRAM                    3962   -
    unix  2      [ ]         DGRAM                    3925   -
    unix  2      [ ]         DGRAM                    1292   -
    unix  2      [ ]         DGRAM                    1105   -
    unix  2      [ ]         DGRAM                    1072   -
    unix  2      [ ]         DGRAM                    940    -
    unix  2      [ ]         DGRAM                    904    -
    unix  2      [ ]         STREAM     CONNECTED     540    -

(13) shutdown() : close()보다 좀더 선택적인 제어를 하면서 연결을 끊을 수 있다. 즉 특정방향으
                 로의 통신만을 끊을 수 있게 된다.
   1)사용법
     #include<sys/socket.h>

     int shutdown(int sockfd, int how);
      => sockfd는 소켓 기술자이며, how는 다음과 같다.
        0(SHUT_RD) : 더 이상의 수신금지
        1(SHUT_WR) : 더 이상의 송신금지
        2(SHUT_RDWR) : 더 이상의 송수신금지 => close()와 같은 경우에 해당
   2) 특징 : 성공하면 소켓기술자값을 리턴하고 실패하면 -1값을 리턴한다.
   2) 참고 : 연결도 되지않은 데이터그램 소켓에 shutdown()을 사용한다면 단지 send(), recv()를
           사용하지 못하게만 만든다. connect()를 사용한 경우에만 이렇게 사용할 수 있다.
(예제12) PID 출력 및 버퍼값 출력하기
  1) 설명 : socketpair()함수를 사용하여 시간을 출력하고 getppid()를 이용하여 프로세스아이디를
          출력한다.
  2)프로그램(sockpair2.c)
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<errno.h>
    #include<string.h>
    #include<time.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<sys/wait.h>

    #ifndef SHUT_WR
    #define SHUT_RD   0
    #define SHUT_WR   1
    #define SHUT_RDWR 2
    #endif

    int main(int argc, char **argv)
    {
      int z;
      int s[2];
      char *msgp;
      int mlen;
      char buf[80];
      pid_t chpid;
      z=socketpair(AF_LOCAL, SOCK_STREAM, 0, s);

      if ( z==-1)
      {
        fprintf(stderr,"%s:socketpair(2)\n",strerror(errno));
        exit(1);
      }

      if ((chpid=fork())==(pid_t)-1)
      {
        fprintf(stderr,"%s: fork(2)\n",strerror(errno));
        exit(1);
      }
      else if (chpid==0)
      {
        char rxbuf[80];
        printf("Parent PID is %ld\n",(long)getppid());
        close(s[0]);
        s[0]=-1;
        msgp = "%A %d-%b-%Y %1:%M %p";
        mlen =strlen(msgp);

        printf("Child sending request '%s'\n",msgp);
        fflush(stdout);

        z=write(s[1],msgp,mlen);

        if (z<0)
        {
          fprintf(stderr,"%s:write(2)\n",strerror(errno));
          exit(1);
        }

        if (shutdown(s[1],SHUT_WR)==-1)
        {
          fprintf(stderr,"%s:shutdown(2)\n",strerror(errno));
          exit(1);
        }

        z=read(s[1],rxbuf,sizeof rxbuf);
        if ( z<0 )
        {
          fprintf(stderr,"%s:read(2)\n",strerror(errno));
          exit(1);
        }
        rxbuf[z]=0;

        printf("Server returned '%s'\n",rxbuf);
        fflush(stdout);
        close(s[1]);
      }
      else
      {
        int status;
        char txbuf[80];
        time_t td;

        printf("Child PID is %ld\n",(long)chpid);
        fflush(stdout);

        close(s[1]);
        s[1]=-1;

        z=read(s[0],buf,sizeof buf);

        if ( z< 0)
        {
          fprintf(stderr,"%s:read(2)\n", strerror(errno));
          exit(1);
        }

        buf[z]=0;
        time(&td);

        strftime(txbuf,sizeof txbuf, buf, localtime(&td));
        z=write(s[0],txbuf,strlen(txbuf));

        if ( z<0 )
        {
          fprintf(stderr,"%s:write(2)\n",strerror(errno));
          exit(1);
        }
        close(s[0]);
        waitpid(chpid,&status,0);
       }
       return 0;
    }
  3)사용예
    [posein@www posein]$ ./a.out
    Child PID is 8714
    Parent PID is 8713
    Child sending request '%A %d-%b-%Y %1:%M %p'
    Server returned 'Wednesday 02-Jan-2002 %1:49 PM'

4. 도메인(Domain)과 주소군(Address Family)
(1) UNIX도메인형식
   1)사용법
     #include<sys/un.h>
     struct sockaddr_un {
        sa_family_t   sun_family;                    /* Address Family */
        char          sun_path[108];                 /* Pathname */
     }
      => sun_family : AF_UNIX(AF_LOCAL)
         sun_path : UNIX경로명('\0'바이트는 없어도 된다.)
         (참고) 전통적인 주소와 추상적인 주소란 말이 있는데 추상적인 주소는 첫번째 바이트가
              '\0'이다.
   2)설명
     ㄱ. 이 주소형식은 같은 호스트 상의 두 프로세스가 통신하기 위해 생성한 소켓에 의해 이용
        된다. 예를 들면 지역프린터로 파일을 출력하려고 하는 경우에 호스트의 스풀링서비스를
        받기위해 이 지역 소켓을 사용한다.
     ㄴ. 전통적인 주소는 sun_path에 기록된 경로명은 파일시스템 내의 실제 파일을 가리켜야 한다.
       물론 이 주소를 사용하기 위해서는 프로세스 경로에 나타난 모든 디렉토리와 소켓파일이 생성
       될 디렉토리에 필요한 접근권한이 있어야 한다.
     ㄷ. 전통적인 주소의 단점으로는 사용한 소켓 파일이 지워지지 않았다면 같은이름으로 bind()
        시스템 호출을 호출하면 에러가 반환된다는 점이다. 리눅스커널 2.2에서는 추상적인 주소를
        갖는 소켓을 생성할 수 있다. 즉 경로명을 나타내는 문자열의 첫번째 바이트를 '\0'문자로
        만들면 파일 시스템내에 소켓 파일이 생성되지 않는다.
(예제13) sockaddr_un형식의 주소를 소켓이 가지도록 설정하기
  1)설명 : sockaddr_un형식의 주소를 소켓이 갖도록 설정하고 netstat명령을 실행시켜 확인해 본다.
  2)프로그램 (unixsock.c)
    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<sys/socket.h>
    #include<sys/un.h>

    static void
    bail(const char *on_what)
    {
      perror("on_what");
      exit(1);
    }

    int main(int argc, char **argv, char **envp)
    {
      int z;
      int sck_unix;
      struct sockaddr_un adr_unix;
      int len_unix;
      const char pth_unix[]="/tmp/my_sock";
      sck_unix=socket(AF_UNIX,SOCK_STREAM,0);
      if ( sck_unix==-1)
       bail("socket()");
      unlink(pth_unix);

      memset(&adr_unix,0,sizeof adr_unix);
      adr_unix.sun_family=AF_UNIX;

      strncpy(adr_unix.sun_path,pth_unix,sizeof adr_unix.sun_path-1)
      [sizeof adr_unix.sun_path-1]=0;

      len_unix=SUN_LEN(&adr_unix);

      z=bind(sck_unix,(struct sockaddr *)&adr_unix,len_unix);

      if ( z==-1)
        bail("bind()");
      system("netstat -pa --unix 2>/dev/null |" "sed -n '/^Active UNIX/,/^proto/p;""/af_unix/p'");
      close(sck_unix);
      unlink(pth_unix);

      return 0;
    }
  3)사용예
    [posein@www posein]$ ./a.out
    Active UNIX domain sockets (servers and established)
    Proto RefCnt Flags       Type       State         I-Node PID/Program name    Path
    unix  2      [ ACC ]     STREAM     LISTENING     4039   -                   /tmp/mysql.so
    ck
    unix  2      [ ]         STREAM                   88376  8838/a.out          /tmp/my_sock
    unix  2      [ ACC ]     STREAM     LISTENING     3959   -                   /tmp/.font-un
    ix/fs7100
    unix  16     [ ]         DGRAM                    892    -                   /dev/log
    unix  2      [ ACC ]     STREAM     LISTENING     1313   -                   /dev/gpmctl
    unix  2      [ ]         DGRAM                    84704  -
    unix  2      [ ]         DGRAM                    84642  -
    unix  2      [ ]         DGRAM                    43272  -
    unix  2      [ ]         DGRAM                    14241  -
    unix  2      [ ]         DGRAM                    12194  -
    unix  2      [ ]         DGRAM                    4606   -
    unix  2      [ ]         DGRAM                    4016   -
    unix  2      [ ]         DGRAM                    3962   -
    unix  2      [ ]         DGRAM                    3925   -
    unix  2      [ ]         DGRAM                    1292   -
    unix  2      [ ]         DGRAM                    1105   -
    unix  2      [ ]         DGRAM                    1072   -
    unix  2      [ ]         DGRAM                    940    -
    unix  2      [ ]         DGRAM                    904    -
    unix  2      [ ]         STREAM     CONNECTED     540    -

(예제14) sockaddr_un형식의 주소를 소켓이 가지도록 설정하기2
  1)설명 : sockaddr_un형식의 주소를 소켓이 갖도록 설정하고 netstat명령을 실행시켜 확인해보도록
         하고 @문자가 추상적인 지역주소를 이용하고 있음을 표시한다.
  2)프로그램 (unixsock1.c)
    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<sys/socket.h>
    #include<sys/un.h>

    static void
    bail(const char *on_what)
    {
      perror("on_what");
      exit(1);
    }

    int main(int argc, char **argv, char **envp)
    {
      int z;
      int sck_unix;
      struct sockaddr_un adr_unix;
      int len_unix;
      const char pth_unix[]="Z*MY-SOCKET*";
      sck_unix=socket(AF_UNIX,SOCK_STREAM,0);
      if ( sck_unix==-1)
       bail("socket()");

      memset(&adr_unix,0,sizeof adr_unix);
      adr_unix.sun_family=AF_UNIX;

      strncpy(adr_unix.sun_path,pth_unix,sizeof adr_unix.sun_path-1)
      [sizeof adr_unix.sun_path-1]=0;

      len_unix=SUN_LEN(&adr_unix);
      adr.unix.sun_path[0]=0;
      z=bind(sck_unix,(struct sockaddr *)&adr_unix,len_unix);

      if ( z==-1)
        bail("bind()");
      system("netstat -pa --unix 2>/dev/null |" "sed -n '/^Active UNIX/,/^proto/p;""/af_uni
      x/p'");
      close(sck_unix);
      return 0;
    }
  3)사용예
    [posein@www posein]$ ./a.out
    Active UNIX domain sockets (servers and established)
    Proto RefCnt Flags       Type       State         I-Node PID/Program name    Path
    unix  2      [ ACC ]     STREAM     LISTENING     4039   -                   /tmp/mysql.so
    ck
    unix  2      [ ]         STREAM                   88836  8935/a.out          @*MY-SOCKET*
    unix  2      [ ACC ]     STREAM     LISTENING     3959   -                   /tmp/.font-un
    ix/fs7100
    unix  16     [ ]         DGRAM                    892    -                   /dev/log
    unix  2      [ ACC ]     STREAM     LISTENING     1313   -                   /dev/gpmctl
    unix  2      [ ]         DGRAM                    84704  -
    unix  2      [ ]         DGRAM                    84642  -
    unix  2      [ ]         DGRAM                    43272  -
    unix  2      [ ]         DGRAM                    14241  -
    unix  2      [ ]         DGRAM                    12194  -
    unix  2      [ ]         DGRAM                    4606   -
    unix  2      [ ]         DGRAM                    4016   -
    unix  2      [ ]         DGRAM                    3962   -
    unix  2      [ ]         DGRAM                    3925   -
    unix  2      [ ]         DGRAM                    1292   -
    unix  2      [ ]         DGRAM                    1105   -
    unix  2      [ ]         DGRAM                    1072   -
    unix  2      [ ]         DGRAM                    940    -
    unix  2      [ ]         DGRAM                    904    -
    unix  2      [ ]         STREAM     CONNECTED     540    -

5. 주소 변환 함수(Address Conversion Functions)
(1) IP주소와 함수 : IP주소를 산정해주는 많은 함수가 존재한다.
   1)예
     ina.sin_addr.s_addr=inet_addr("203.27.40.252")
      => inet_addr()함수를 이용하여 IP주소를 unsigned long변수에 저장한 것이다. 참고로 inet_
        addr()은 htonl을 사용하지 않고 그 결과값으로 NBO값을 돌려준다. 주의할 것은 에러의 경우
       -1을 돌려주게 되는데 이는 unsigned long에서는 255.255.255.255을 뜻하며 이는 broadcast
       주소가 된다.
(2)커널 선정 IP주소 : 프로세스는 소켓의 IP주소를 커널에게 위임할 수도 있다 .2개 이상의 네트
                    워크 인터페이스가 장착된 경우 많이 이용된다. 실제연결이 설정될 때 소켓주소
                    를 결정한다 .포트번호도 커널이 선택한다.
   1)예
     struct sockaddr_in  adr_inet;
     int                 adr_len;

     memset(&adr_inet,0,sizeof(adr_inet);

     adr_inet.sin_family=AF_INET;
     adr_inet.sin_port=ntohs(0);
     adr_inet.sin_addr.s_addr=ntohl(INADDR_ANY);
     adr_len=sizeof(adr_inet);
      => 소켓의 주소는 실제 연결이 설정되는 순간에 커널에 의해 결정된다. sockaddr_in구조체의
        sin_addr.saddr필드에 ntohl(INADDR_ANY)값을 저장하면 된다. 마찬가지로 포트번호도 커널이
        선택할 수 있도록 지정할 수 있는 포트번호 필드를 0값으로 지정한다.
(예제15) 특정IP주소로 소켓의 주소를 지정하기
  1)설명 : 특정IP주소로 소켓의 주소를 지정한다.
  2)프로그램(ipaddr.c)
    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<sys/socket.h>
    #include<netinet/in.h>

    static void bail(const char *on_what)
    {
      perror(on_what);
      exit(1);
    }

    int main(int argc, char **argv, char **envp)
    {
      int z;
      int sck_inet;
      struct sockaddr_in adr_inet;
      int len_inet;
      const unsigned char IPno[]={127,0,0,1};
      sck_inet=socket(AF_INET,SOCK_STREAM,0);
      sck_inet=socket(AF_INET,SOCK_STREAM,0);

      if(sck_inet==-1)
       bail("socket()");
      memset(&adr_inet,0,sizeof adr_inet);

      adr_inet.sin_family=AF_INET;
      adr_inet.sin_port=htons(9000);
      memcpy(&adr_inet.sin_addr.s_addr,IPno,4);
      len_inet=sizeof adr_inet;

      z=bind(sck_inet,(struct sockaddr *)&adr_inet,len_inet);

      if(z==-1)
       bail("bind()");

      system("netstat -pa --tcp 2>/dev/null|" "sed -n '1,/^Proto/p;/af_inet/p'");
      close(sck_inet);
      return 0;
    }
  3)사용예
    [posein@www posein]$ ./a.out
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address        Foreign Address      State      PID/Program name
(3) inet_addr()
   1)사용법
     #include<sys/socket.h>
     #include<netinet/in.h>
     #include&arpa/inet.h>

     unsigned long inet_addr(const char *string);
   2)설명
     ㄱ. 32비트 IP주소로 변환 : 네트워크 바이트순서이다.
         string:dotted-quad표기형식("203.247.40.252")
     ㄴ. 현재는 ipv6까지 지원하지 않는 함수라면 inet_aton을 사용하는 것이 좋다.
     ㄷ. 반환값은 성공했을 경우에는 32비트주소(네트워크 바이트순서)로 리턴하며, 실패할 경우에
        는 -1(INADDR_NONE)를 리턴한다.
(4) inet_aton() : inet_addr()함수의 기능을 개선한 함수라고 할 수 있다.
   1)사용법
     #include<sys/socket.h>
     #include<netinet/in.h>
     #include<arpa/inet.h>

     int inet_aton(const char *string, struct in_addr *addr);
   2)설명
     ㄱ.인자
        a. string : dotted-quad IP주소
        b. addr : 변환될 주소가 저장될 인자
     ㄴ. 반환값 : 성공하면 0이 아닌 값을 리턴하며, 실패할 경우에는 0을 반환한다.
   3)사용예
     AF_INET 소켓의 주소를 다음과 같이 정의했다면
       struct sockaddr_in adr_inet;
     inet_aton()의 두번째인자는 다음과 같은 형태가 전달된다.
       &adr_inet.sin_addr
     그러므로, 실제 호출 형태는
       inet_aton(string,&adr_inet.sin_addr);
     이다.
(5) inet_ntoa()
   1)사용법
     #include<sys/socket.h>
     #include<netinet/in.h>
     #include<arpa/inet.h>

     char *inet_ntoa(struct in_addr addr);
   2)설명
     ㄱ. IP주소를 dotted-quad표기 문자열로 변환
     ㄴ. static문자 배열을 이용 : 반환값은 이 배열의 주소이고 다음번 호출때까지만 결과가 유효
       하다.
   3)사용예
     struct sockaddr_in addr;

     printf("IP ADDR"%s\n",inet_ntoa(addr.sin_addr);
      => 이 함수는 인자를 하나만 받는다. 이 인자는 32비트로 되어 있는 IP주소값이 된다. 이 주소
        를 dotted quad표기 형태의 문자열로 변환한다. 이 변환 작업중에 함수는 내부적으로 할당해
        놓은 정적변수를 이용한다. 변환 결과가 이 정적 문자 배열에 저장되거 함수의 반환값으로
        이용한다. 변환 결과가 이 정적 문자 배열에 저장되고 함수의 반환값으로는 정적 변수의 주소
        가 변환된다. 그러므로 변환 결과는 다음 번 이 함수가 호출되기 전까지만 유효하다. 왜냐하
        면 다음 번 호출이 일어나면 결과가 다시 이 정적 변수에 저장되기 때문이다.
(6) 기타 변환 함수들
   1)사용법
     #include<sys/socket.h>
     #include<netinet/in.h>
     #include<arpa/inet.h>

     unsigned long inet_lnaof(struct in_addr addr);
     unsigned long inet_netof(struct in_addr addr);
     unsigned long inet_makeaddr(int net,int host);
   2)설명
     ㄱ. inet_lnaof() : 32비트주소(네트워크 바이트순서) => 32비트 호스트번호(호스트순서)
     ㄴ. inet_netof() : 32비트주소(네트워크 바이트순서) => 32비트 네트워크번호(호스트순서)
     ㄷ. inet_makeaddr() : 네트워크번호 + 호스트번호 => 32비트 IP주소(네트워크바이트순서)
(7) getpeername()
   1)사용법
     #include <sys/socket.h>

     int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);
   2)설명
     ㄱ. 상대편의 스트림 소켓에 누가 연결했는지 알려준다.
     ㄴ. sockfd : 스트림소켓 기술자
     ㄷ. addr : struct sockaddr(또는 struct sockaddr_in)의 포인터(상대편정보를 갖음)
     ㄹ. addrlen : 구조체의 크기를 나타내는 정수를 가리키는 포인터
     ㅁ. 반환값으로는 성공하면 0을 리턴하며, 실패하면 -1을 리턴한다.
(8)gethostname()
   1)사용법
     #include<unistd.h>

     int gethostname(char *hostname, size_t size);
   2)설명
     ㄱ. 현재 프로그램이 실행중인 컴퓨터의 이름을 알려준다.
     ㄴ. Hostname은 문자열의 포인터(함수가 돌려주는 값을 넣을 변수이다.)
     ㄷ. size는 문자열의 크기이다.
     ㄹ. 반환값으로는 성공하면 0을 리턴하며, 실패하면 -1을 리턴한다.

6. socket()과 bind()
(1)bind()
   1)사용법
     #include<sys/types.h>
     #include<sys/socket.h>

     int bind(int sockfd, sockaddr *my_addr, int addrlen);
   2)설명
     ㄱ. sockfd : socket()함수에서 받은 소켓기술자
     ㄴ. my_addr : struct sockaddr에 대한 포인터(IP주소에 관한 정보를 갖고 있음)
     ㄷ. addrlen : 구조체의 크기(sizeof(struct sockaddr))
     ㄹ. 반환값은 성공하면 0을 리턴하며, 실패하면 -1을 리턴한다.
   3)특징 : bind()함수를 호출하지 않아도 되는 경우가 있는데 그것을 예를 들면 다른 호스트에
          connect()함수를 이용해 연결하고자 하는 경우에는 자신의 포트에 관심을 두지 않아도
          된다. 수신측은 목적지로 연결을 시도할 때 자신의 포트번호는 1024보다 큰 것으로 random
          하게 잡고 간다.
(2)Port와 주소의 자동지정
   1)사용법
     my_addr.sin_port=0;
     my_addr.sin_addr.s_addr=INADDR_ANY;
   2)설명
     ㄱ. my_addr.sin_port는 '0'으로 설정되면 쓰이고 있지않은 포트번호를 지정해준다. 주의할 점
        은 1024보다 작은 번호는 모두 예약된 포트번호이다. 따라고 그 보다 큰범위로부터 65535까지
        의 번호를 할당한다.
     ㄴ. My_addr.sin_addr.s_addr을 INADDR_ANY로 지정하는 것은 지금 자신의 IP주소를 자동으로
        지정해 주라는 의미이다.
(예제16) bind()함수를 이용하여 소켓을 만들어보기
  1)설명 : bind()함수 이용해 본다.
  2)프로그램(bind.c)
    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>

    static void bail(const char *on_what)
    {
      perror(on_what);
      exit(1);
    }

    int main(int argc, char **argv, char **envp)
    {
      int z;
      int sck_inet;
      struct sockaddr_in adr_inet;
      int len_inet;

      sck_inet=socket(AF_INET, SOCK_STREAM, 0);
      if (sck_inet==-1)
       bail("socket()");
      memset(&adr_inet,0,sizeof adr_inet);

      adr_inet.sin_family=AF_INET;
      adr_inet.sin_port=htons(9000);

      inet_aton("127.0.0.24",&adr_inet.sin_addr);

      len_inet=sizeof adr_inet;

      z=bind(sck_inet,(struct sockaddr *)&adr_inet,len_inet);

      if(z==-1)
       bail("bind()");
      system("netstat -pa --tcp 2>/dev/null |""sed -n '1,/^Proto/p;/bind/p'");
      close(sck_inet);
      return 0;
    }
   3)사용예
     [posein@www posein]$ ./a.out
     Active Internet connections (servers and established)
     Proto Recv-Q Send-Q Local Address        Foreign Address      State     PID/Program name

7. 비연결성 프로토콜(Connectionless Protocols)
(예제17) 데이터그램서버
  1)설명 : 데이타그램을 이용한 서버프로그램을 실행시켜 본다.
  2)프로그램(dgramsrvr.c)
    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<string.h>
    #include<time.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>

    static void bail(const char *on_what)
    {
      fputs(strerror(errno),stderr);
      fputs(": ",stderr);
      fputs(on_what,stderr);
      fputc('\n',stderr);
      exit(1);
    }

    int main(int argc, char **argv)
    {
      int z;
      char *srvr_addr =NULL;
      struct sockaddr_in adr_inet;
      struct sockaddr_in adr_clnt;
      int len_inet;
      int s;
      char dgram[512];
      char dtfmt[512];
      time_t td;
      struct tm tm;

      if ( argc >= 2)
      {
        srvr_addr=argv[1];
      }
      else
      {
        srvr_addr="127.0.0.23";
      }

      s=socket(AF_INET,SOCK_DGRAM,0);
      if (s==-1)
       bail("socket()");
      memset(&adr_inet,0,sizeof adr_inet);
     adr_inet.sin_family=AF_INET;
     adr_inet.sin_port=htons(9090);
     adr_inet.sin_addr.s_addr=inet_addr(srvr_addr);

     if (adr_inet.sin_addr.s_addr==INADDR_NONE)
      bail("bad address.");
      len_inet=sizeof adr_inet;

     z=bind(s,(struct sockaddr *)&adr_inet,len_inet);
     if (z==-1)
       bail("bind()");
     for( ; ;)
     {
       len_inet=sizeof adr_clnt;
       z=recvfrom(s,
           dgram,
           sizeof dgram,
           0,
          (struct sockaddr *)&adr_clnt,
          &len_inet);
       if ( z<0)
         bail("recvfrom(2)");
        dgram[2]=0;
        if (!strcasecmp(dgram,"QUIT"))
        break;
        time(&td);
        tm = *localtime(&td);
        strftime(dtfmt,
                 sizeof dtfmt,
                 dgram,
                 &tm);
        z=sendto(s,
                 dtfmt,
                 strlen(dtfmt),
                 0,
                 (struct sockaddr *)&adr_clnt,
                 len_inet);
        if (z <0)
          bail("sendto(2)");
      }
      close(s);
      return 0;
    }
  3)사용예
    [posein@www posein]$ ./a.out &
    [1] 10340
    [posein@www posein]$ ./a.out 203.247.40.252 &
    [2] 10341
    [posein@www posein]$ ./a.out 210.123.193.194 &
    [3] 10342
    [posein@www posein]$ Cannot assign requested address: bind()

    [3]+  Exit 1                  ./a.out 210.123.193.194
      => netstat -anp 명령으로 확인해보자.

8. 연결기반(Connection-Oriented) 프로토콜
(1)서버프로그램
(예제18) TCP기반 프로토콜의 서버프로그램을 실행하여 특정포트로 소켓을 설정하기
  1)설명 : TCP기반 프로토콜에서 특정포트를 열어 소켓을 활성화한 후 접속한 클라이언트에게 현재
         시간을 출력해준다.
  2)프로그램(tcpserver.c)
    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<string.h>
    #include<time.h>
    #include<sys/types.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    #include<netdb.h>

    static void bail(const char *on_what)
    {
      if (errno !=0)
      {
        fputs(strerror(errno),stderr);
        fputs(": ",stderr);
      }
      fputs(on_what,stderr);
      fputc('\n',stderr);
      exit(1);
    }

    int main(int argc, char **argv)
    {
      int z;
      char *srvr_addr= NULL;
      char *srvr_port="9099";
      struct sockaddr_in adr_srvr;
      struct sockaddr_in adr_clnt;
      int len_inet;
      int s;
      int c;
      int n;
      time_t td;
      char dtbuf[128];

      if (argc >= 2)
      {
        srvr_addr=argv[1];
      }
      else
      {
        srvr_addr="127.0.0.1";
      }
      if (argc>=3)
        srvr_port = argv[2];
      s = socket(PF_INET, SOCK_STREAM,0);
      if ( s==-1)
       bail("socket()");
      memset(&adr_srvr,0,sizeof adr_srvr);
      adr_srvr.sin_family=AF_INET;
      adr_srvr.sin_port=htons(atoi(srvr_port));
      if ( strcmp(srvr_addr,"*") !=0)
      {
        adr_srvr.sin_addr.s_addr=inet_addr(srvr_addr);
        if (adr_srvr.sin_addr.s_addr==INADDR_NONE)
         bail("bad address.");
      }
      else
      {
         adr_srvr.sin_addr.s_addr=INADDR_ANY;
      }
      len_inet=sizeof adr_srvr;
      z=bind(s,(struct sockaddr *)&adr_srvr,len_inet);
      if ( z==-1)
       bail("bind(2)");
      z=listen(s,10);
      if ( z==-1)
       bail("listen(2)");
      for (;;)
      {
        len_inet=sizeof adr_clnt;
        c=accept(s,
                 (struct sockaddr *)&adr_clnt,
                 &len_inet);
        if ( c==-1)
          bail("accept(2)");
        time(&td);
        n=(int) strftime(dtbuf,sizeof dtbuf,"%A %b %d %M %S %Y\n",
                         localtime(&td));
        z=write(c,dtbuf,n);
        if ( z==-1)
         bail("write(2)");
        close(c);
       }
       return 0;
    }
  3)사용예
    ㄱ. 실행
     [posein@www posein]$ ./a.out &
     [1] 12272
    ㄴ. 확인
      [posein@www posein]$ netstat -p --inet
      Active Internet connections (w/o servers)
      Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
      tcp        1      0 localhost.localdom:9000 localhost.localdo:43128 CLOSE_WAIT  -
    ㄷ. 접속해보기
       [posein@www posein]$ telnet localhost 9099
       Trying 127.0.0.1...
       Connected to localhost.localdomain (127.0.0.1).
       Escape character is '^]'.
       Thursday Jan 03 52 37 2002                      //현재 날짜와 시간등을 출력해준다.
       Connection closed by foreign host.
(2)클라이언트프로그램
(예제19) 서버로부터 메시지를 받아오는 클라이언트 프로그램
  1)설명 : 작동중인 서버프로그램으로부터 특정포트로 접속하여 메시지를 받아오도록 한다.
  2)프로그램(tcpclient.c)
    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    #include<netdb.h>

    static void bail(const char *on_what)
    {
      if(errno !=0)
      {
        fputs(strerror(errno),stderr);
        fputs(": ",stderr);
      }
      fputs(on_what,stderr);
      fputc('\n',stderr);
      exit(1);
    }
    int main(int argc,char **argv)
    {
      int z;
      char *srvr_addr=NULL;
      char *srvr_port="9099";
      struct sockaddr_in adr_srvr;
      int len_inet;
      int s;
      char dtbuf[128];
      if (argc >=2)
      {
        srvr_addr=argv[1];
      }
      else
     {
       srvr_addr="127.0.0.1";
     }
      if (argc>=3)
         srvr_port=argv[2];
      s=socket(PF_INET,SOCK_STREAM,0);
      if (s==-1)
       bail("socket()");
      memset(&adr_srvr,0,sizeof adr_srvr);
      adr_srvr.sin_family=AF_INET;
      adr_srvr.sin_port=htons(atoi(srvr_port));
      adr_srvr.sin_addr.s_addr=inet_addr(srvr_addr);
      if (adr_srvr.sin_addr.s_addr==INADDR_NONE)
       bail("bad address.");
      len_inet=sizeof adr_srvr;

      z=connect(s,&adr_srvr,len_inet);
      if (z==-1)
       bail("connect(2)");
      z=read(s,&dtbuf,sizeof dtbuf-1);
      if(z==-1)
      bail("read(2)");
      dtbuf[z]=0;
      printf("Date & Time is : %s\n",dtbuf);
      close(s);
      putchar('\n');
      return 0;
    }
  3)실행예
    [posein@www posein]$ ./tcpclient
    Date & Time is : Thursday Jan 03 23 31 2002
      => 서버로부터 현재시간을 받아 출력한다.
    [posein@www posein]$ ./tcpclient 9099
    Invalid argument: connect(2)                  //오류형태1
    [posein@www posein]$ ./tcpclient aaa 9099
    bad address.                                  //오류형태2

9. 블로킹과 select()함수
(1)블로킹
   1)블로킹 : 잠자는 듯이 기다리는 것을 의미한다.
   2)예 : recvfrom()함수를 호출하였을 때 데이터가 들어올 때까지 기다리게 되는데 이런 것이
        블로킹이다.
   3)블로킹되는 함수 : accept(), recv(), recvfrom()등
   4)사용하는 이유 : 함수를 블록 가능하게 하는 이유는 소켓이 socket()함수로 만들어 질 때 블록
                  가능하게 설정했기 때문이다.
   5)블로킹방지 : fcntl()함수를 이용하여 블로킹을 방지
     ㄱ. 사용법
        #include<unistd.h>
        #include<fcntl.h>

        sockfd=socket(AF_INET,SOCK_STREAM,0);
        fcntl(sockfd,F_SETFL,O_NONBLOCK);
      ㄴ. 설명
         a. 효과적인 socket이용가능
         b. 데이터가 접수되지 않은 소켓에서 데이터를 읽으려고 시도하면 -1을 반환, errno를
           EWOULDBLOCK으로 세팅한다.
(2)select() : 서버프로세스가 동작하고 있을 경우 이미 연결된 소켓에 데이터가 들어올 경우가
              있다. 중복 입출력은 관리하는 함수이다.
   1)사용법
     #include<sys/time.h>
     #include<sys/types.h>
     #include<unistd.h>

     int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
                struct timeval *timeout);
   2)설명
     ㄱ. 파일 기술자의 집합인 readfds, writefds, exceptfds등을 관리
     ㄴ. numfds : 가장 높은 파일 기술자에 '1'을 더해서 지정함.
     ㄷ. Timeval : 시간간격을 정의함.
   3) struct timeval의 구조
     ㄱ. 사용법
        struct timeval {
            int tv_sec;                             /* seconds */
            int tv_usec;                            /* microseconds */
        }
     ㄴ. 설명
        a. tv_sec은 초단위
        b. tv_usec은 마이크로 초(백만분의 일초)단위
     ㄷ. 특징 : 만약에 timeval변수들을 '0'으로 하면 select()함수는 즉시 그 결과를 돌려주게
              되며 지금의 set의 내용들을 바로 알려주게 된다. 만약에 timeout를 null로 지정
              하면 끝나지 않고 파일 기술자를 기다리게 되고, 어떤 특정한 set의 변화에 관심이
              없다면 그러한 부분을 null로 채우면 된다.
   4) 파일 기술자 집합의 관리 : 각각의 집합은 fd_set형이고 다음에 나오는 매크로형으로 제어가능
     ㄱ. FD_ZERO(fd_set *set) : 파일 기술자 집합을 제거
     ㄴ. FD_SET(int fd_set *set) : fd를 set에 더해줌
     ㄷ. FD_CLR(int fd,fd_set *set) : fd를 set에서 제거
     ㄹ. FD_ISSET(int fd, fd-set *set) : fd가 set안에 있는지 검사

10. 호스트이름과 네트워크이름 검색
(1)uname() : 프로그램이 수행 중인 시스템에 관한 정보를 제공해준다. 지역 호스트에 관한 자세한
             정보를 얻어낼 수 있다.
   1)사용법
     #include<sys/utsname.h>

     int uname(struct utsname *buf);

     struct utsname {
          char    sysname[SYS_NMNL];
          char    nodename[SYS_NMLN];
          char    release[SYS_NMLN];
          char    version[SYS_NMLN];
          char    machine[SYS_NMLN];
          char    domainname[SYS_NMLN];
     }
   2)구조체의 필드값
     ㄱ. sysname : 현재 운용중인 운영체제의 이름
        예) "Linux"
     ㄴ. nodename : 컴퓨터의 네트워크 호스트 이름
        예) "com01"
     ㄷ. release : 운영체제의 배포번호
        예) 2.2.16
     ㄹ. version : 운영체제의 버전, 리눅스는 버전번호, 커널 빌드의 날짜
        예)"#2 SMP Sat Dec 23 10:03:50 KST 2000"
     ㅁ. machine : 하드웨어 유형
        예) "i686"
     ㅂ. domainname : NIS/YP 도메인 이름
   3)설명 : 성공하면 0을 리턴하며 실패하면 -1을 리턴한다.

(예제20) 시스템의 정보보기
  1)설명 : uname()함수를 이용하여 시스템의 정보를 출력해보자.
  2)프로그램(uname.c)
    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<string.h>
    #include<sys/utsname.h>

    int main (int argc, char **argv)
    {
      int z;
      struct utsname u_name;

      z=uname(&u_name);

      if(z==-1)
      {
        fprintf(stderr,"%s,: uname(2)\n",strerror(errno));
        exit(1);
      }
      printf("  sysname[]='%s';\n",u_name.sysname);
      printf("  nodename[]='%s';\n",u_name.nodename);
      printf("  release[]='%s';\n",u_name.release);
      printf("  version[]='%s';\n",u_name.version);
      printf("  machine[]='%s';\n",u_name.machine);

      return 0;
    }

  3)실행예
    [posein@www posein]$ ./a.out
      sysname[]='Linux';
      nodename[]='www';
      release[]='2.4.2-3';
      version[]='#1 Sun Jun 24 01:31:37 KST 2001';
      machine[]='i686';
(2)호스트이름과 도메인이름
   1)사용법
     #include<unistd.h>

     int gethostname(char *name,size_t len);
     int getdomainname(char *name, size_t len);

   2)설명
     ㄱ. 각각 호스트이름과 NIS도메인 이름을 반환
     ㄴ. getdomainname()함수는 내부적으로 uname()함수를 사용
   3)반환값 : 성공하면 0을 실패하면 -1을 리턴한다.
(예제21) 현재 시스템의 호스트이름과 도메인이름 출력
  1)설명 : 현재시스템의 호스트이름과 도메인이름을 출력해본다.
  2)프로그램(gethostname.c)
    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<string.h>

    int main(int argc, char **argv)
    {
      int z;
      char buf[32];

      z=gethostname(buf,sizeof buf);

      if (z==-1)
      {
        fprintf(stderr,"%s: gethostname(2)\n", strerror(errno));
        exit(1);
      }
      printf("host name='%s'\n",buf);

      z=getdomainname(buf,sizeof buf);

      if (z==-1)
      {
        fprintf(stderr,"%s: getdomainname(2)\n",strerror(errno));
        exit(1);
      }

      printf("domain name='%s'\n",buf);

      return 0;
    }
  3)사용예
    [posein@www posein]$ ./a.out
    host name='www'
    domain name='(none)'
(3)원격 호스트 주소검색
(예제22) 원격 호스트의 주소를 검색
  1)설명 : 원격지 호스트의 주소를 검색해본다.
  2)프로그램(lookup.c)
    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<string.h>
    #include<errno.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    #include<netdb.h>

    extern int h_errno;

    #define TRUE 1
    #define FALSE 0

    int main(int argc, char **argv)
    {
      int x, x2;
      struct hostent *hp;

      sethostent(TRUE);

      for (x=1;xh_name);
        fputs("  Aliases:\t",stdout);
        for(x2=0;hp->h_aliases[x2];++x2)
        {
          if (x2)
            fputs(",",stdout);
          fputs(hp->h_aliases[x2],stdout);
        }
        fputc('\n',stdout);
        printf("  Type:\t\t%s\n",
               hp->h_addrtype == AF_INET
                   ? "AF_INET"
                   : "AF_INET6");
        if (hp->h_addrtype == AF_INET)
        {
          for (x2=0; hp->h_addr_list[x2];++x2)
          printf("  Address:\t%s\n",
          printf("  Address:\t%s\n",
                  inet_ntoa( *(struct in_addr *)
                  hp->h_addr_list[x2]));
        }
        putchar('\n');
      }
      return 0;
    }
  3)사용예
    [posein@www posein]$ ./a.out www.microsoft.com www.chosun.com
    Host www.microsoft.com :
      Officially:   www.microsoft.akadns.net
      Aliases:      www.microsoft.com
      Type:         AF_INET
      Address:      207.46.197.100
      Address:      207.46.197.101
      Address:      207.46.197.102
      Address:      207.46.197.113
      Address:      207.46.230.218
      Address:      207.46.230.219
      Address:      207.46.230.220

    Host www.chosun.com :
      Officially:   www.chosun.com
      Aliases:
      Type:         AF_INET
      Address:      210.116.112.100
      Address:      210.116.112.200
(예제23) 원격 호스트의 열려진 포트 검색
  1)설명 : 원격 호스트의 열려진 포트(1024번이내)를 검색하는 프로그램이다.
  2)프로그램(p.c)
    #include<string.h>
    #include<stdio.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netdb.h>
    #include<netinet/in.h>
    #include<errno.h>

    int samplescan(char *hostaddr, int cport, int socktype);
    int main(int argc, char *argv[])
    {
        int loop_port;
       //loop defined
       // address check
       // if address argc don't collect, exit

        if (argc < 2)
        {
           printf("samplescan address\n");
           exit(1);
        }

        // well-known port scaning range.

        for (loop_port = 1;loop_port < 1024;loop_port++)
        {
         //if term is collect, excute
           if (samplescan(argv[1], loop_port, SOCK_STREAM) == 0)
           {
             printf("%5d Open\n", loop_port);
           }
         }
         return 0;
    }

    int samplescan(char *hostaddr, int loop_port, int socktype)
    {
      int sockfd;
     //soket fd(file descripter)
      struct hostent *he;
      struct sockaddr_in destaddr;
      int pok;
      if ((he = gethostbyname(hostaddr)) == NULL)
      {
         printf("error");
         return 0;
      }
      sockfd = socket(AF_INET, socktype, 0);

      destaddr.sin_family = AF_INET;       /* TCP/IP connetion request */
      destaddr.sin_addr = *((struct in_addr *)he->h_addr);
      /* address value */
      destaddr.sin_port = htons(loop_port);                /* port place */
      bzero(&(destaddr.sin_zero), 8);
      pok = connect(sockfd, (struct sockaddr *)&destaddr, sizeof(struct sockaddr));
      /* connect */
      close(sockfd);
      /* close */

      if (pok == -1)
      /* connet return value 0 but -1 */
      return -1;
      return 0;
    }
  3)사용예
    [posein@www posein]$ ./a.out www.hannam.ac.kr
        7 Open
        9 Open
       13 Open
       19 Open
       21 Open
       23 Open
       25 Open
       37 Open
       79 Open
       80 Open
      109 Open
      110 Open
      111 Open
      512 Open
      513 Open
      514 Open
      540 Open
      587 Open