웹 프록시 서버의 첫걸음은 매~우 간단한 에코 서버를 만들어 보는 것! 말 그대로 메아리처럼 받은 입력을 그대로 돌려주는 서버를 구현해야만 했다. CS:APP
를 참고하며 꼼꼼히 코드를 작성하면, 무리 없이 Echo Server
를 구현할 수 있었다!
아무래도 우리가 흔히 사용하는 함수가 아닌, csapp.h
에 정의된 다양한 도움 함수들을 이용하기에, 처음 코드를 마주했을 때 이해하는 것이 상당히 어려웠다. 코드를 하나하나 뜯어보며 주석을 작성했었는데, 후배 정글러들이 이해할 때 도움이 되길 바라며 올려본다.
#include "csapp.h"
int main(int argc, char **argv) // argc: 입력받은 인자의 수 argv: 입력받은 인자들의 배열
{
int clientfd;
char *host, *port, buf[MAXLINE];
rio_t rio;
if (argc != 3) { // 파일 실행 시 인자를 제대로 넘겨주지 않았을 경우
fprintf(stderr, "usage: %s <host> <port>\n", argv[0]); // 안내 메세지를 출력하고,
exit(0); // 어플리케이션 종료
}
host = argv[1]; // 전달해준 첫 번째 인자를 host에 저장
port = argv[2]; // 전달해준 첫 번째 인자를 port에 저장
clientfd = Open_clientfd(host, port); // 소켓 인터페이스 핸들링을 도와주는 Open_clientfd 함수를 호출하여 서버와 연결하고, 리턴받은 소켓 식별자를 clientfd에 저장
Rio_readinitb(&rio, clientfd); // rio 구조체를 초기화하고, rio를 통해 파일 디스크립터 clientfd에 대한 읽기 작업을 수행할 수 있도록 설정
while (Fgets(buf, MAXLINE, stdin) != NULL) { // 반복하여 유저에게서 받은 입력을 buf에 저장하는데, 입력이 끊기거나 오류가 발생한다면 반복문을 종료
Rio_writen(clientfd, buf, strlen(buf)); // 파일 디스크립터를 통해 buf에 저장된 데이터를 서버로 전송
Rio_readlineb(&rio, buf, MAXLINE); // rio 구조체를 통해 파일 디스크립터에서 한 줄의 문자열을 읽어와 buf에 저장, MAXLINE은 버퍼의 최대 크기
Fputs(buf, stdout); // buf에 저장된 문자열을 표준 출력 stdout에 출력해줌
}
Close(clientfd); // 파일 디스크립터를 닫아서 클라이언트의 연결을 종료하고 사용한 리소스를 반환해줌
exit(0);
}
#include "csapp.h"
void echo(int connfd); // 클라이언트와 통신하는 함수 echo를 선언
int main(int argc, char **argv)
{
int listenfd, connfd; // 리스닝 소켓과 연결된 클라이언트 소켓의 파일 디스크립터
socklen_t clientlen; // 클라이언트의 주소 길이
struct sockaddr_storage clientaddr; // 클라이언트의 주소 정보를 저장하는 구조체
char client_hostname[MAXLINE], client_port[MAXLINE]; // 클라이언트의 호스트 이름과 포트 번호를 저장하는 배열
if (argc != 2) { // 프로그램 실행 시 포트 번호를 인자로 입력하지 않았을 경우
fprintf(stderr, "usage: %s <port>\n", argv[0]); // 사용법을 출력하고
exit(0); // 프로그램 종료
}
listenfd = Open_listenfd(argv[1]); // 주어진 포트 번호로 리스닝 소켓을 열어서 파일 디스크립터를 얻음
while (1) { // 무한 루프
clientlen = sizeof(struct sockaddr_storage); // 클라이언트의 주소 길이를 초기화
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen); // 클라이언트의 연결을 받아들이고, 연결된 클라이언트 소켓의 파일 디스크립터를 얻음
Getnameinfo((SA*) &clientaddr, clientlen, client_hostname, MAXLINE, client_port, MAXLINE, 0); // 클라이언트의 호스트 이름과 포트 번호를 얻음
printf("Connected to (%s, %s)\n", client_hostname, client_port); // 연결된 클라이언트의 정보를 출력
echo(connfd); // 클라이언트와 통신하는 echo 함수 호출
Close(connfd); // 클라이언트 소켓을 닫음
}
exit(0);
}