0203

학교

[컴퓨터 네트워크] Socket programming (Part 2) - (3)

일자 : 24-2 11주차 2차시

[9] 동기화

  • 동기화 문제

    • 두 개 이상의 스레드가 공유 데이터를 접근할 때, 오직 하나의 스레드만이 공유 데이터를 접근할 수 있으며, 다른 스레드는 접근할 수 없어야 한다.

  • 일반적인 동기화 방법

    • 뮤텍스(Mutex)

    • 세마포어(Semaphore)

[10] 동기화 문제

1) 정상

  • 변수 num에 순차적으로 접근하면 문제가 발생하지 않는다.

  • 상황 설명

    • 변수 num에 순차적으로 접근할 때는 문제가 발생하지 않는다.

    • Thread 1이 num을 99에서 100으로 증가시키고

    • Thread 2가 그 후에 100에서 101로 증가시킨다.

    • 결과적으로 변수가 예상대로 2번 증가하여 101이 된다.

2) 비정상

  • 동기화 문제

  • 스레드 1과 스레드 2는 각각 1씩 증가하지만, 변수 num은 1만 증가한다.

  • 상황 설명

    • 두 스레드가 동시에 변수에 접근할 때 발생하는 문제를 보여준다.

    • Thread 1과 Thread 2가 각각 변수를 1씩 증가시키려고ㅎ한다.

    • 하지만 두 스레드가 거의 동시에 변수 num에 접근하면서:

      1. Thread 1이 99를 읽음

      2. Thread 2도 99를 읽음

      3. Thread 1이 100으로 증가

      4. Thread 2도 100으로 증가

    • 결과적으로 두 번의 증가 연산이 있었음에도 변수는 단 한 번만 증가한 것처럼 된다.

  • 이는 Race Condition이라고 불리는 전형적인 동기화 문제이다

  • 이러한 문제를 해결하기 위해서는 뮤텍스(Mutex)나 세마포어(Semaphore)와 같은 동기화 메커니즘을 사용해야 한다.

  • 이를 통해 한 번에 하나의 스레드만 변수에 접근할 수 있도록 보장할 수 있습니다.

[11] 뮤텍스 (Mutex)

  • 뮤텍스 변수는 공유 데이터 자원에 대한 접근을 보호하는 "잠금"처럼 작용한다.

  • 한 번에 하나의 스레드만 뮤텍스 변수를 잠글(또는 소유할) 수 있다. 따라서 여러 스레드가 뮤텍스를 잠그려고 시도하더라도 오직 하나의 스레드만 성공하게 된다.

  • 다른 스레드는 소유 스레드가 해당 뮤텍스를 잠금 해제할 때까지 그 뮤텍스를 소유할 수 없다. 스레드는 보호된 데이터에 접근하기 위해 "차례대로" 접근해야 한다.

[12] 뮤텍스 함수들

C
#include <pthread.h>

int pthread_mutex_init(pthread_mutex_t *restrict mutex, 
                       const pthread_mutexattr_t *restrict attr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
  • 뮤텍스 변수(mutex)는 사용되기 전에 pthread_mutex_init()을 통해 초기화되어야 한다.

1) 파라미터 설명

  • mutex: 뮤텍스 변수

  • attr: 기본값은 NULL

2) 설명

  • pthread_mutex_lock() 함수는 스레드가 지정된 뮤텍스 변수에 대한 잠금을 획득할 때 사용된다. 만약 뮤텍스가 이미 다른 스레드에 의해 잠겨 있으면, 해당 스레드는 잠금이 풀릴 때까지 대기한다.

  • pthread_mutex_unlock() 함수는 소유 스레드가 호출할 경우 뮤텍스 잠금을 해제한다.

4. 간단한 웹 서버 구현

[1] 간단한 웹 서버

  • TCP 소켓을 사용하여 간단한 웹 서버를 구현한다.

  • 우리 웹 서버는 클라이언트 요청에 의해 간단한 웹 페이지를 제공할 수 있다.

  • 동시성을 지원하는 서버를 구현할 것이다.

  • 클라이언트 프로그램을 구현하는 대신 웹 브라우저를 사용할 것이다.

[2] 간단한 웹 서버를 위한 함수 호출

1) 서버 측 초기화 단계:

  1. socket() - 서버 소켓 생성

  2. bind() - 소켓을 특정 IP와 포트에 바인딩

  3. listen() - 클라이언트의 연결 요청을 대기

2) 통신 과정:

  1. 클라이언트 측:

    • 웹 브라우저 실행

    • URL 입력하여 서버에 연결 요청

  2. 연결 설정:

    • 클라이언트가 연결 요청 (Connection Request) 보냄

    • 서버의 accept() 함수가 이를 수락

    • 새로운 스레드 생성하여 클라이언트 요청 처리

  3. HTTP 통신:

    • 클라이언트: HTTP Request 전송 (예: "GET /index.html HTTP/1.1")

    • 서버: request_handler()로 요청 처리

    • 서버: send_data()로 응답 전송 (예: "HTTP/1.0 200 OK" + index.html)

    • 클라이언트: HTTP Response 수신 및 웹 페이지 표시

  4. 연결 종료:

    • close(client): 클라이언트와의 연결 종료

    • close(server): 서버 소켓 종료

3) 주요 특징:

  • 서버는 멀티스레드 방식으로 동작하여 여러 클라이언트 요청을 동시 처리

  • HTTP 프로토콜을 사용하여 요청과 응답을 주고받음

  • 각 연결은 독립적으로 처리되며, 작업 완료 후 연결이 종료됨