본문 바로가기

Network/초급 TCP/IP socket

06. TCP 에코 클라이언트 소켓 (TCP ECHO CLIENT SOCKET)

 에코 서버/클라이언트: 서버/클라이언트가 전송해 주는 데이터를 그대로 되돌려 전송해 주는 기능의 서버/클라이언트를 말합니다

 

클라이언트에 메세지를 입력하고 서버로부터 되돌아온 메세지를 화면에 출력합니다

 

참고1) 실행

./실행파일 IP주소 메세지 의 형태로 합니다

 

참고2)linux에코 설정 setup 

 

설정후 리셋해준다



IPv4 TCP
클라이언트

 

중요한 4가지 단계

1.socket()을 이용하여 TCP소켓을 생성

2.connect()를 이용하여 서버와의 연결을 설정

3.send(),recv()를 이용하여 통신을 수행

4.close()를 이용하여 연결을 종효

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "practical.h"
#define BUFSIZE 30

int main(int argc, char *argv[])
{
  char *servIP=argv[1];
  char *echoString=argv[2];
  int sock;
  int renVal;
  char buffer[BUFSIZE]; //
/출력 버퍼
  in_port_t servPort;
  unsigned int totalBytesRcvd = 0//
수신한 문자 개수
  size_t echoStringLen;
  ssize_t numBytes; 
  struct sockaddr_in servAddr;
  int rtnVal;

/*
명령어 인자의 정확한 갯수 확인*/
  if(argc < 3 || argc > 4)
    DieWithUserMessage("Parameter(s)","<Server Address><Echo Word>[<Server Port>]");

/*
 번째 인자(선택)) : 서버 포트(숫자형식), 7 사용*/
  servPort  = (argc == 4) ? atoi(argv[3]) : 7;
    
/*TCP
 사용하여 안정된 스트림 소켓 생성*/
  sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if(sock < 0)
    DieWithSystemMessage("socket() failed");

/*
서버주소 구조체 생성*/
  memset(&servAddr, 0 ,sizeof(servAddr)); //
구조체 초기화

  servAddr.sin_family = AF_INET; //IPv4 주소 패밀리

/*
주소변화*/
  rtnVal = inet_pton(AF_INET,servIP, &servAddr.sin_addr.s_addr);
  if(rtnVal == 0)
    DieWithUserMessage("inet_pton() failed","invalid address string");
  else if(rtnVal <0)
    DieWithSystemMessage("inet_pton() failed");
/*
서버포트*/
  servAddr.sin_port = htons(servPort);

/*
에코 서버에 연결 설정*/
  if(connect(sock, (struct sockaddr *) &servAddr, sizeof(servAddr))<0)
  DieWithSystemMessage("connect() failed");

/*
입력받은 문자열의 길이*/
  echoStringLen = strlen(echoString);

/*
서버에 에코 문자열 전송*/
  numBytes = send(sock, echoString, echoStringLen, 0);
  if(numBytes < 0)
    DieWithSystemMessage("send() failed");
  else if(numBytes != echoStringLen)
    DieWithUserMessage("send()""send unexpected number of bytes");

/*
서버로 부터 동일한 문자열 수신*/
  fputs("Received : ", stdout);    //
돌려받은 에코 문자열 출력을 위한 설정

  while(totalBytesRcvd < echoStringLen)
  {
    numBytes = recv(sock, buffer, BUFSIZE - 10);

    if(numBytes < 0)
      DieWithSystemMessage("recv() failed");
    else if(numBytes == 0)
      DieWithUserMessage("recv()","connection closde prematurely");

    totalBytesRcvd += numBytes; ); //
총 받은 크기를 기록
    buffer[numBytes] = '\0';  //
널 문자를 추가하여 문자열 완성 

  fputs(buffer,stdout); //에코 버퍼를 출력
  }
  fputc('\n',stdout); //
마지막으로 개행문자 출력

close(sock);
  exit(0);
  return 0;
}

 

동작순서 요약

1. 응용프로그램 초기 설정 및 파라미터 파싱

2. TCP소켓 생성 AF_INET SOCK_STREAM 0 의 형태이고 에러처리 DieWithUserMessage()함수사용

3. 주소 준비 및 연결 설정

*서버의 주소를 담기 위해여 sockaddr_in 구조체를 준비 / memset()구조체를 초기화

*inet_pton의 함수로 구조체를 체움

*connect는 구조체sockaddr_in이 가리키는 주소로 연결 시도

4. 에코 문자열을 서버에 보냅니다 send()이용

5. 에코 서버의 응답수신 recv()를 이용

      *버퍼출력 : null문자로 끝나는 일반문자열을 다루는 fputs()를 사용하여 출력

      *개행문자 줄바꿈


참고)printf 의 경우 심각한 보안상이 허점으로 인하여 잘 사용하지 않습니다

 

 

practical.h 헤더파일 처리하기

void DieWithUserMessage(const char *msg, const char *detail);
void DieWithSystemMessage(const char *msg);



 

DieWithUserMessage.c 파일(오작동시 에러메세지를 출력!!)

#include <stdio.h>
#include <stdlib.h>

void DieWithUserMessage(const char *msg, const char *detail)
{
  fputs(msg,stderr);
  fputs(":",stderr);
  fputs(detail, stderr);
  fputc('\n',stderr);
  exit(1);
}
void DieWithSystemMessage(const char *msg)
{
  perror(msg);
  exit(1);
}


 

컴파일(gcc) 순서

1)gcc –o TCPEchoClient4 TCPEchoClient4.c

2)gcc –c DieWithMessage DieWithMessage.c

3)gcc –o TCPEchoClient4 TCPEchoClient4.c DieWithMessage.o


실행
./TCPEchoClient4 127.0.0.7 안녕하세요!!