Web-Proxy Lab: Echo Server를 구현하며 웹 서버의 기초를 배워보자

이형준·2023년 5월 20일
2

TIL

목록 보기
26/37

  • 클라이언트(위) 와 서버(아래)가 통신하는 모습

야호~ 에코 서버 📢

웹 프록시 서버의 첫걸음은 매~우 간단한 에코 서버를 만들어 보는 것! 말 그대로 메아리처럼 받은 입력을 그대로 돌려주는 서버를 구현해야만 했다. 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);
}
profile
저의 미약한 재능이 세상을 바꿀 수 있을 거라 믿습니다.

0개의 댓글