# echo 서버
: 클라가 보낸 데이터를 그대로 돌려주는 서버
> 구현 목적
: 소켓 I/O 흐름, 에러처리 익히기 (TCP 감 잡기)
# echo_server.c
//파일위치- tiny,proxy는 과제코드, echo_server.c는 연습용 네트워크 프로그램이라 별도 폴더 X
/*
# 파일 설명
에코 서버- 클라가 보낸 데이터를 그대로 돌려주는 서버
# 컴파일
: 따로 makefile 안 건드려도 되고 터미널에서
cd webproxy-lab
gcc -Wall -O2 echo_server.c -o echo_server
./echo_server
*/
#include <stdio.h> // printf, perror 등 기본 입출력 함수
#include <stdlib.h> // exit() 등
#include <string.h> // memset(), strlen() 등 문자열 처리
#include <unistd.h> // close(), read(), write() 등 시스템콜
#include <arpa/inet.h> // inet_ntoa(), htons(), htonl() 등 네트워크 변환 함수
#include <netinet/in.h> // sockaddr_in 구조체 정의
#define PORT 8080 // 서버가 열 포트 번호
#define BUF_SIZE 1024 // 버퍼 크기 (한번에 읽을 최대 Byte수)
int main(){
int listenfd, connfd;
struct sockaddr_in servaddr, cliaddr;
socklen_t clilen;
char buf[BUF_SIZE]; //데이터 읽고 쓸 버퍼
ssize_t n; // read/write 반환값 저장용
// 1- socket() : 소켓 생성 (AF_INET: IPv4 , SOCK_STREAM: TCP)
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0){
perror("socket error!");
exit(1);
}
// 2- 주소 재사용 옵션 설정 (서버 재시작 시 "Address already in use" 방지)
int optval = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
// 3- 서버 주소 구조체 초기화
memset(&servaddr, 0, sizeof(servaddr)); //일단 구조체메모리 0으로 초기화
servaddr.sin_family = AF_INET; //IPv4 사용
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //모든 네트워크 인터페이스에서 수신 _ex: 127.0.0.1
servaddr.sin_port = htons(PORT); //포트번호를 네트워크 바이트 순서로 변환 _ex: 8080
/*“내(서버)가 8080번 포트를 열고,
모든 IP(INADDR_ANY) 로 들어오는 요청을 받을 준비를 하겠다.”*/
// 4- bind() : 소켓을 해당 주소-포트에 바인딩
if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
perror("bind error!");
exit(1);
}
// 5- listen() : 연결 요청 대기 상태로 전환 (동시 처리 가능 연결큐 크기 = 10)
if (listen(listenfd,10) < 0){
perror("listen error!");
exit(1);
}
printf("Echo server running on port %d...\n", PORT);
// 6- 무한루프 : 클라 연결 계속 수락 (accept() -> write/read() -> close())
while (1){
clilen = sizeof(cliaddr);
// accept()
connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen); //accept()->커널이 cliaddr채워줌
if (connfd < 0){
perror("accept error!");
continue; // 에러 나도 서버 종료 안하고 다음 요청 대기
}
// 연결됐다!
printf("Connected from %s: %d\n",
inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port)); //클라포트번호: os가 자동 랜덤배정
// 클라한테 받은 데이터 읽고 그대로 돌려줌
while ((n = read(connfd, buf, BUF_SIZE)) > 0){ //n:읽은 바이트수, connfd: 주고받을 통로(연결소켓)
//읽은 데이터 그대로 전송 (echo)
write(connfd, buf, n); //보낼통로:connfd소켓, 아까보낸내용담은버퍼:buf,보낼 양:n
}
// 클라 연결 종료
printf("client disconnected!\n");
close(connfd);
}
// 7- 서버소켓 닫기 (이 코드엔 거의 도달안함)
close(listenfd);
return 0;
}
>> 실행시키고 터미널에서 밑에처럼하면 프로그램 실행됨

> 여기서
nc 127.0.0.1 8080
이거 쳤을 때 내부 상황:
socket();
connect("127.0.0.1", 8080);
write("hello\n");
read(reply);
>> 클라쪽 소켓 생성 -> 연결요청(서버 ip주소, port번호 가지고) -> 작업 (W/R)
----> 저 모든 작업들을 한번에 해주는게 nc인데,
그걸 코드로 직접 구현하면 아래와 같음
# echo_client.c
#include <stdio.h> // printf, fgets 등 표준 입출력
#include <stdlib.h> // exit() 등
#include <string.h> // strlen(), memset()
#include <unistd.h> // read(), write(), close()
#include <arpa/inet.h> // inet_pton(), htons(), sockaddr_in 등
#define PORT 8080 // 서버 포트 번호
#define BUF_SIZE 1024 // 송수신 버퍼 크기
int main(){
int sockfd; // client-socket
struct sockaddr_in servaddr; // 서버 주소 구조체
char sendbuf[BUF_SIZE]; // 전송 버퍼
char recvbuf[BUF_SIZE]; // 수신 버퍼
ssize_t n; //read() 결과 저장용
// 1- socket() :소켓 생성 (IPv4, TCP)
sockfd = socket(AF_INET, SOCK_STREAM, 0); //아직은 틀만 잡힌 빈 전화기 (-> )
if (sockfd < 0){
perror("socket error!");
exit(1);
}
// 2- 타깃 서버 주소 구조체 초기화 (목적: 커널에 내가 연결하고싶은 서버의 주소 알려주기)
memset(&servaddr, 0, sizeof(servaddr)); // 구조체 0으로 초기화
servaddr.sin_family = AF_INET; // IPv4 사용
servaddr.sin_port = htons(PORT); // 포트번호 -> 네트워크 바이트 순서로 변환
// 3- 타깃 서버 ip주소 설정 (127.0.0.1 = 로컬호스트)
if (inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr) <= 0){ //inet_pton(): internet p to n (사람읽는주소->컴 주소)
// -> &servaddr.sin_addr에 변환된 결과(IPv4)가 저장됨
perror("inet_pton error");
exit(1);
}
// 4- connect() :서버에 연결 시도
if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
perror("connect error!");
exit(1);
}
// 연결 성공!
printf("Connected to echo server at 127.0.0.1: %d\n", PORT);
printf("입력한 문장을 서버에 전송합니다. (ctrl+C로 종료)\n\n");
// 5- write() :표준 입력(stdin)으로부터 한 줄씩 읽어서 서버로 "전송" / read()
while (fgets(sendbuf, BUF_SIZE, stdin) != NULL){
// 서버로 전송
write(sockfd, sendbuf, strlen(sendbuf)); //서버와 연결된 소켓, 보낼 데이터 든 버퍼, -
// 서버로부터 응답 수신
n = read(sockfd, recvbuf, BUF_SIZE - 1); //연결된 소켓, 서버가 보낸 데이터 저장 버퍼, -
// 서버가 응답 닫은 경우
if (n == 0){
printf("server disconnected..!\n");
break;
}
// 받은 데이터를 문자열로 출력
recvbuf[n] = '\0'; //문자열 끝에 NULL 추가
printf("서버 응답: %s", recvbuf);
}
// 6 - 종료 시 소켓 닫기
close(sockfd);
printf("클라 종료! 안녕~~!\n");
return 0;
}
/*
# 실행 순서
1) 서버 실행
cd webproxy-lab
gcc -Wall -O2 echo_server.c -o echo_server
./echo_server
2) 클라 실행
cd webproxy-lab
gcc -Wall -O2 echo_client.c -o echo_client
./echo_client
>> 테스트
*/'STUDY > 정글' 카테고리의 다른 글
| [정글] W8 퀴즈 개념정리 (0) | 2025.11.11 |
|---|---|
| [네트워크] TCP 소켓 통신 구조 정리 (0) | 2025.11.05 |
| [csapp] 세마포어로 쓰레드 동기화하기 (12.5) (0) | 2025.11.03 |
| [TIL] 포인터 문제풀이(2) - G2G (0) | 2025.10.15 |
| [정글] W5 퀴즈 (주요 내용 정리) (0) | 2025.10.14 |