1121-TIL(quiz, echo, tiny)

그로밋·2023년 11월 21일
0

krafton jungle

목록 보기
34/58

quiz

echo server

open_clientfd() of csapp.c

/**
 * int open_clientfd(char *hostname, char *port)
 *      @include : <csapp.h>
 *      @brief : 파라미터로 받아오는 호스트이름과 포트번호로 되있는 서버에게 커넥션을 오픈
 *                (이 함수는 재진입을 지원하고 프로토콜로부터 독립적)
 *      @param[in] p->ai_family : int domain (32-bit IP address사용함을 나타냄)
 *      @param[in] p->ai_socktype : int type (이 소켓이 end-point임을 나타냄)
 *      @param[in] p->ai_protocol : int protocol
 *      @return : 성공시, 읽고 쓸 준비가 되어있는 소켓 디스크립터를 반환.
 *                실패시, -1
*/
int open_clientfd(char *hostname, char *port) {
    int clientfd, rc;
    struct addrinfo hints, *listp, *p;

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM;  /* Open a connection */
    hints.ai_flags = AI_NUMERICSERV;  /* ... using a numeric port arg. */
    hints.ai_flags |= AI_ADDRCONFIG;  /* Recommended for connections */
    /**
     * int getaddrinfo(hostname, port, &hints, &listp)
     *      freeaddrinfo(), gai_strerror()와 한세트세트.
     *      @include: <sys/types.h>, <sys/socket.h>, <netdb.h>
     *      @brief : 호스트 이름, 주소, 서비스이름, 포트넘버의 스트링을 socket address 구조로 바꿔줌
     *      @param[in] hostname->const char *host
     *      @param[in] port->const char *service
     *      @param[in] 앤퍼센트hints ->const struct addrinfo *hints
     *      @param[in] 앤퍼센트listp ->struct addrinfo **result 
     *                               여기서 &listp가 더블포인터(**result)와 같은 이유: listp가 포인터라서.
     *      @return : 성공시, return 0
     *                실패시, nonzero error code
     */
    if ((rc = getaddrinfo(hostname, port, &hints, &listp)) != 0) {
        fprintf(stderr, "getaddrinfo failed (%s:%s): %s\n", hostname, port, gai_strerror(rc));
        return -2;
    }
  
    /* 링크드리스트를 하나씩 워크스루하며 성공적으로 연결가능한 것을 찾는다. addrinfo구조체에 링크드리스트의 다음 아이템을 위한 *ai_next 있음 */
    for (p = listp; p; p = p->ai_next) {
        /* socket descriptor 생성하기 */
        /** int socket(p->ai_family, p->ai_socktype, p->ai_protocol)
         *      @include: <sys/types.h>, <sys/socket.h>
         *      @brief : socket descriptor를 생성함
         *      @param[in] p->ai_family : int domain (32-bit IP address사용함을 나타냄)
         *      @param[in] p->ai_socktype : int type (이 소켓이 end-point임을 나타냄)
         *      @param[in] p->ai_protocol : int protocol
         *      @return : 성공시, 양수의 descriptor. 실패시, -1
        */
        if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) 
            continue; /* 실패시 다음 시도. Socket failed, try the next */

        /* 서버와 연결하기 */
        /** int connect(clientfd, p->ai_addr, p->ai_addrlen)
         *      @include : <sys/socket.h>
         *      @brief : a 클라이언트가 서버와 연결을 한다
         *      @param[in] clientfd : int clientfd
         *      @param[in] p->ai_addr : const struct sockaddr *addr
         *      @param[in] p->ai_addrlen : socklen_t addrlen
         *      @return : 성공시, 0. 실패시, -1
        */
        if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1) 
            break; /* Success */
        if (close(clientfd) < 0) { /* Connect failed, try another */  //line:netp:openclientfd:closefd
            fprintf(stderr, "open_clientfd: close failed: %s\n", strerror(errno));
            return -1;
        } 
    } 

    /* Clean up */
    freeaddrinfo(listp);
    if (!p) /* All connects failed */
        return -1;
    else    /* The last connect succeeded */
        return clientfd;
}
/* $end open_clientfd */

open_listenfd() of csapp.c

/**  
 * int open_listenfd(char *port) 
 *      @brief : listening descriptor를 오픈하고 리턴함
 *                (이 함수는 헬퍼함수로 재진입을 지원하고 프로토콜로부터 독립적)
 *      @param[in] port : 
 *      @return : 성공시, 읽고 쓸 준비가 되어있는 리스닝 디스크립터를 반환.
 *                실패시, -2 for getaddrinfo error
 *                      -1 with errno set for other errors
*/
int open_listenfd(char *port) 
{
    struct addrinfo hints, *listp, *p;
    int listenfd, rc, optval=1;

    /* Get a list of potential server addresses */
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM;             /* Accept connections */
    hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* ... on any IP address */
    hints.ai_flags |= AI_NUMERICSERV;            /* ... using port number */
    if ((rc = getaddrinfo(NULL, port, &hints, &listp)) != 0) {
        fprintf(stderr, "getaddrinfo failed (port %s): %s\n", port, gai_strerror(rc));
        return -2;
    }

    /* Walk the list for one that we can bind to */
    for (p = listp; p; p = p->ai_next) {
        /* Create a socket descriptor */
        if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) 
            continue;  /* Socket failed, try the next */

        /* Eliminates "Address already in use" error from bind */
        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,    //line:netp:csapp:setsockopt
                   (const void *)&optval , sizeof(int));

        /* Bind the descriptor to the address */
        /* 디스크립터를 주소에 바인드하기 */
        /** bind(listenfd, p->ai_addr, p->ai_addrlen)
         *      @include : <sys/socket.h>
         *      @brief : 디스크립터를 주소에 바인드. 커널에게 서버의 소켓 주소(addr)을 소켓 디스크립터(sockfd)와 연결하라고 요청함
         *      @param[in] listenfd : int sockfd
         *      @param[in] p->ai_addr : const struct sockaddr *addr
         *      @param[in] p->ai_addrlen : socklen_t addrlen
         *      @return : 성공시, 0. 실패시, -1
        */
        if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)
            break; /* Success */
        if (close(listenfd) < 0) { /* Bind failed, try the next */
            fprintf(stderr, "open_listenfd close failed: %s\n", strerror(errno));
            return -1;
        }
    }


    /* Clean up */
    freeaddrinfo(listp);
    if (!p) /* No address worked */
        return -1;

    /* Make it a listening socket ready to accept connection requests */
    if (listen(listenfd, LISTENQ) < 0) {
        close(listenfd);
	return -1;
    }
    return listenfd;
}
/* $end open_listenfd */

main() of echoclient.c

/* echoclient p.968 */

#include "csapp.h"

/**
 * int main(int argc, char **argv)
 *      @include : <csapp.h>
 *      @brief : fgets함수가 EOF를 만날 때 까지, 반복적으로
 *               텍스트 라인을 서버에게 보내고, 서버로부터 오는 에코라인을 읽고, 결과를 출력한다.
 *               루프 종료시, 디스크립터를 클로즈한다.
 *      @param[in] argc : 안쓰임. 왜안쓰이지..?
 *      @param[in] 별별argv : argv[1] - host정보, argv[2] - port정보
 *      @param[in] p->ai_protocol : int protocol
*/
int main(int argc, char **argv)
{
    int clientfd;
    char *host, *port, buf[MAXLINE];
    rio_t rio; 

    if (arg != 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); /* Associate a descriptor with a read buffer and reset buffer   */
    
    while (Fgets(buf, MAXLINE, stdin) != NULL) {
        Rio_writen(clientfd, buf, strlen(buf));
        Rio_readlineb(&rio, buf, MAXLINE);
        Fputs(buf, stdout);
    }
    Close(clientfd);
    exit(0);


}

main() of echoserveri.c

/* echoclient p.970 */

/**
 * int main(int argc, char **argv)
 *      @include : <csapp.h>
 *      @brief : 리스닝 디스크립터를 열고 무한루프에 들어가서 반복적으로
 *               클라이언트로부터의 연결을 기다리고,
 *               연결된 클라이언트의 도메인 이름과 포트를 출력하고,
 *               클라이언트의 echo 함수를 호출한다.
 *      @param[in] argc : 
 *      @param[in] 별별argv : argv[0] - ?, argv[1] - port정보
*/

#include "csapp.h"

void echo(int connfd);

int main(int argc, char **argv)
{
    int listenfd, connfd;
    socklen_t clientlen;
    /**
     * 여기 아래에 clientaddr을 주목할 필요가 있다.
     *      struct sockaddr_in 아니라 sockaddr_storage 구조체로 한 이유는 
     *      이 구조체는 어떤 타입의 소켓 주소를 담아도 충분히 크기 때문에 프로토콜에 독립적이기 때문이다.
     *      따라서 accept가 리턴되기 전에 클라이언트 소켓의 주소를 clientaddr에 채워넣어진다.
    */
    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);
        /**
         * Accept(listenfd, (SA *)&clientaddr, &clientlen)
         * Accept(int s, struct sockaddr *addr, <error-type> *addrlen)
        */
        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);
        Close(connfd);
    }

    exit(0);
}

echo() of echoserveri.c

/**
 * repeatedly reads and writes lines of text until the rio_readlineb function encounters EOF

*/
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) {
        Rio_writen(connfd, buf, n);
    }
}

텔넷으로 테스트

aws

인바운드 규칙 편집에서 테스트를 위한 규칙을 아래 그림과 같이 추가해준다.
포트번호는 well known port number 제외하고 아무거나 해주고 ip는 퍼블릭아이디/32로 해준다.(왜 32로 해줘야하는지는 모르겠다)

mac terminal

telnet을 brew install telnet 명령어로 다운하고
telnet 퍼블릭아이피주소 열어둔포트번호 명령어를 치면 연결이 되는 모습을 아래와 같이 확인할 수 있다.

tiny

https://github.com/geeks-lab/webproxy-lab

profile
Work as though your strength were limitless. <S. Bernhardt>

0개의 댓글