인터넷과 소켓프로그래밍

TCP 에코 클라이언트

kang057 2024. 8. 1. 08:32

● 헤더파일

/*
 * tcpecho.c
 * TCP Echo Client 프로그램
 * gcc -o tcpechoc hostname port 번호
 * 사용법 : tcpechoc hostname port 번호
 */
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

 

● struct hostent *hp, *gethostbyname();

- hostent는 <netdb.h>에 있다.

struct hostent
{
	char *h_name; //공식 도메인 이름
	char **h_aliases; //공식 이외 도메인 이름들
	int h_addrtype; //주소정보 체계(IPv4: AF_INET, IPv6: AF_INET6)
	int h_length; //IP주소의 크기를 담는다. (IPv4는 4)
	char **h_addr_list; //도메인 이름에 대한 IP주소가 정수 형태로 반환될 때 이 멤버 변수를 사용
}

 

- gethostbyname 의 결과값 : 주소값

 

● 소켓 생성 - socket()

if (argv < 3) {
	printf("Usage: tcpechoc hostname portnumber\n");
    exit(0);
}

/*
 * 스트림 소켓 생성 (소켓 패밀리, 소켓 종류, 하위계층 프로토콜)
 * 새로운 소켓 디스크립터를 리턴(sockfd)
 */
 
 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0) < 0) {
	printf("client: can't open stream socket");
    exit(1);
}

printf("SOCKET CREATED sockfd = %d\n", sockfd);

 

● Echo 서버 실별자 구하기 (1) - Family

- Family : AF_INET 또는 PF_INET
- IP 주소 : 220.100.50.20

- 포트번호 : 8888

bzero((char 8)&serv_addr, sizeof(serv_addr));

/*
 * 서버 정보 구하고, 채우기
 * [서버소켓 패밀리, 서버 IP주소, 서버프로그램식별자(포트번호)]
 */
 
 /* 패밀리 정보를 serv_addr에 저장 */
 serv_addr.sin_family = AF_INET;
/*
 * 서버 호스트의 IP 주소 및 관련 정보 구하기
 * gethostbyname() : name resolver라고 함
 * /etc/hosts 파일을 검색하고, 없으면 Name Server에게 요청
 */
 
hp = gethostbyname(argv[1]);
if (hp == (struct hostent *) 0) {
	fprintf(stderr, "%s: unknown host\n", argv[1]);
    exit(1);
}

/* 구한 서버 호스트 정보를 serv_addr에 저장 */
bcopy((char *) &serv_addr.sin_addr, (char *) hp->h_addr_list[0], hp->h_length);

 

● gethostbyname()

=> 도메인 이름(호스트 이름)을 IP 주소로 변환하는 함수이다. 이 함수는 주로 IPv4 주소를 반환하며 IPv6 주소는 지원하지 않는다.

 

- 함수 선언

#include <netdb.h>

struct hostent *gethostbyname(const char *name);

○ name : 변환할 호스트 이름을 나타내는 문자열이다.

○ 반환값 : 'hostent' 구조체를 가리키는 포인터이다. 실패하면 'NULL'을 반환한다.

 

● Echo 서버 식별자 구하기 (1) - Port 번호

/* 서버프로그램 식별자(포트번호)는 이미 알고 있어야 함*/
/* 서버 소켓의 포트 값을 serv_addr에 저장 */
serv_addr.sin_port = htons(atoi(argv[2]));

 

● connect() - TCP에게 접속 설정 요청

/* TCP에게 접속 설정 요청 connect() */
printf("CONNECTING....\n");
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr) == -1) {
	printf("client: connect error\n");
    exit(1);
}

printf("접속 성공!!! \n");

 

● Local Port 번호 생성

○ echo server의 포트번호는 

  - /etc/services에 저장된 well-known port에 의해 미리 지정되거나

    ex) echo 서버는 7

  - 사용자가 직접 지정해야 함

    ex) 8888

 

○ echo client의 포트번호는 

  - 응용 프로세스에서 접속요청이 발생하면 TCP는 현재 사용하고 있지 않은 local computer에서 유일한(unique) 포트번호를 할당함(dynamic)

 

● fgets() - 키보드로 부터 입력

/* 키보드 입력 대기 : stdin(standard input) */
pirntf("입력 : ");

if (fgets(buf, sizeof(buf), stdin) == NULL) {
	exit(0);
}

 

● write()/send() - 데이터 전송 // read()/recv() - 데이터 수신

nbyte = strlen(buf);

/* 에코 서버로 메시지 전송 */
/* TCP에게 데이터 전송 요청 */

if (write(sockfd, buf, nbyte) == -1) {
	printf("Echo Client: data trafer error\n");
    exit(1);
}

printf("수신: ");
/* TCP에게 데이터 수신 요청 */
if((nbyte = read(sockfd, buf, MAXLINE) < 0) {
	perror("read fail");
    exit(0);
}
buf[nbyte] = 0;

/* 수신된 에코 메시지 화면 출력 */
printf("%s", buf);

 

● close() - TCP에게 접속종료 요청

	/* 소켓 닫기 : TCP에게 접속 종료 요청 */
	close(sockfd);
	printf("CLOSED\n");
	return (0);
}