시그널 함수







소켓의 다양한 옵션

소켓의 타입 변경
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main()
{
  int tcp_sock, udp_sock;
  int sock_type=-1;
  socklen_t optlen;
  int state;

  optlen=sizeof(sock_type);
  tcp_sock=socket(PF_INET, SOCK_STREAM, 0);
  udp_sock=socket(PF_INET, SOCK_DGRAM, 0);

  printf("SOCK_STREAM : %d \n", SOCK_STREAM);
  printf("SOCK_DGRAM  : %d \n", SOCK_DGRAM);

  state=getsockopt(tcp_sock, SOL_SOCKET, SO_TYPE, &sock_type, &optlen);
  if(state)
  {
    printf("tcp getsockopt error");
  }
  printf("첫번째 소켓의 타입은 [%d]\n", sock_type);
  
  state=getsockopt(udp_sock, SOL_SOCKET, SO_TYPE, &sock_type, &optlen);
  if(state)
  {
    printf("udp getsockopt error");
  }
  printf("두번째 소켓의 타입은 [%d]\n", sock_type);

  return 0;
}
소켓 버퍼의 크기 변경
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>

int main()
{
  int sock;
  int snd_buf=500;
  int rcv_buf=1000;

  int state;
  socklen_t len;

  sock=socket(PF_INET, SOCK_STREAM, 0);

  /* 입출력 버퍼 크기 설정 */
  state=setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcv_buf, sizeof(rcv_buf));
  if(state)
  {
    printf("rcv setsockopt error");
  }

  state=setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &snd_buf, sizeof(snd_buf));
  if(state)
  {
    printf("snd setsockopt error");
  }

  /* 입출력 버퍼 크기 추출 */
  len=sizeof(rcv_buf);
  state=getsockopt(sock, SOL_SOCKET,SO_RCVBUF, &rcv_buf, &len);
  if(state)
  {
    printf("rcv getsockopt error");
  }

  len=sizeof(snd_buf);
  state=getsockopt(sock, SOL_SOCKET,SO_SNDBUF, &snd_buf, &len);
  if(state)
  {
    printf("snd getsockopt error");
  }

  printf("데이터 입력받기 위한 소켓의 버퍼 크기 : [%d]\n", rcv_buf);
  printf("데이터 출력받기 위한 소켓의 버퍼 크기 : [%d]\n", snd_buf);

  return 0;
}
소켓 옵션을 변경할때는 setsockopt()함수를 사용한다. 원형은 다음과 같다.
int setsockopt(int s, int level, int opt. const char *optval, int optlen);
s : 옵션을 바꿀 소켓 번호
level : 프로토콜 레벨(일반적인 옵션 일 경우 SOL_SOCKET, IP프로토콜 일 경우 IPPROTO_IP,
        TCP에 관한 내용 일 경우 IPPROTO_TCP를 선택)
opt : 선택할 옵션(맨 위의 표 참조)
*optval : 옵션 지정에 필요한 값의 포인터 
optlen : *optval의 크기

소켓의 현재 옵션 값을 알아내려면 getsockopt() 함수를 사용한다. 원형은 다음과 같다.
int getsockopt(int s, int level, int opt, const char *optval, int *len);
setsockopt() 함수와 같다. 단 optval,len은 입력인자가 아닌 값을 얻는 출력 인자가 된다.


TCP(연결형)소켓을 사용하는 클라이언트 프로그램

TCP 클라이언트 프로그램의 작성 절차는 일반적으로 위와 같다. 소켓의 프로토콜 체계를 PF_INET, 서비스타입을  SOCK_STREAM으로 선택 ex) socket(PF_INET, SOCK_STREAM, 0);
클라이언트는 connect를 호출하기 전에 연결하고자하는 서버의 주소를 지정하여야 하는데, IP주소와 PORT를 포함하는 소켓주소 구조체 sockaddr_in을 작성해야한다. 예제는 다음과 같다.
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_falimy=AF_INET;
serv_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
serv_addr.sin_port=htons(9190);
서버 구조체를 이용하여 서버에 접속을 요청하기위해 connect()를 호출한다.  함수 원형은 다음과 같다.
int connect(int s, const struct sockaddr *addr, int addrlen);
ex) connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
클라이언트가 서버와 접속이되면 send(), recv() 또는 read(), write()를 사용하여 메시지를 송수신 할수있다.

문 법

인 자

int send(int s, char* buf, int

              

             length, int flags);

s

소켓 번호

buf

전송할 메시지가 저장된 버퍼

length

buf버퍼의 크기

flags

보통 0

int write(int s, const void*

             

                 buf, int length);

s

소켓 번호

buf

전송할 메시지가 저장된 버퍼

length

buf버퍼의 크기

int recv(int s, char *buf, int

             

               length, int flags);

s

소켓 번호

buf

전송할 메시지가 저장된 버퍼

length

buf버퍼의 크기

flags

보통 0

int read(int s, void* buf, int

              

                   length);

s

소켓 번호

buf

전송할 메시지가 저장된 버퍼

length

buf버퍼의 크기

소켓 사용을 마치려면 해당 소켓번호를 지정하여 close()를 호출하면된다.






인터넷 주소 변환
#include <stdio.h>
#include <arpa/inet.h>

int main()
{
  char *addr="1.2.3.4";
  struct sockaddr_in addr_inet;
  if(!inet_aton(addr, &addr_inet.sin_addr))
  {
    printf("Conversion error");
  }
  printf("unsigned long address(network ordered : %x \n", addr_inet.sin_addr);
  return 0;
}
#include <arpa/inet.h>
인터넷 주소 표현 방식에는 도메인 네임, 32비트 IP주소, 십진수(dotted decimal)등 3가지 방식이 있다.
바이트 순서에는 호스트 바이트 순서와 네트웤 바이트 순서 두가지가 있다. 호스트 바이트 순서는 Little-Endian방식과 Big-Endian방식 두가지가 있는데 인텔(80x86)계열이 Little-Endian방식을 사용한다. 네트워크 바이트 순서는 High-order바이트부터 전송하기로 정했다. big-Endian방식과 동일하다. 인텔CPU는 Little-Endian방식이므로 네트워크에 전송할경우 같은값이아닌 반대값이 나오게된다.(1234보내면 3412로 나옴) 그래서 전송을 하기전에 바이트 순서를 함수로 이용하여 바꾸어 주어야된다. 함수는 다음과 같다.
htonl() ☜ host to network 변환(4바이트 unsigned long형)
htons() ☜ host to network 변환(2바이트 unsigned short형)
ntohl() ☜ network to host 변환(4바이트 unsigned long형)
ntohs() ☜ network to host 변환(2바이트 unsigned short형)
※ 주의사항 : 바이트 순서를 맞추는 것이 필요하 경우는 IP주소나 포트번호등의 숫자를 네트워크로 전송할대이며 텍스트나 문서, 바이너리 파일등 일반 사용자 데이터는 변환이 필요없다. 이유는 일반 데이터는 메모리에 저장되었다가 바이트 단위로 메모리 앞 주소부터 차례대로 전송되며 수신측도 차례대로 저장하기 때문이다.

inet_addr() ☜ 32비트 IP주소로 변환시 사용(01111111 00000000 00000000 00000001 이런 식)
inet_aton() ☜ 위와 같으나 addr은 32비트 IP주소를 리턴하는데 이녀석은 두번째 인자로 소켓주소의 포인터를 리턴
inet_ntoa() ☜ IP주소를 dotted decimal로 변환시 사용(127.0.0.1 이런 식)

gethostbyname() 함수로 도메인 네임으로 IP주소 알아내기
#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>

int 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]);
    return 0;
  }

  /* 호스트 이벤트 구조체 구하기 */
  myhost = gethostbyname(argv[1]);
  if(myhost == 0)
  {
    printf("host error\n");
    return 0;
  }

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

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

  /* 호스트 주소 타입 출력 */
  printf("host address type : %d\n", myhost->h_addrtype);

  /* 호스트 주소 길이 출력 */
  printf("host address length : %d\n", myhost->h_length);
  
  /* 호스트 주소를 dotted decimal 형태로 출력 */
  i=0;
  while(myhost->h_addr_list[i] != NULL)
  {
    myinaddr.s_addr=*((u_long*)(myhost->h_addr_list[i]));
    printf("IP address : %s \n", inet_ntoa(myinaddr));
    i++;
  }
  return 0;
}#include <netdb.h>
gethostbyname() 함수는 도메인 네임 h_name을 스트리밍 형태로 입력으로 받고 그 이름에
해당하는 호스트의 각종 정보를 가지고 hostent 구조체의 포인터를 리턴한다.
gethostbyaddr() 함수는 IP주소를 포함하고 있는 구조체 in_addr의 포인터와 주소길이, 타입을
입력하여 해당 호스트의 정보를 가지고 있는 hostent 구조체의 포인터를 리턴한다. 아래는 구조체
struct hostent {
char *h_name;        // 호스트 이름
char **h_aliases;    // 호스트 별명
int h_addrtype;      // 호스트 주소의 종류(AF_INET=2 등등등..)
int h_length;        // 주소의 크기(바이트 단위며 IPv4에서는 4임)
char **h_addr_list;  // IP주소 리스트
};
#define h_addr h_addr_list[0]  // 첫번째 주소
소켓의 개설
통신을 하기위해서는 통신에 사용할 프로토콜(TCP 또는 UDP), 자신의 IP, 자신의 PORT, 상대방의 IP, 상대방의 PORT가 필요하다. 우선 소켓 생성부터 알아보기로 한다. 소스는 아래와 같다.
#include <stdio.h>
#include <arpa/inet.h>  // 프로토콜 타입 헤더파일 
#include <sys/socket.h>  // 소켓 헤더 파일 

int main()
{
  int tcp_sock;
  int udp_sock;

  /* 연결지향 tcp 소켓 생성 */
  tcp_sock=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  if(tcp_sock == -1)
  {
    printf("TCP socket error\n");
  }

  /* 비연결지향 udp 소켓 생성 */
  udp_sock=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
  if(udp_sock == -1)
  {
    printf("UDP socket error\n");
  }

  printf("TCP socket : %d \n", tcp_sock);
  printf("UDP socket : %d \n", udp_sock);

  close(tcp_sock);
  close(udp_sock);
  
  return 0;
}
소켓의 사용문법은 다음과 같다.
int socket(int domain, int type, int protocol);
domain   : 프로토콜 체계
type     : 서비스 타입
protocol : 소켓에서 사용할 프로토콜
TCP/IP 프로토콜을 사용하려면 인터넷체계의 소켓을 개설해야하는데 그러기위해서는 domain을
PF_INET으로 지정해주어야한다. domain의 속성은 다음과 같다.
domain : PF_INET  (인터넷 프로토콜 체계)
         PF_INET6 (IPv6 프로토콜 체계)
         PF_UNIX  (유닉스 방식 프로토콜 체계)
         PF_NS    (XEROX 네트워크 시스템 프로토콜 체계)
type은 서비스 타입을 말하며 속성은 다음과 같다.
type : SOCK_STREAM (스트림 방식의 소켓 TCP)
       SOCK_DGRAM  (데이터그램 방식의 소켓 UDP
       SOCK_RAW    (RAW 모드의 소켓 생성) tcp나 udp를 거치지않고 바로 ip계층 이용
리눅스에서는 SOCK_PACKET타입을 지원 이는 ip계층도 거치지않고 바로링크 계층 인터페이스 이용
protocol은 구체적인 프로토콜을 선책 대부분 0을 지정
tcp_sock = socket(PF_INET, SOCK_STREAM, 0); << 이렇게해도 똑같음.
소켓주소 구조체
소켓의 구체적인 주소를 표현하기위해서는 주소체계, IP 주소, 포트번호 3가지가 지정되어야하며 이 세가지 정보를 소켓 주소라고 부른다. types.h 헤더파일에 설정되어 있으며 소켓 주소의 구조체는 다음과 같다.
struct sockaddr {
   u_short sa_family; // 주소체계
   char sa_data[14] // 주소
};  
액세스 할 수 있는 인터넷 전용 소켓주소 구조체
struct sockaddr_in {
   short sin_family; // 주소체계
   u_short sin_port; // 16비트 포트번호
   struct in_addr sin_addr // 32비트 IP주소
   char sin_zero[8];  // 전체 크기를 16바이트로 맞추기 위한 dummy
};
sin_family : AF_INET
                 AF_UNIX
                 AF_NS
위의 주소체계는 설명안해도 이제 무엇인지 다 알것이다.

+ Recent posts