구현하기

Web Proxy #2 : 기본적인 Echo 서버 만들기

pwerty 2025. 5. 3. 22:00

2025.05.03 - [구현하기] - Tiny Web Server 개발 기록 #1

 

Tiny Web Server 개발 기록 #1

2025.05.02 - [분류 전체보기] - [CS:APP] 11 : 웹 서버 [CS:APP] 11 : 웹 서버모든 네트워크 애플리케이션은 클라이언트-서버 모델을 기반으로 한다.여기서 이야기하는 모델에 따르면, 애플리케이션은 한

hyeonistic.tistory.com

지난 글에서는 알아보았다. 자연어 식 코드를.. 그럼 이제 실제로 해보겠다.

헤더.zip
0.01MB

 

이걸 받는다. 그러고 같은 경로에 소스를 작성한다.

echo : client

#include "csapp.h"

int Open_clientfd(char *hostname, char *port);

int main(int argc, char **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];
    port = argv[2];

    clientfd = Open_clientfd(host, port);
    Rio_readinitb(&rio, clientfd);

    while (Fgets(buf,MAXLINE,stdin) != NULL)
    {
        Rio_writen(clientfd, buf, strlen(buf));
        Rio_readlineb(&rio, buf, MAXLINE);
        Fputs(buf, stdout);
    }
    Close(clientfd);
    exit(0);
}

int Open_clientfd(char *hostname, char *port)
{
    int clientfd;
    struct addrinfo hints, *listp, *p;

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_NUMERICSERV;
    hints.ai_flags |= AI_ADDRCONFIG;
    Getaddrinfo(hostname, port, &hints, &listp);

    for( p = listp; p; p = p->ai_next)
    {
        if((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)
            continue;
        if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1)
            break;
        Close(clientfd); 

    }
    Freeaddrinfo(listp);
    if (!p)
        return -1;
    else
        return clientfd;
}

echo : server

#include "csapp.h"

void echo(int connfd);
int Open_listenfd(char *port);

void echo(int connfd)
{
    size_t n;
    char buf[MAXLINE];
    rio_t rio;

    Rio_readinitb(&rio, connfd);
    while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0)
    {
        printf("server received %d byted\n", (int)n);
        Rio_writen(connfd, buf, n);
    }
}

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);
        echo(connfd);
        Close(connfd);
    }
}

int Open_listenfd(char *port)
{
    struct addrinfo hints, *listp, *p;
    int listenfd, optval = 1;
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
    hints.ai_flags |= AI_NUMERICSERV;

    Getaddrinfo(NULL, port, &hints, &listp);    
    for (p = listp; p; p->ai_next)
    {
        if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)
            continue;
        Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int));

        if(bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)
            break;
        Close(listenfd);
    }

    Freeaddrinfo(listp);
    if (!p)
        return -1;

    if (listen(listenfd, LISTENQ) < 0)
    {
        Close(listenfd);
        return -1;
    }
    return listenfd;
}

Makefile

# Makefile for echoclient and echoserveri

CC = gcc
CFLAGS = -Wall -Wextra -g
LDFLAGS = -lpthread

# 디렉토리 및 파일
SRCDIR = .
TARGETS = echoclient echoserveri

all: $(TARGETS)

echoclient: $(SRCDIR)/echoclient.c $(SRCDIR)/csapp.c
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)

echoserveri: $(SRCDIR)/echoserveri.c $(SRCDIR)/csapp.c
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)

clean:
	rm -f $(TARGETS)

Makefile을 작성해서 이 내용으로 기입한다.

그리고 make하고,

./[서버] <사용 할 포트 번호> | ./[클라] localhost <왼쪽에 적었던 포트번호>

이 두 명령어를 각기 다른 Terminal에서 실행한다. 그럼 끝! 

다음엔 진짜 Tiny Server에 대해 논해보겠다.