[05.07/week08] TIL

CHO WanGi·2025년 5월 7일

KRAFTON JUNGLE 8th

목록 보기
48/89

오늘 하루 요약

✏️ 오늘의 한 일

  • Web_Proxy Lab Sequental Proxy, Thread per connection

🌈오늘의 성장!

Sequental Proxy

void doit(int clientfd)
{
  int serverfd;
  char request_buf[MAXLINE], response_buf[MAXLINE];
  char method[MAXLINE], uri[MAXLINE], path[MAXLINE], hostname[MAXLINE], port[MAXLINE];
  char *srcp, filename[MAXLINE], cgiargs[MAXLINE];
  rio_t request_rio, response_rio;

  /* Request 요청 라인 읽기  Client -> Proxy */
  Rio_readinitb(&request_rio, clientfd);             // 클라이언트의 요청을 읽기 위해 rio와 fd 연결
  Rio_readlineb(&request_rio, request_buf, MAXLINE); // 요청 라인 읽기
  printf("Request headers:\n %s\n", request_buf);
  sscanf(request_buf, "%s %s", method, uri); // 요청 라인에서 method, uri를 읽어서 지역 변수 `method` `uri`에 할당
  parse_uri(uri, hostname, port, path);
  sprintf(request_buf, "%s %s %s\r\n", method, path, "HTTP/1.0"); // end server에 전송 요청  & 라인 수정

  // 요청 메소드가 GET | HEAD가 아닌 경우 예외 처리
  if (strcasecmp(method, "GET") && strcasecmp(method, "HEAD"))
  {
    clienterror(clientfd, method, "501", "Not implemented", "Tiny does not implement this method");
    return;
  }

  // end server 소켓 생성
  serverfd = is_local_test ? Open_clientfd(hostname, port) : Open_clientfd("52.79.234.188", port);
  if (serverfd < 0)
  {
    clienterror(serverfd, method, "502", "Bad Gateway", "Failed to establish connection with the end server");
    return;
  }

  /* Request 요청 라인 전송 Proxy -> Server */
  Rio_writen(serverfd, request_buf, strlen(request_buf));

  /* Request 요청 헤더 읽기 & 전송  Client -> Proxy -> Server */
  read_requesthdrs(&request_rio, request_buf, serverfd, hostname, port);

  /* Response 라인 읽기 & 전송  Server -> Proxy -> Client */
  Rio_readinitb(&response_rio, serverfd);                   // 서버의 응답을 담을 버퍼 초기화
  Rio_readlineb(&response_rio, response_buf, MAXLINE);      // 응답 라인 읽기
  Rio_writen(clientfd, response_buf, strlen(response_buf)); // 클라이언트에 응답 라인 보내기
  /* Response  헤더 읽기 & 전송 Server -> Proxy -> Client */
  int content_length;
  while (strcmp(response_buf, "\r\n"))
  {
    Rio_readlineb(&response_rio, response_buf, MAXLINE);
    if (strstr(response_buf, "Content-length")) // 응답 바디 수신에 사용하기 위해 바디 사이즈 저장
      content_length = atoi(strchr(response_buf, ':') + 1);
    Rio_writen(clientfd, response_buf, strlen(response_buf));
  }
  /* Response 바디 읽기 & 전송 Server -> Proxy -> Client */
  if (content_length)
  {
    srcp = malloc(content_length);
    Rio_readnb(&response_rio, srcp, content_length);
    Rio_writen(clientfd, srcp, content_length);
    free(srcp);
  }
}

Thread per connection

  while (1)
  {
 
    clientlen = sizeof(clientaddr);
    printf("Main thread: Waiting for connection...\n"); // 메인 스레드 상태 로그
    fflush(stdout);

    // 주의: connfd를 루프 지역 변수로 바로 사용하면 레이스 컨디션 발생 가능!
    // 동적 할당을 사용하여 각 스레드에 고유한 connfd 복사본을 전달해야 함.
    int *connfd_ptr = malloc(sizeof(int)); // 1. connfd를 저장할 메모리 동적 할당
    if (connfd_ptr == NULL)
    {
      fprintf(stderr, "Malloc error\n");
      continue; // 메모리 할당 실패 시 다음 연결 시도
    }

    *connfd_ptr = Accept(listenfd, (SA *)&clientaddr, &clientlen); // 2. Accept 결과를 할당된 메모리에 저장
    if (*connfd_ptr < 0)
    {                   // Accept 실패 시
      free(connfd_ptr); // 할당된 메모리 해제
      // 에러 로그 또는 기타 처리 (Accept 래퍼가 종료시킬 수도 있음)
      continue;
    }

    // 클라이언트 정보 가져오기 (선택 사항)
    char client_hostname[MAXLINE], client_port[MAXLINE];
    Getnameinfo((SA *)&clientaddr, clientlen, client_hostname, MAXLINE, client_port, MAXLINE, 0);
    printf("Main thread: Accepted connection from (%s, %s) on fd %d\n", client_hostname, client_port, *connfd_ptr);
    fflush(stdout);

    // 새로운 스레드 생성
    pthread_t tid;
    // 3. 스레드 생성 함수(thread_routine)를 호출하고, connfd_ptr을 인자로 전달
    Pthread_create(&tid, NULL, thread_routine, connfd_ptr);
    // !!! 중요 !!!
    // Main 스레드는 clientfd(여기서는 *connfd_ptr)를 닫으면 안 됩니다!
    // 새로 생성된 스레드가 doit 함수를 실행하고 난 뒤 직접 닫아야 합니다.
    // Main 스레드는 스레드 생성 후 바로 다음 Accept()로 돌아갑니다.
  }
} 

두 코드의 핵심 차이점

구분Sequential Proxy (순차적)Thread per connection (연결당 스레드)
처리 방식클라이언트 요청 하나씩 순서대로 처리각 클라이언트 연결마다 별도 스레드를 생성하여 동시에 처리
동시성없음 (단일 작업 흐름)높음 (다중 작업 흐름)
대기 시간앞선 요청이 끝나야 다음 요청 처리 (클라이언트 대기 발생)여러 요청 동시 처리 (클라이언트 대기 시간 감소)
자원 활용시스템 자원 활용도 낮음시스템 자원 활용도 높음 (병렬 처리)
복잡도단순상대적으로 복잡 (스레드 관리, connfd 안전 전달 필요)
성능 (부하 시)요청 많아지면 느려짐요청 많아도 비교적 빠른 응답 유지 (단, 스레드 한계 존재)

⛏ 오늘의 삽질

make 파일 안쓰고 일일히 컴파일하던 내자신....

profile
제 Velog에 오신 모든 분들이 작더라도 인사이트를 얻어가셨으면 좋겠습니다 :)

0개의 댓글