2011년7월8일_TCP Echo Server(임시제목)

 

● TCPEchoServer4.c

1. socket()를 통해 TCP소켓을 생성.
2. bind()를 통해 소켓에 포트번호를 할당.
3. listen()을 통해 해당포트가 연결을 받아들이도록 시스템에 알림.
4. 다음과 같은 일을 계속적으로 반복
   ① accept()를 통해 각 클라이언트와 통신에 필요한 새로운 소켓을 획득.
   ② 새롭게 생성된 클라이언트 소켓에 send(), recv()를 호출하여 통신을 수행.
   ③ close()를 통해 클라이언트와 연결을 종료



image


● TCPEchoServer4.c

   1:  // TCPEchoServer4.c
   2:  #include <stdio.h>
   3:  #include <stdlib.h>
   4:  #include <string.h>
   5:  #include <sys/types.h>
   6:  #include <sys/socket.h>
   7:  #include <netinet/in.h>
   8:  #include <arpa/inet.h>
   9:  #include "Practical.h"
  10:  #include "TCPServerUtility.h"
  11:   
  12:   
  13:  static const int MAXPENDING = 5;        //연결 요청을 대기할 수 있는 최대수
  14:   
  15:  int main(int argc, char *argv[])
  16:  {
  17:      in_port_t servPort;                    //첫 번째 인자: 지역 포트
  18:      int servSock;                        //서버 소켓 식별자
  19:      struct sockaddr_in servAddr;        //지역주소
  20:      struct sockaddr_in clntAddr;        //클라이언트 주소
  21:      socklen_t clntAddrLen;
  22:      int clntSock;
  23:      char clntName[INET_ADDRSTRLEN];        //클라이언트 주소를 담기 위한 문자열
  24:      
  25:      if(argc != 2)                //명령어 인자의 개수 확인
  26:      {
  27:          DieWithUserMessage("Parameter(s)", "<Server Port>");
  28:      }
  29:   
  30:      servPort = atoi(argv[1]);        //첫 번째 인자: 지역 포트
  31:   
  32:      //연결 요청을 처리하는 소켓 생성
  33:      if((servSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
  34:      {
  35:          DieWithSystemMessage("socket() failed");
  36:      }
  37:   
  38:      //지역 주소 구조체 할당
  39:      memset(&servAddr, 0, sizeof(servAddr));            //0으로 구조체 초기화
  40:      servAddr.sin_family = AF_INET;                    //IPv4 주소 패밀리
  41:      servAddr.sin_addr.s_addr = htonl(INADDR_ANY);    //호스트의 어떠한 IP로도 연결 요청 수락
  42:      servAddr.sin_port = htons(servPort);            //지역포트
  43:   
  44:      //지역 주소에 바인드
  45:      if(bind(servSock, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0)
  46:      {
  47:          DieWithSystemMessage("bind() failed");
  48:      }
  49:   
  50:      //소켓에 들어오는 요청을 처리할 수 있도록 설정
  51:      if(listen(servSock, MAXPENDING) < 0)
  52:      {
  53:          DieWithSystemMessage("listen() failed");
  54:      }
  55:   
  56:      for(;;)        //무한 실행
  57:      {
  58:          //클라이언트 주소 구조체의 크기 설정
  59:          clntAddrLen = sizeof(clntAddr);
  60:   
  61:          //클라이언트의 연결을 기다림
  62:          clntSock = accept(servSock, (struct sockaddr *) &clntAddr, &clntAddrLen);
  63:          if(clntSock < 0)
  64:          {
  65:              DieWithSystemMessage("accept() failed");
  66:          }
  67:   
  68:          //clntSock가 클라이언트와 연결됨
  69:          if(inet_ntop(AF_INET, &clntAddr.sin_addr.s_addr, clntName, sizeof(clntName)) != NULL)
  70:          {
  71:              printf("Handling client %s/%d\n", clntName, ntohs(clntAddr.sin_port));
  72:          }
  73:          else
  74:          {
  75:              puts("Unable to get client address");
  76:          }
  77:   
  78:          HandleTCPClient(clntSock);
  79:      }
  80:   
  81:      //실행되지 않음
  82:      
  83:      return 0;
  84:  }


//설명/.


● TCPServerUtility.h

   1:  #ifndef __TCPSERVERUTILITY_H__
   2:  #define __TCPSERVERUTILITY_H__
   3:  void HandleTCPClient(int);
   4:  #define BUFSIZE 100
   5:  #endif    //__TCPSERVERUTILITY_H__




● TCPServerUtility.c

   1:  #include <stdio.h>
   2:  #include <stdlib.h>
   3:  #include <string.h>
   4:  #include <sys/types.h>
   5:  #include <sys/socket.h>
   6:  #include <netinet/in.h>
   7:  #include <arpa/inet.h>
   8:  #include "Practical.h"
   9:  #include "TCPServerUtility.h"
  10:   
  11:  //TCPServerUtility.c
  12:  void HandleTCPClient(int clntSocket)
  13:  {   
  14:      char buffer[BUFSIZE];   //에코 문자열을 위한 버퍼
  15:      ssize_t numBytesRcvd;
  16:      ssize_t numBytesSent;
  17:                                      
  18:      //클라이언트로부터 메시지 수신
  19:      numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0);
  20:      if(numBytesRcvd < 0)
  21:      {
  22:          DieWithSystemMessage("recv() failed");
  23:      }
  24:   
  25:      //수신한 문자열을 전송하고 여분의 데이터를 스트림이 끝날 때까지 수신
  26:      while(numBytesRcvd > 0)        //0일 경우 스트림의 종료를 의미
  27:      {
  28:          //클라이언트로 에코메시지를 보냄
  29:          numBytesSent = send(clntSocket, buffer, numBytesRcvd, 0);
  30:          //화면에 출력 추가(수만)
  31:          buffer[numBytesSent] = '\0';
  32:          printf("from Client: %s\n", buffer);
  33:          if(numBytesSent < 0)
  34:          {
  35:              DieWithSystemMessage("send() failed");
  36:          }
  37:          else if(numBytesSent != numBytesRcvd)
  38:          {
  39:              DieWithUserMessage("send()", "sent unexpected number of bytes");
  40:          }
  41:   
  42:          //받을 수 있는 데이터가 더 남아있는지 확인
  43:          numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0);
  44:          if(numBytesRcvd < 0)
  45:          {
  46:              DieWithSystemMessage("recv() failed");
  47:          }
  48:      }
  49:   
  50:      close(clntSocket);        //클라이언트 소켓 종료
  51:  }


26행의  while문은 데이터를 모두 수신할 때까지 recv()를 반복호출한다.
만약 보낸 데이터가 20Bytes라고 현재 수신된 데이터가 10Bytes인 경우 recv()를 다시 호출하여 데이터를 더 받는다.

★ TCP의 경우 데이터의 경계가 없어 send()를 호출하면, 모든 데이터가 recv()호출시 들어오는 것이 아니다.
    send()와 recv(), write()와 read()는 모두 버퍼에 데이터를 읽고 쓰는 함수이다.
    send()는 송신버퍼에 데이터를 기록      (버퍼 –> 디바이스)
    recv()는 수신버퍼에서 데이터를 읽어옴. (버퍼 <- 디바이스)

 

 

참조(Reference)

 

 DSCN3579