2025. 5. 7. 15:09ㆍ개발
Tiny Web Server 분석 (CS:APP 기반)
이 글에서는 CS:APP 책 11장 (네트워크 프로그래밍) 내용을 기반으로 Tiny Web Server의 전체 동작 흐름을 분석
1. 전체 동작 흐름 요약
- main() 함수에서 서버 소켓을 열고(
Open_listenfd()), 클라이언트의 연결을 기다림. - 클라이언트가 접속하면
Accept()를 호출해 새 연결을 받고,doit()함수에 전달. - doit() 함수는 HTTP 요청을 파싱하고, 정적(static)인지 동적(dynamic)인지 판별.
- 정적 콘텐츠면
serve_static(), 동적 콘텐츠면serve_dynamic()호출. - 응답을 보낸 후 연결을
Close()로 종료.
2. 클라이언트 연결 흐름 (main)
int listenfd = Open_listenfd(argv[1]);
while (1) {
clientlen = sizeof(clientaddr);
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
Getnameinfo((SA *)&clientaddr, clientlen, hostname, MAXLINE, port, MAXLINE, 0);
doit(connfd);
Close(connfd);
}
📍 clientaddr는 struct sockaddr_storage 구조체입니다.
이는 IPv4와 IPv6를 모두 포괄할 수 있는 범용 주소 구조체로, OS가 자동으로 알맞게 채워줍니다.
struct sockaddr_storage {
sa_family_t ss_family; // 주소 체계 (AF_INET, AF_INET6)
char __ss_padding[_SS_PADSIZE];
};
이 주소를 문자열로 바꾸기 위해 Getnameinfo()를 사용합니다. 클라이언트의 IP, 포트를 hostname, port로 저장합니다.
3. doit() 함수 – 요청 파싱 및 라우팅
Rio_readinitb(&rio, fd);
Rio_readlineb(&rio, buf, MAXLINE);
sscanf(buf, "%s %s %s", method, uri, version);
1. 요청 메서드(GET), URI, 버전 파싱
2. parse_uri()를 통해 정적/동적 여부 판단
3. stat() 함수로 파일 존재 여부 확인
4. 파일의 읽기/실행 권한 확인 후 응답 처리
4. parse_uri() – 정적 vs 동적 콘텐츠 구분
if (!strstr(uri, "cgi-bin")) {
strcpy(cgiargs, "");
strcpy(filename, ".");
strcat(filename, uri);
if (uri[strlen(uri) - 1] == '/')
strcat(filename, "home.html");
return 1;
} else {
ptr = strchr(uri, '?');
...
return 0;
}
- URI에 cgi-bin이 포함되어 있으면 동적 콘텐츠로 간주하고 CGI 실행
- 포함되어 있지 않으면 정적 콘텐츠 (파일 그대로 반환)
5. serve_static() – 정적 파일 응답
파일 타입을 판별해 Content-type 헤더를 설정하고, 파일을 mmap()으로 메모리에 매핑 후 전송합니다.
srcfd = Open(filename, O_RDONLY, 0);
srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
Rio_writen(fd, srcp, filesize);
Munmap(srcp, filesize);
정적 콘텐츠: 이미지, html, 텍스트 등을 그대로 읽어서 전송
📌 파일 확장자에 따라 get_filetype()으로 MIME 결정
6. serve_dynamic() – CGI 프로그램 실행
CGI 실행을 위해 환경변수를 설정하고, Dup2()로 표준 출력을 클라이언트 소켓으로 리다이렉션한 후 Execve()로 실행합니다.
if (Fork() == 0) {
setenv("QUERY_STRING", cgiargs, 1);
Dup2(fd, STDOUT_FILENO);
Execve(filename, emptylist, environ);
}
Wait(NULL);
이때 실행되는 CGI는 adder 같은 프로그램이며, 실행 결과가 그대로 클라이언트에게 출력됩니다.
7. clienterror() – 오류 응답 처리
존재하지 않는 파일, 권한 문제, 미지원 메서드 등의 오류에 대해 HTML 에러 메시지를 생성하고 전송합니다.
HTTP/1.0 404 Not Found
Content-type: text/html
Content-length: ...
에러도 하나의 완전한 HTTP 응답이기 때문에, 상태 라인, 헤더, 바디 모두 포함됩니다.
8. CS:APP 책과의 연결 – 11장 내용 정리
- 11.4.1: 소켓 생성과 바인딩 –
socket(),bind(),listen()을Open_listenfd()안에서 처리 - 11.4.3: 클라이언트 연결 처리 –
Accept()로 연결 수락,sockaddr_storage사용 - 11.5.1: HTTP 요청 파싱 – 요청 라인과 헤더 분석
- 11.5.3: 정적/동적 콘텐츠 응답 – 파일 처리 vs CGI 실행
정리
- Tiny Web Server는 HTTP 1.0 기반의 간단한 웹 서버로, 정적 파일과 CGI 실행을 모두 지원합니다.
sockaddr_storage는 IP 버전에 상관없이 클라이언트 주소를 담을 수 있어 범용적입니다.- 소켓 흐름, MIME 처리, CGI 실행 등 실제 웹 서버 작동 원리를 배우기에 훌륭한 예제입니다.
이 Tiny 서버를 기반으로 멀티스레드/캐시/프록시 등으로 확장할 수 있습니다.
tiny 웹서버 코드구현
https://github.com/applepc24/tiny_web-proxy/blob/main/tiny/tiny.c
'개발' 카테고리의 다른 글
| 프록시 서버에서 캐시 최신화 방법들 (0) | 2025.05.15 |
|---|---|
| 프록시 서버 구현 (0) | 2025.05.14 |
| 동적 메모리 할당(Malloc) 심화 및 구현 1-2 (0) | 2025.04.30 |
| 동적 메모리 할당(Malloc) 심화 및 구현 1-1 (0) | 2025.04.28 |
| 컴퓨터 시스템 1주차 (0) | 2025.03.24 |