2011년7월11일_데이터의 경계가 없는 TCP와 데이터의 경계가 있는 UDP프로토콜을 알아보는 실습코드

 

● TCP와 UDP프로토콜의 비교 

         TCP                         UDP
1. 신뢰성 있는 통신             ↔            1. 비신뢰성 통신
   ┌ 데이터 누락
   └ 순서대로 전달
2. 연결 성공 후 통신            ↔            2. 연결없이 통신
3. 구현하기 복잡함              ↔            3. 구현이 간단.         
4. 데이터의 경계없음           ↔            4. 데이터의 경계가 있음


image


1. bnd_server.c, bnd_client.c 소스를 작성하시오.


   1:  // bnd_server.c
   2:  // 데이터의 경계가 없는 통신
   3:  #include <stdio.h>
   4:  #include <stdlib.h>
   5:  #include <string.h>
   6:  #include <unistd.h>
   7:  #include <arpa/inet.h>
   8:  #include <sys/types.h>
   9:  #include <sys/socket.h>
  10:   
  11:  #define BUFSIZE    100
  12:   
  13:  void error_handling(char *message);
  14:   
  15:  int main()
  16:  {
  17:      int serv_sock;
  18:      int clnt_sock;
  19:      char message[BUFSIZE];
  20:      int str_len;
  21:   
  22:      struct sockaddr_in serv_addr;
  23:      struct sockaddr_in clnt_addr;
  24:      int clnt_addr_size;
  25:   
  26:      serv_sock = socket(PF_INET, SOCK_STREAM, 0);
  27:   
  28:      if(-1 == serv_sock)
  29:      {
  30:          error_handling("socket() error ");
  31:      }
  32:   
  33:      memset(&serv_addr, 0, sizeof(serv_addr));
  34:      serv_addr.sin_family = AF_INET;
  35:      serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  36:      serv_addr.sin_port = htons(9190);
  37:      
  38:      if(-1 == bind(serv_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)))
  39:      {
  40:          error_handling("bind() error ");
  41:      }
  42:   
  43:      if(-1 == listen(serv_sock, 5))
  44:      {
  45:          error_handling("listen() error ");
  46:      }
  47:   
  48:      clnt_addr_size = sizeof(clnt_addr);
  49:      clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_addr, &clnt_addr_size);
  50:   
  51:      if(-1 == clnt_sock)
  52:      {
  53:          error_handling("accept() error ");
  54:      }
  55:   
  56:      sleep(5);        //수신 버퍼에 쌓아놓기 위해 대기
  57:   
  58:      str_len = read(clnt_sock, message, BUFSIZE);    //메시지 수신
  59:      write(clnt_sock, message, str_len);                //메시지 전송
  60:   
  61:      close(clnt_sock);
  62:   
  63:      return 0;
  64:  }
  65:   
  66:  void error_handling(char *message)
  67:  {
  68:      fputs(message, stderr);
  69:      fputc('\n', stderr);
  70:      exit(1);
  71:  }
  72:      
  73:          
  74:   


   1:  // bnd_client.c
   2:  // 데이터의 경계가 없는 통신
   3:  #include <stdio.h>
   4:  #include <stdlib.h>
   5:  #include <string.h>
   6:  #include <unistd.h>
   7:  #include <arpa/inet.h>
   8:  #include <sys/types.h>
   9:  #include <sys/socket.h>
  10:   
  11:  void error_handling(char *message);
  12:   
  13:  int main(void)
  14:  {
  15:      int sock;
  16:      int str_len;
  17:      int i;
  18:      struct sockaddr_in serv_addr;
  19:   
  20:      char msg1[] = "Hello Everybody~";
  21:      char msg2[] = "I am so happy!";
  22:      char message[10];
  23:      
  24:      sock = socket(PF_INET, SOCK_STREAM, 0);
  25:   
  26:      if(-1 == sock)
  27:      {
  28:          error_handling("socket() error ");
  29:      }
  30:   
  31:      memset(&serv_addr, 0, sizeof(serv_addr));
  32:      serv_addr.sin_family = AF_INET;
  33:      serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
  34:      serv_addr.sin_port = htons(9190);
  35:      
  36:      if(-1 == connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)))
  37:      {
  38:          error_handling("connect() error ");
  39:      }
  40:   
  41:      write(sock, msg1, strlen(msg1));        //메시지 1차 전송
  42:      write(sock, msg2, strlen(msg2));        //메시지 2차 전송
  43:      
  44:      sleep(10);            //수신 버퍼에 쌓아놓기 위해
  45:   
  46:      //4번에 걸쳐 데이터 수신
  47:      for(i = 0 ; i < 4 ; i++)
  48:      {
  49:          str_len = read(sock, message,  sizeof(message) - 1);
  50:          message[str_len] = 0;
  51:          printf("서버로부터 전송된 메시지 : %s \n", message);
  52:      }
  53:      
  54:      //접속종료
  55:      close(sock);
  56:   
  57:      return 0;
  58:  }
  59:   
  60:  void error_handling(char *message)
  61:  {
  62:      fputs(message, stderr);
  63:      fputc('\n', stderr);
  64:      exit(1);
  65:  }
  66:      
  67:          
  68:   


<실행결과>

image



● Client측 데이터 전송

   1:      write(sock, msg1, strlen(msg1));        //메시지 1차 전송
   2:      write(sock, msg2, strlen(msg2));        //메시지 2차 전송

msg1[] = "Hello Everybody~"
msg2[] = "I am so happy!"
배열에 있는 문자열 msg1 16Bytes를 먼저 전송하고 바로 이어서 msg2에 있는 14Bytes를 전송한다.
그럼 버퍼에는 30Bytes의 Byte Stream(데이터가 순대로 나열된 형태)로 있게 된다.
네트워크 디바이스를 통해 loopback주소로 전송하면 바로 돌아와 수신버퍼에 쌓이게 된다.

 

● Server측 데이터 수신과 전송(Echo)

   1:      clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_addr, &clnt_addr_size);
   2:   
   3:      if(-1 == clnt_sock)
   4:      {
   5:          error_handling("accept() error ");
   6:      }
   7:   
   8:      sleep(5);        //수신 버퍼에 쌓아놓기 위해 대기
   9:   
  10:      str_len = read(clnt_sock, message, BUFSIZE);    //메시지 수신
  11:      write(clnt_sock, message, str_len);                //메시지 전송


accept( )로 Client를 무한정 기다리다 Client가 접속하면 통신을 위한 clnt_sock을 생성하고 예외처리를 한다음…
sleep( )로 수신버퍼에 데이터가 완전히 다 들어 올 때까지 5초간 대기한다.
read( )로 수신버퍼의 값을 모두 읽어와 그대로 Client측으로 돌려준다.

 

● Client측 데이터 수신 (4회에 걸쳐 나누어 받음)

   1:      sleep(10);            //수신 버퍼에 쌓아놓기 위해
   2:   
   3:      //4번에 걸쳐 데이터 수신
   4:      for(i = 0 ; i < 4 ; i++)
   5:      {
   6:          str_len = read(sock, message,  sizeof(message) - 1);
   7:          message[str_len] = 0;
   8:          printf("서버로부터 전송된 메시지 : %s \n", message);
   9:      }


Client측에서 데이터를 송신하고 바로 받으면 Server측에서 처리하기도 전에 수신하는 것이 되므로,
sleep( )로 10초간 대기하여 수신 버퍼에 데이터가 완전히 쌓일 때까지 기다린다.

message배열은 크기가 10Bytes로 문자열의 마지막에 공백문자를 넣기 위해 1Byte를 빼면 받을 수 있는 데이터의 크기는 9Bytes가 된다.
그래서 sizeof( )의 리턴값에 -1을 한 것이다.
message[str_len]에 NULL문자를 넣으면 문자열이 완성이 되어 %s형식지정자로 출력할 수 있게 된다.

30Bytes의 데이터를 9bytes씩 받으니,

image  Hello Eve
  rybody~I
  am so hap
  py!

이렇게 문장이 잘려서 보이는 것이다.
TCP통신은 데이터의 경계를 구분하지 않으므로 만약 한글을 전송한다면 문제가 생길 수 있다.
전송 데이터의 크기에 관한 처리는 모두 프로그래머가 해야한다.

TCP통신에서는 read( ), write( )의 호출횟수는 별 의미없고, 모두 버퍼를 통해 데이터를 읽고 쓴다.
read( ) – 수신버퍼에서 데이터 읽어옴.
write( ) – 송신버퍼에 데이터 쓰기.

 

 

// UDP추가와 다듬기 해야함.

 
● udp_bnd_Server.c

   1:  // bnd_server.c
   2:  // 데이터의 경계가 있는 통신
   3:  #include <stdio.h>
   4:  #include <stdlib.h>
   5:  #include <string.h>
   6:  #include <unistd.h>
   7:  #include <arpa/inet.h>
   8:  #include <sys/types.h>
   9:  #include <sys/socket.h>
  10:   
  11:  #define BUFSIZE    30
  12:   
  13:  void error_handling(char *message);
  14:   
  15:  int main()
  16:  {
  17:      int serv_sock;
  18:      char message[BUFSIZE];
  19:      int str_len;
  20:      int num = 0;
  21:   
  22:      struct sockaddr_in serv_addr;
  23:      struct sockaddr_in clnt_addr;
  24:      int clnt_addr_size;
  25:   
  26:      serv_sock = socket(PF_INET, SOCK_STREAM, 0);
  27:   
  28:      if(-1 == serv_sock)
  29:      {
  30:          error_handling("socket() error ");
  31:      }
  32:   
  33:      memset(&serv_addr, 0, sizeof(serv_addr));
  34:      serv_addr.sin_family = AF_INET;
  35:      serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  36:      serv_addr.sin_port = htons(9190);
  37:      
  38:      if(-1 == bind(serv_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)))
  39:      {
  40:          error_handling("bind() error ");
  41:      }
  42:   
  43:      sleep(5);        //수신 버퍼에 쌓아놓기 위해
  44:   
  45:      while(1)
  46:      {
  47:          clnt_addr_size = sizeof(clnt_addr);
  48:          sleep(1);        //눈으로 쉽게 확인하기 위한 대기
  49:   
  50:          str_len = recvfrom(serv_sock, message, BUFSIZE, 0,
  51:                                      (struct sockaddr*)&clnt_addr, &clnt_addr_size);
  52:          printf("수신번호 : %d \n", num++);
  53:   
  54:          sendto(serv_sock, message, str_len, 0,
  55:                      (struct sockaddr*)&clnt_addr, sizeof(clnt_addr));
  56:      }
  57:      
  58:      close(serv_sock);
  59:   
  60:      return 0;
  61:  }
  62:   
  63:  void error_handling(char *message)
  64:  {
  65:      fputs(message, stderr);
  66:      fputc('\n', stderr);
  67:      exit(1);
  68:  }
  69:      
  70:          
  71:   





● udp_bnd_client.c

   1:  // udp_bnd_client.c
   2:  // 데이터의 경계가 있는 통신
   3:  #include <stdio.h>
   4:  #include <stdlib.h>
   5:  #include <string.h>
   6:  #include <unistd.h>
   7:  #include <arpa/inet.h>
   8:  #include <sys/types.h>
   9:  #include <sys/socket.h>
  10:   
  11:  #define BUFSIZE 30
  12:   
  13:  void error_handling(char *message);
  14:   
  15:  int main(void)
  16:  {
  17:      int sock;
  18:      char message[BUFSIZE];
  19:      int str_len;
  20:      int addr_size;
  21:      int i;
  22:   
  23:      char MSG1[] = "Good ";
  24:      char MSG2[] = "Afternoon ";
  25:      char MSG3[] = "Everybody!";
  26:   
  27:      struct sockaddr_in serv_addr;
  28:      struct sockaddr_in from_addr;
  29:      
  30:      sock = socket(PF_INET, SOCK_STREAM, 0);
  31:   
  32:      if(-1 == sock)
  33:      {
  34:          error_handling("socket() error ");
  35:      }
  36:   
  37:      memset(&serv_addr, 0, sizeof(serv_addr));
  38:      serv_addr.sin_family = AF_INET;
  39:      serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
  40:      serv_addr.sin_port = htons(9190);
  41:      
  42:      sendto(sock, MSG1, strlen(MSG1), 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
  43:      sendto(sock, MSG2, strlen(MSG2), 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
  44:      sendto(sock, MSG3, strlen(MSG3), 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
  45:      
  46:      for(i = 0 ; i < 3 ; i++)
  47:      {
  48:          addr_size = sizeof(from_addr);
  49:          str_len = recvfrom(sock, message, BUFSIZE, 0, (struct sockaddr*)&from_addr, &addr_size);
  50:          message[str_len] = 0;
  51:          printf("서버로부터 수신된 %d차 메시지 : %s \n", i, message);
  52:      }
  53:      
  54:      //접속종료
  55:      close(sock);
  56:   
  57:      return 0;
  58:  }
  59:   
  60:  void error_handling(char *message)
  61:  {
  62:      fputs(message, stderr);
  63:      fputc('\n', stderr);
  64:      exit(1);
  65:  }
  66:      
  67:          
  68: