반응형

std::optional이 필요한가요?

C++에서는 전통적으로 "값이 없음을 나타내는" 방법이 마땅치 않았어요.

  • 포인터일 경우는 nullptr로 표현할 수 있었지만,
  • 값 타입(int, double, struct 등)에서는 특별한 값을 정하거나 별도 플래그를 둬야 했죠.

이런 문제를 해결하기 위해 optional이 등장했어요.

std::optional 이란?

std::optional<T>는 T 타입의 값을 가질 수도 있고, 안 가질 수도 있는 컨테이너입니다.

#include <optional>

std::optional<int> maybeGetValue(bool condition) {
    if (condition)
        return 42;
    else
        return std::nullopt;  // 아무 값도 없음
}

 


 주요 특징 정리

기능설명
std::optional<T> T 타입의 optional 객체 생성
std::nullopt 값이 없음을 표현하는 상수
.has_value() or if (opt) 값이 있는지 확인
.value() 값을 가져옴 (값이 없으면 예외 발생)
.value_or(default) 값이 있으면 그 값, 없으면 기본값 반환
*opt or opt.value() 값에 접근 (값이 있어야 안전함)

사용 예제

#include <iostream>
#include <optional>
#include <string>

std::optional<std::string> findNameById(int id) {
    if (id == 1)
        return "Alice";
    else
        return std::nullopt;
}

int main() {
    auto name = findNameById(2);

    if (name.has_value()) {
        std::cout << "이름: " << *name << "\n";
    } else {
        std::cout << "이름을 찾을 수 없습니다.\n";
    }

    return 0;
}

언제 쓰면 좋을까?

  • 함수에서 값을 반환할 수도, 안 할 수도 있는 경우 (null 대신)
  • 포인터를 쓰지 않고도 값 없음(null) 상태 표현 가능
  • std::optional<T>는 T가 복사/이동 가능한 타입일 때 사용 가능

C++에서 Optional은 어떤 점이 좋을까?

  • 명시적이고 타입 안정성 있음 (null pointer 같은 오류 줄임)
  • 함수 인터페이스가 더 직관적이고 안전해짐
  • null 체크나 특별한 에러코드를 리턴하는 방식보다 표현력이 풍부함

'Develop > C&CPP' 카테고리의 다른 글

[cpp] thread  (0) 2025.04.20
[cpp] atomic  (0) 2025.04.20
[cpp] mutex, lock  (0) 2025.04.20
[cpp] cpp17에서 달라진 점  (0) 2025.04.20
[cpp] cpp14에서 추가된 것  (0) 2025.04.20
반응형

std::thread 사용법 정리 (C++17 기준)

1. 기본 개념

  • C++11부터 도입된 std::thread는 병렬 실행을 위한 클래스입니다.
  • 쓰레드를 생성하면 자동으로 시작되며, 작업이 끝날 때까지 join() 또는 detach() 호출로 관리해야 합니다.

2. 기본 예제

#include <iostream>
#include <thread>

void say_hello() {
    std::cout << "Hello from thread!" << std::endl;
}

int main() {
    std::thread t(say_hello); // 쓰레드 시작
    t.join();                 // 메인 쓰레드가 t를 기다림
}
  • join()이 없으면 프로그램이 끝나기 전에 쓰레드가 종료되지 않을 수 있어 런타임 오류 발생.

3. 인자 전달

void print_sum(int a, int b) {
    std::cout << a + b << std::endl;
}

int main() {
    std::thread t(print_sum, 3, 4);
    t.join();
}
  • 값은 복사되어 전달됩니다.
  • 참조를 넘기려면 std::ref() 사용
void modify(int& x) {
    x += 10;
}

int main() {
    int value = 5;
    std::thread t(modify, std::ref(value));
    t.join();
    std::cout << value << std::endl; // 15
}

4. 람다 함수 사용

std::thread t([](){
    std::cout << "Running in a lambda thread!" << std::endl;
});
t.join();

5. 멤버 함수 호출

class Worker {
public:
    void run() {
        std::cout << "Worker running!" << std::endl;
    }
};

int main() {
    Worker w;
    std::thread t(&Worker::run, &w); // 객체 포인터 전달
    t.join();
}

6. join() vs detach()

  • join(): 쓰레드가 끝날 때까지 기다림
  • detach(): 쓰레드를 백그라운드에서 실행시키고 제어를 넘김
std::thread t([]() {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Detached thread done" << std::endl;
});
t.detach(); // join하지 않아도 됨

주의: detach한 쓰레드는 더 이상 제어할 수 없으며, 메인 함수가 먼저 종료되면 문제가 생길 수 있음.


7. 쓰레드 ID 확인

 
std::cout << std::this_thread::get_id() << std::endl;

8. 쓰레드 관련 함수들

  • std::this_thread::sleep_for(duration): 일정 시간 동안 sleep
  • std::this_thread::yield(): 다른 쓰레드에게 CPU 양보
  • std::thread::hardware_concurrency(): 시스템의 논리 코어 수 반환

9. 예외 처리

std::thread 자체는 예외를 전달하지 않기 때문에, 예외는 쓰레드 내부에서 처리해야 합니다.

std::thread t([]() {
    try {
        throw std::runtime_error("error!");
    } catch (const std::exception& e) {
        std::cout << "Caught exception: " << e.what() << std::endl;
    }
});
t.join();

10. C++17에서 유용한 추가 기능

  • std::scoped_lock을 쓰레드와 뮤텍스에 함께 사용 가능
  • std::shared_mutex (읽기/쓰기 락)과 함께 사용 시 성능 최적화 가능
  • 쓰레드 안전한 std::atomic 변수와 병행 사용

 

std::shared_mutex란?

  • 동시에 여러 개의 쓰레드가 읽을 수 있도록 허용하지만,
  • 쓰기(수정) 시에는 단 하나의 쓰레드만 접근 가능하게 만드는 락입니다.

읽기: 병렬 허용
쓰기: 단독만 허용

예: std::mutex와 std::shared_mutex의 차이

일반 std::mutex 사용 시

cpp
std::mutex mtx; void read() {
std::lock_guard<std::mutex> lock(mtx); // 읽기 작업
}

읽기조차 직렬화됨 (읽기끼리도 동시에 접근 불가)


std::shared_mutex 사용 시

#include <shared_mutex>

std::shared_mutex shared_mtx;

void read() {
    std::shared_lock<std::shared_mutex> lock(shared_mtx); // 여러 쓰레드 동시에 가능
    // 읽기 작업
}

void write() {
    std::unique_lock<std::shared_mutex> lock(shared_mtx); // 단독 접근
    // 쓰기 작업
}

여러 쓰레드가 동시에 읽기 가능
쓰기 쓰레드는 단독 접근만 허용 (읽기 중인 쓰레드가 끝날 때까지 대기)

성능 최적화의 이유

쓰기보다 읽기가 많은 상황 (예: 캐시, 설정 조회 등)에서는:

  • std::mutex: 모든 작업이 직렬 → 비효율적
  • std::shared_mutex: 읽기 병렬화 가능 → 성능 향상
#include <iostream>
#include <thread>
#include <shared_mutex>
#include <vector>

std::shared_mutex shared_mtx;
int shared_data = 0;

void reader(int id) {
    std::shared_lock<std::shared_mutex> lock(shared_mtx);
    std::cout << "Reader " << id << " read value: " << shared_data << std::endl;
}

void writer(int value) {
    std::unique_lock<std::shared_mutex> lock(shared_mtx);
    shared_data = value;
    std::cout << "Writer wrote value: " << value << std::endl;
}

int main() {
    std::vector<std::thread> threads;

    // 읽기 쓰레드 여러 개
    for (int i = 0; i < 5; ++i)
        threads.emplace_back(reader, i);

    // 쓰기 쓰레드 하나
    threads.emplace_back(writer, 100);

    for (auto& t : threads) t.join();
}

'Develop > C&CPP' 카테고리의 다른 글

[cpp] optional  (0) 2025.04.25
[cpp] atomic  (0) 2025.04.20
[cpp] mutex, lock  (0) 2025.04.20
[cpp] cpp17에서 달라진 점  (0) 2025.04.20
[cpp] cpp14에서 추가된 것  (0) 2025.04.20
반응형

std::atomic 개념과 사용법 (C++17 기준)

1. std::atomic이란?

  • 동기화된 접근을 보장하는 데이터 타입 템플릿입니다.
  • 멀티스레드에서 **뮤텍스 없이도 변수 접근의 원자성(atomicity)**을 보장합니다.
  • std::atomic<int>, std::atomic<bool>, std::atomic<T> 등 다양한 타입으로 사용 가능.

2. 기본 사용법

#include <atomic>
#include <iostream>
#include <thread>

std::atomic<int> counter{0};

void increment() {
    for (int i = 0; i < 10000; ++i) {
        counter++;
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

    t1.join();
    t2.join();

    std::cout << "Final counter: " << counter << std::endl;
}
  • counter++는 원자적으로 수행됨 → 경쟁 조건(race condition) 없이 동작

3. 주요 연산

  • store(val): 값 설정
  • load(): 현재 값 반환
  • exchange(val): 기존 값을 새로운 값으로 바꾸고, 이전 값을 반환
  • compare_exchange_strong(expected, desired)
  • compare_exchange_weak(expected, desired): CAS(compare-and-swap) 연산
std::atomic<int> value{5};
int expected = 5;
bool success = value.compare_exchange_strong(expected, 10);
// 성공 시 value는 10이 되고 true 반환
 

4. 메모리 순서 (memory ordering)

  • 기본은 memory_order_seq_cst (가장 강력하고 안전한 순서)
  • 필요에 따라 memory_order_relaxed, acquire, release 등 지정 가능
counter.fetch_add(1, std::memory_order_relaxed); // 약한 메모리 순서

하지만 대부분의 경우 기본 순서를 사용하는 것으로 충분하며, 성능 최적화가 필요한 경우만 조절합니다.


5. std::atomic_flag (가장 가벼운 락)

  • 간단한 락 구현이나 spin lock 등에 사용
  • 초기화는 반드시 ATOMIC_FLAG_INIT로
std::atomic_flag flag = ATOMIC_FLAG_INIT;

void work() {
    while (flag.test_and_set(std::memory_order_acquire)) {
        // busy-wait
    }
    // critical section
    flag.clear(std::memory_order_release);
}

6. 뮤텍스 vs atomic

항목  mutex atomic
동기화 범위 임의의 코드 블록 변수 단위
오버헤드 상대적으로 큼 작음
복잡한 동작 가능 제한적
예외 안전성 RAII로 안전 직접 관리 필요

7. C++17에서의 특징

  • 템플릿 deduction을 사용할 수 없지만, std::atomic<T>의 인터페이스는 보다 풍부해졌습니다.
  • std::atomic<std::shared_ptr<T>>도 제공되어, 참조 카운팅이 원자적으로 가능해졌습니다.

'Develop > C&CPP' 카테고리의 다른 글

[cpp] optional  (0) 2025.04.25
[cpp] thread  (0) 2025.04.20
[cpp] mutex, lock  (0) 2025.04.20
[cpp] cpp17에서 달라진 점  (0) 2025.04.20
[cpp] cpp14에서 추가된 것  (0) 2025.04.20
반응형

C++17에서의 mutex, lock

1. 기본 std::mutex 사용

std::mutex는 임계 구역(critical section)을 보호하기 위한 가장 기본적인 뮤텍스입니다.

#include <iostream>
#include <mutex>
#include <thread>

std::mutex mtx;

void print_message(const std::string& msg) {
    mtx.lock();
    std::cout << msg << std::endl;
    mtx.unlock();
}

int main() {
    std::thread t1(print_message, "Hello from thread 1");
    std::thread t2(print_message, "Hello from thread 2");

    t1.join();
    t2.join();
    return 0;
}
 

주의: lock()과 unlock()은 반드시 쌍으로 호출되어야 하며, 예외 발생 시 unlock이 호출되지 않아 데드락의 원인이 될 수 있음.


2. std::lock_guard 사용

std::lock_guard는 예외 안전성을 위해 사용되는 RAII 스타일의 뮤텍스 관리 도구입니다.

void print_message(const std::string& msg) {
    std::lock_guard<std::mutex> lock(mtx);
    std::cout << msg << std::endl;
}

스코프를 벗어나면 자동으로 unlock()이 호출됨.


3. std::unique_lock 사용

std::unique_lock은 lock_guard보다 더 유연한 뮤텍스 관리가 가능합니다. 예: 지연 잠금, 조건 변수와의 조합 등

void print_message(const std::string& msg) {
    std::unique_lock<std::mutex> lock(mtx);
    std::cout << msg << std::endl;
    // lock.unlock(); // 수동 해제도 가능
}

4. std::recursive_mutex

같은 쓰레드에서 여러 번 lock이 필요한 경우 사용합니다.

std::recursive_mutex rmtx;

void recursive_function(int count) {
    if (count <= 0) return;
    rmtx.lock();
    std::cout << "Depth: " << count << std::endl;
    recursive_function(count - 1);
    rmtx.unlock();
}

5. std::timed_mutex, std::recursive_timed_mutex

특정 시간 내에 lock을 얻지 못하면 포기하도록 설계된 뮤텍스입니다.

std::timed_mutex tmtx;

void try_lock_example() {
    if (tmtx.try_lock_for(std::chrono::milliseconds(100))) {
        std::cout << "Lock acquired" << std::endl;
        tmtx.unlock();
    } else {
        std::cout << "Timeout: failed to acquire lock" << std::endl;
    }
}

6. std::lock 함수

여러 뮤텍스를 동시에 안전하게 lock할 때 사용합니다. 데드락 방지를 위한 도구입니다.

데드락 케이스

// 쓰레드 A
m1.lock();
m2.lock(); // 잠금 대기 중

// 쓰레드 B
m2.lock();
m1.lock(); // 잠금 대기 중 → 데드락 발생!

쓰레드는 제 각각 돌기 때문에, 쓰레드A, B가 m1, m2를 lock을 한 다음 lock을 하게 되는 경우가 발생하게 되면 서로가 가진 뮤텍스를 기다리다가 영원히 대기 상태에 빠지게 됩니다.

#include <iostream>
#include <mutex>
#include <thread>

std::mutex m1, m2;

void threadA() {
    std::lock(m1, m2); // 안전하게 두 뮤텍스를 모두 잠금
    std::lock_guard<std::mutex> lk1(m1, std::adopt_lock);
    std::lock_guard<std::mutex> lk2(m2, std::adopt_lock);
    std::cout << "Thread A acquired both locks\n";
}

void threadB() {
    std::lock(m2, m1); // 순서를 바꿔도 안전하게 잠금
    std::lock_guard<std::mutex> lk1(m2, std::adopt_lock);
    std::lock_guard<std::mutex> lk2(m1, std::adopt_lock);
    std::cout << "Thread B acquired both locks\n";
}

int main() {
    std::thread t1(threadA);
    std::thread t2(threadB);

    t1.join();
    t2.join();
}

여기서 std::lock은 내부적으로 양쪽 쓰레드에서 같은 방식으로 동기화를 해주기 때문에 데드락 없이 둘 다 잠금에 성공할 수 있어요.


7. 주의사항 요약

  • 뮤텍스는 가능한 한 짧은 영역에서만 잠그고 풀어야 함
  • lock을 해제하지 않고 예외가 발생하면 데드락이 발생할 수 있음 → RAII 스타일 사용 권장
  • 여러 뮤텍스를 동시에 잠글 경우 반드시 std::lock 사용
  • 가능한 경우 std::scoped_lock (C++17부터 도입)을 사용하면 더 안전하게 여러 뮤텍스를 다룰 수 있음
std::mutex m1, m2;

void example() {
    std::scoped_lock lock(m1, m2);  // C++17: 데드락 방지 + RAII
    // 임계 구역
}
 

 

'Develop > C&CPP' 카테고리의 다른 글

[cpp] thread  (0) 2025.04.20
[cpp] atomic  (0) 2025.04.20
[cpp] cpp17에서 달라진 점  (0) 2025.04.20
[cpp] cpp14에서 추가된 것  (0) 2025.04.20
[service] 윈도우 서비스 프로그램  (0) 2025.01.05
반응형

C++17에서 달라진 점 (C++14와 비교)

C++17은 C++14에 비해 여러 가지 새로운 기능과 개선점을 도입하여, 개발자 생산성, 코드 가독성, 그리고 언어의 표현력을 크게 향상시켰습니다. 아래 표와 목록에서 주요 변경 사항을 정리합니다.

주요 변경점 비교

C++17
기능 c++14 c++17
Inline 변수 미지원 지원 (헤더 파일에서 변수 정의/초기화 가능)
구조적 바인딩 (Structured Bindings) 미지원 지원 (튜플, 구조체 분해 가능)
constexpr if 미지원 지원 (컴파일 타임 조건 분기)
Fold Expressions 미지원 지원 (가변 인자 템플릿 연산 간결화)
파일 시스템 라이브러리 미지원 지원 (std::filesystem)
병렬/동시 STL 알고리즘 미지원 지원 (병렬 실행 정책)
if/switch 초기화자 미지원 지원 (if/switch문에서 변수 선언 및 초기화)
중첩 네임스페이스 미지원 지원 (namespace a::b::c와 같이 간결하게 표현)
옵셔널 타입 미지원 지원 (std::optional)
클래스 템플릿 인자 추론(CTAD) 미지원 지원 (템플릿 인자 자동 추론)
인라인 변수 미지원 지원 (ODR 위반 없이 헤더에 변수 정의 가능)
 

C++17에서 추가된 주요 기능 및 개선 사항

  • Inline 변수: 헤더 파일에서 변수 정의/초기화 가능, ODR(One Definition Rule) 위반 방지
  • 구조적 바인딩 (Structured Bindings): 튜플, 구조체 등 복합 객체를 개별 변수로 분해할 수 있음
  •  
    cpp
    auto [x, y] = std::make_pair(1, 2);
  • constexpr if: 템플릿 코드에서 컴파일 타임 조건 분기 가능
  •  
    cpp
    if constexpr (조건) { /* ... */ }
  • Fold Expressions: 가변 인자 템플릿을 간결하게 연산할 수 있는 문법 도입
  •  
    cpp
    template<typename... Args> auto sum(Args... args) { return (args + ...); }
  • 파일 시스템 라이브러리(std::filesystem): 파일 및 디렉토리 작업을 표준화된 방식으로 지원
  • 병렬/동시 STL 알고리즘: 표준 라이브러리 알고리즘에 병렬 실행 정책 지원
  • if/switch 초기화자: if, switch문에서 변수 선언 및 초기화 가능
  •  
    cpp
    if (int x = func(); x > 0) { /* ... */ }
  • 중첩 네임스페이스: 여러 네임스페이스를 한 줄로 선언 가능
  •  
    cpp
    namespace a::b::c { /* ... */ }
  • std::optional: 값이 있을 수도 없을 수도 있는 상황을 명확하게 표현
  • 클래스 템플릿 인자 추론(CTAD): 객체 생성 시 템플릿 인자를 명시하지 않아도 자동 추론
  •  
    cpp
    std::pair p(1, 2); // std::pair<int, int>로 추론
  • 기타: std::variant, std::any, std::string_view 등 유틸리티 타입 추가, lambdas 개선, 기타 표준 라이브러리 확장

요약

  • C++14는 C++11의 기능을 다듬고 소폭 확장한 "마이너 업그레이드"였다면,
    C++17은 새로운 문법, 표준 라이브러리 확장, 컴파일 타임 프로그래밍 강화 등
    실질적인 "메이저 업그레이드"에 가까운 변화가 많습니다
  • 대부분의 기능은 기존 코드와의 호환성을 유지하면서,
    새로운 코드 작성 시 코드의 간결성, 안전성, 효율성을 크게 높여줍니다.

C++17로의 전환은 최신 컴파일러(GCC, Clang, MSVC 등)에서 모두 지원되며,
현대적인 C++ 코드를 작성하고자 한다면 적극적으로 권장됩니다

'Develop > C&CPP' 카테고리의 다른 글

[cpp] atomic  (0) 2025.04.20
[cpp] mutex, lock  (0) 2025.04.20
[cpp] cpp14에서 추가된 것  (0) 2025.04.20
[service] 윈도우 서비스 프로그램  (0) 2025.01.05
[server] ThreadPool, client 접속이 많을 때. TCP Server  (0) 2025.01.05
반응형

C++14에서 달라진 점 (C++11과 비교)

C++14는 C++11의 기능을 다듬고 확장한 표준으로, 대규모 기능 추가보다는 기존 기능의 개선과 편의성 향상에 초점을 맞췄습니다. 아래는 C++11 대비 C++14에서 달라진 주요 내용을 정리한 것입니다.

1. 람다 함수의 확장

  • 제네릭 람다(Generic Lambdas)
    • 람다 함수의 매개변수 타입에 auto 사용 가능
    •  
      cpp
      auto lambda = [](auto x, auto y) { return x + y; };
    • 템플릿 함수처럼 동작하는 람다 작성 가능

2. constexpr 기능 강화

  • C++11의 constexpr 함수는 단순한 리턴만 가능했으나,
    C++14에서는 if문, 반복문, 지역 변수 선언 등 복잡한 로직을 지원
  • 더 많은 함수와 상황에서 constexpr 사용 가능.

3. 함수 반환 타입 추론

  • Return Type Deduction
    • 함수의 반환 타입을 auto로 지정하면, 구현부의 return문을 기반으로 타입이 자동 추론됨
    •  
      cpp
      auto func() { return 42; }

4. 표준 라이브러리 및 기타 개선

  • Reader-Writer Locks
    • 다중 스레드 환경에서 읽기/쓰기 락 지원(std::shared_timed_mutex 등)
  • std::exchange
    • 객체의 값을 새로운 값으로 교체하고, 이전 값을 반환하는 함수 템플릿(라이브러리 확장).
  • 이진 리터럴 지원
    • 0b 또는 0B 접두사로 이진수 리터럴 사용 가능
    •  
      cpp
      int n = 0b101010;
  • [[deprecated]] 속성
    • 함수, 변수 등에 deprecated 속성 부여 가능

5. 기타 언어 및 코드 스타일 관련 변화

  • 변수 템플릿(Variable Templates)
    • 변수도 템플릿으로 선언 가능
  • decltype(auto)
    • auto와 decltype의 타입 추론 결합
  • 람다 캡처 초기화
    • 람다 캡처 시 임의의 표현식으로 멤버 초기화 가능
  • 이종 탐색(Heterogeneous Lookup)
    • 연관 컨테이너에서 키 타입과 다른 타입으로 검색 가능

요약 표

C++14 개선점 및 추가 기능
구분 C++11 C++14
람다 일반 람다 제네릭 람다(매개변수에 auto 사용)
constexpr 단순 리턴만 가능 if/반복문/지역변수 등 복잡한 로직 지원
함수 반환 타입 명시적 타입, 일부 auto 지원 모든 함수에 auto 반환 타입 추론
표준 라이브러리 기존 컨테이너, 락 Reader-Writer 락, std::exchange 등 추가
리터럴 10진/16진 등 이진 리터럴(0b) 지원
속성 없음 [[deprecated]] 속성 추가
타입/템플릿 함수/클래스 템플릿 변수 템플릿, decltype(auto)
람다 캡처 변수 캡처만 가능 표현식 기반 캡처(초기화) 지원
 

C++14는 C++11의 기반 위에서 실질적인 코드 작성의 편의성과 성능, 타입 추론의 유연성을 크게 높였습니다

반응형

 

Privacy Policy

This Privacy Policy describes how the Chrome extension "Encrypted Memo" handles your information.
Data Collection and Storage
  • We do not collect, store, or transmit any personal information
  • All memo data is encrypted and stored locally on your device using Chrome's storage API
  • No data is sent to external servers or third parties
  • All data remains within your browser and under your control

Data Security

  • All memo content is encrypted before being stored locally
  • The encryption/decryption process happens entirely within your browser
  • We have no access to your stored memo content

Third-Party Services

  • This extension does not integrate with any third-party services
  • We do not use analytics or tracking tools

Changes to This Policy

We may update this Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on the Chrome Web Store.

 

Contact
If you have any questions about this Privacy Policy, please contact us at [crazyj7@gmail.com].

 

https://chromewebstore.google.com/detail/%EC%95%94%ED%98%B8%ED%99%94-%EB%A9%94%EB%AA%A8-%EA%B4%80%EB%A6%AC/hnlkeechodbhgflbjjbjdfkdjbkidckf

반응형

 

일본어번역 도우미 Chrome Web Extension

 

 

Privacy Policy

This Chrome extension does not collect, store, or share any personal data.
Users can use this extension without providing any personal information.

This extension only translates selected text on web pages and does not store any data on external servers.

If you have any questions, please contact us.

 

크롬 웹앱.  일본어학습 번역 도우미. 설치 링크. 

https://chromewebstore.google.com/detail/%EC%9D%BC%EB%B3%B8%EC%96%B4%ED%95%99%EC%8A%B5-%EB%B2%88%EC%97%AD%EB%8F%84%EC%9A%B0%EB%AF%B8/dddmbbfeoihflcjodecgilbcdfodbfjg

 

일본어학습 번역도우미 - Chrome 웹 스토어

마우스로 선택한 일본어 텍스트를 한국어로 번역해주는 확장 프로그램. 구글번역, 파파고번역, 네이버사전 연결

chromewebstore.google.com

 

반응형

MyService 로그램 용법

1. 비스 관 령어

 

  • 주요 기
  • 자동 시작 비스로 설치
  • 5초마다 상태 로
  • Ctrl+C로 버그 모드 종료 가능
     
  • 디버그 모드
  • 개발/테스트   -d  옵션으로 콘솔에서 직접 실행 가능
  • 로그가 콘솔에 출력
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable: 4819)
#pragma warning(disable: 4100)

#include <windows.h>
#include <tchar.h>
#include <winsvc.h>
#include <iostream>
#include <fstream>
#include <chrono>
#include <ctime>
#include <string>
#include <filesystem>

// 상수 정의
#define SERVICE_NAME _T("MyService")
#define SERVICE_DISPLAY_NAME _T("My Service")
#define SERVICE_DESC _T("My Service Description")
#define LOG_FILE "c:\\mysvc.log"

// 전역 변수
static bool g_debug_mode = false;
static std::ofstream g_log_file;
static SERVICE_STATUS_HANDLE g_ssh = NULL;
static HANDLE g_stop_event = NULL;

// 유니코드 매크로
#ifdef UNICODE
#define tprintf wprintf
#define tstring std::wstring
#else
#define tprintf printf
#define tstring std::string
#endif

// 함수 선언
void MySetStatus(DWORD dstatus, DWORD daccept);
void ServiceHandler(DWORD dwcontrol);
void ServiceHandlerEx(DWORD dwcontrol, DWORD dweventtype, LPVOID lpeventdata, LPVOID lpcontext);
void ServiceMain();
VOID WINAPI ServiceMainProc(DWORD argc, LPTSTR* argv);
bool InstallService();
bool UninstallService();
BOOL MyStartService(tstring Name);
bool StopService();
BOOL WINAPI DebugConsoleHandler(DWORD signal);
bool KillService();

// 로깅 함수
void LogMessage(const std::string& message) {
    auto now = std::chrono::system_clock::now();
    auto time = std::chrono::system_clock::to_time_t(now);
    std::string timestamp = std::ctime(&time);
    timestamp.pop_back();

    std::string formatted_msg = "[" + timestamp + "] " + message + "\n";

    if (g_debug_mode) {
        std::cout << formatted_msg;
    }
    
    if (g_log_file.is_open()) {
        g_log_file << formatted_msg;
        g_log_file.flush();
    }
}

// 서비스 상태 관리 함수
void MySetStatus(DWORD dstatus, DWORD daccept) {
    SERVICE_STATUS ss = { 0, };
    if (!g_ssh) {
        LogMessage("MySetStatus: ERR status handle is null!");
        return;
    }
    ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    ss.dwCurrentState = dstatus;
    ss.dwControlsAccepted = daccept;
    ss.dwWin32ExitCode = 10 * 1000;
    SetServiceStatus(g_ssh, &ss);
}

// 서비스 이벤트 핸들러
void ServiceHandler(DWORD dwcontrol) {
    LogMessage("ServiceHandler: control=" + std::to_string(dwcontrol));
    switch (dwcontrol) {
    case SERVICE_CONTROL_STOP:
        LogMessage("SERVICE_CONTROL_STOP received");
        MySetStatus(SERVICE_STOP_PENDING, SERVICE_ACCEPT_STOP);
        SetEvent(g_stop_event);
        break;
    default:
        MySetStatus(dwcontrol, SERVICE_ACCEPT_STOP);
        break;
    }
}

void ServiceHandlerEx(DWORD dwcontrol, DWORD dweventtype, LPVOID lpeventdata, LPVOID lpcontext) {
    LogMessage("ServiceHandlerEx: control=" + std::to_string(dwcontrol));
    switch (dwcontrol) {
    case SERVICE_CONTROL_SESSIONCHANGE:
        LogMessage("ServiceHandlerEx: session change");
        break;
    default:
        ServiceHandler(dwcontrol);
        break;
    }
}

// 서비스 메인 함수
void ServiceMain() {
    while (WaitForSingleObject(g_stop_event, 500) == WAIT_TIMEOUT) {
        LogMessage("Service is running...");
    }

    LogMessage("ServiceMain: End");
    if (!g_debug_mode) {
        MySetStatus(SERVICE_STOPPED, 0);
    }

    if (g_debug_mode) {
        CloseHandle(g_stop_event);
    }
}

VOID WINAPI ServiceMainProc(DWORD argc, LPTSTR* argv) {
    g_log_file.open(LOG_FILE, std::ios::app);
    LogMessage("Service started");

    g_ssh = RegisterServiceCtrlHandlerEx(SERVICE_NAME, (LPHANDLER_FUNCTION_EX)ServiceHandlerEx, NULL);
    LogMessage("ServiceMainProc: g_ssh=" + std::to_string((uint64_t)g_ssh));
    if (!g_ssh) {
        LogMessage("Failed to register service control handler");
        g_log_file.close();
        return;
    }

    MySetStatus(SERVICE_START_PENDING, 0);

    if (!g_stop_event) {
        g_stop_event = CreateEvent(NULL, TRUE, FALSE, NULL);
        if (!g_stop_event) {
            MySetStatus(SERVICE_STOPPED, 0);
            g_log_file.close();
            return;
        }
    }

    MySetStatus(SERVICE_RUNNING, SERVICE_ACCEPT_STOP);
    ServiceMain();
    g_log_file.close();
}

// 서비스 관리 함수들
bool InstallService() {
    SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
    if (!scm) return false;

    TCHAR path[MAX_PATH];
    GetModuleFileName(NULL, path, MAX_PATH);
    _tcscat_s(path, _T(" -svc"));

    SC_HANDLE service = CreateService(
        scm, SERVICE_NAME, SERVICE_DISPLAY_NAME,
        SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
        SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path,
        NULL, NULL, NULL, NULL, NULL);

    if (service) {
        SERVICE_DESCRIPTION sd = {const_cast<LPTSTR>(SERVICE_DESC)};
        ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &sd);
        CloseServiceHandle(service);
    }
    CloseServiceHandle(scm);
    return service != NULL;
}

bool UninstallService() {
    SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!scm) return false;

    SC_HANDLE service = OpenService(scm, SERVICE_NAME, SERVICE_ALL_ACCESS);
    if (!service) {
        CloseServiceHandle(scm);
        return false;
    }

    SERVICE_STATUS status;
    ControlService(service, SERVICE_CONTROL_STOP, &status);
    bool result = DeleteService(service);

    CloseServiceHandle(service);
    CloseServiceHandle(scm);
    return result;
}

BOOL MyStartService(tstring Name) {
    SC_HANDLE schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!schSCManager) {
        DWORD err = GetLastError();
        LogMessage("OpenSCManager failed. Error: " + std::to_string(err));
        return FALSE;
    }

    SC_HANDLE schService = OpenService(schSCManager, Name.c_str(), SERVICE_START | SERVICE_QUERY_STATUS);
    if (!schService) {
        DWORD err = GetLastError();
        LogMessage("OpenService failed. Error: " + std::to_string(err));
        CloseServiceHandle(schSCManager);
        return FALSE;
    }

    // 서비스 상태 확인
    SERVICE_STATUS status;
    if (QueryServiceStatus(schService, &status)) {
        if (status.dwCurrentState == SERVICE_RUNNING) {
            LogMessage("Service is already running");
            CloseServiceHandle(schService);
            CloseServiceHandle(schSCManager);
            return TRUE;
        }
    }

    // 서비스 시작
    if (!StartService(schService, 0, NULL)) {
        DWORD err = GetLastError();
        if (err == ERROR_SERVICE_ALREADY_RUNNING) {
            LogMessage("Service is already running");
            CloseServiceHandle(schService);
            CloseServiceHandle(schSCManager);
            return TRUE;
        }
        LogMessage("StartService failed. Error: " + std::to_string(err));
        CloseServiceHandle(schService);
        CloseServiceHandle(schSCManager);
        return FALSE;
    }

    // 서비스가 실제로 시작될 때까지 대기
    int retries = 0;
    while (retries < 10) {  // 최대 10초 대기
        if (QueryServiceStatus(schService, &status)) {
            if (status.dwCurrentState == SERVICE_RUNNING) {
                LogMessage("Service started successfully");
                CloseServiceHandle(schService);
                CloseServiceHandle(schSCManager);
                return TRUE;
            }
            if (status.dwCurrentState == SERVICE_STOPPED) {
                LogMessage("Service failed to start");
                break;
            }
        }
        Sleep(1000);
        retries++;
    }

    CloseServiceHandle(schService);
    CloseServiceHandle(schSCManager);
    return FALSE;
}

bool StopService() {
    SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!scm) return false;

    SC_HANDLE service = OpenService(scm, SERVICE_NAME, SERVICE_STOP | SERVICE_QUERY_STATUS);
    if (!service) {
        CloseServiceHandle(scm);
        return false;
    }

    SERVICE_STATUS status;
    bool result = ControlService(service, SERVICE_CONTROL_STOP, &status);
    
    // 서비스가 완전히 중지될 때까지 대기
    if (result) {
        while (QueryServiceStatus(service, &status)) {
            if (status.dwCurrentState == SERVICE_STOPPED) {
                break;
            }
            Sleep(1000);
        }
    }

    CloseServiceHandle(service);
    CloseServiceHandle(scm);
    return result;
}

// 디버그 모드 핸들러
BOOL WINAPI DebugConsoleHandler(DWORD signal) {
    if (signal == CTRL_C_EVENT || signal == CTRL_BREAK_EVENT) {
        LogMessage("Debug mode stop requested");
        if (g_stop_event) {
            SetEvent(g_stop_event);
        }
        return TRUE;
    }
    return FALSE;
}

// 서비스 강제 종료 함수 구현
bool KillService() {
    SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!scm) {
        LogMessage("OpenSCManager failed in KillService");
        return false;
    }

    SC_HANDLE service = OpenService(scm, SERVICE_NAME, SERVICE_ALL_ACCESS);
    if (!service) {
        LogMessage("OpenService failed in KillService");
        CloseServiceHandle(scm);
        return false;
    }

    // 현재 서비스 상태 확인
    SERVICE_STATUS status = { 0 };
    if (!QueryServiceStatus(service, &status)) {
        LogMessage("QueryServiceStatus failed in KillService");
        CloseServiceHandle(service);
        CloseServiceHandle(scm);
        return false;
    }

    // 서비스가 이미 중지 상태면 종료
    if (status.dwCurrentState == SERVICE_STOPPED) {
        LogMessage("Service is already stopped");
        CloseServiceHandle(service);
        CloseServiceHandle(scm);
        return true;
    }

    // 서비스 강제 종료 후 상태를 STOPPED로 변경
    LogMessage("Forcing service to stop state");
    memset(&status, 0, sizeof(SERVICE_STATUS));
    status.dwCurrentState = SERVICE_STOPPED;
    ControlService(service, SERVICE_CONTROL_STOP, &status);

    CloseServiceHandle(service);
    CloseServiceHandle(scm);
    return true;
}

// 메인 함수
int main(int argc, char* argv[]) {
    if (argc > 1) {
        if (strcmp(argv[1], "-i") == 0) {
            return InstallService() ? 0 : 1;
        }
        else if (strcmp(argv[1], "-r") == 0) {
            return MyStartService(SERVICE_NAME) ? 0 : 1;
        }
        else if (strcmp(argv[1], "-s") == 0) {
            return StopService() ? 0 : 1;
        }
        else if (strcmp(argv[1], "-u") == 0) {
            return UninstallService() ? 0 : 1;
        }
        else if (strcmp(argv[1], "-d") == 0) {
            g_debug_mode = true;
            SetConsoleCtrlHandler(DebugConsoleHandler, TRUE);
            g_log_file.open(LOG_FILE, std::ios::app);
            LogMessage("Debug mode started");

            g_stop_event = CreateEvent(NULL, TRUE, FALSE, NULL);
            if (!g_stop_event) {
                g_log_file.close();
                return 1;
            }
            ServiceMain();
            g_log_file.close();
            return 0;
        }
        else if (strcmp(argv[1], "-svc") == 0) {
            SERVICE_TABLE_ENTRY serviceTable[] = {
                {const_cast<LPTSTR>(SERVICE_NAME), ServiceMainProc},
                {NULL, NULL}
            };
            return StartServiceCtrlDispatcher(serviceTable) ? 0 : 1;
        }
        else if (strcmp(argv[1], "-k") == 0) {
            return KillService() ? 0 : 1;
        }
    }
    return 0;
}

 

 

 

 

 

반응형

- 클라이언트 접속이 많은 경우, 부하 조정.  쓰레드 풀을 만들어 성능 관리. 동시 실행 쓰레드 수를 제한하여 시스템 과부하 방지.

- 쓰레드 생성 오버헤드 제거. 동시 접속자수 제한으로 서버 안정성 확보. 쓰레드 풀 크기 조정을 하여 성능 조정.

- ^C로 종료.

#include <iostream>
#include <thread>
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <windows.h>
#include <string>
#include <sstream>
#include <atomic>
#include <csignal>
#include <chrono>

#pragma comment(lib, "ws2_32.lib")

#define SERVER_PORT 30000
#define BUFFER_SIZE 1024

#define MAX_THREADS 5           // 최대 스레드 수; CPU 코어 수 만큼 설정
#define MAX_QUEUE_SIZE 500     // 최대 큐 크기. 대기 클라이언트 수 만큼 설정.
#define MAX_CONNECTIONS (MAX_QUEUE_SIZE+MAX_THREADS+10)   // 최대 연결 수

#define SOCKET_TIMEOUT 3000    // 소켓 connect 타임아웃 (밀리초)
#define IDLE_TIMEOUT 10000  // 클라이언트 유휴 타임아웃 (밀리초)


// 디버그 및 에러 로그 매크로 정의
static bool g_debug_mode = false;
static bool g_error_mode = false;
static bool g_info_mode = true;

#define DEBUG_LOG(msg) \
    if (g_debug_mode) { \
        std::stringstream ss; \
        ss << "[DEBUG] " << msg; \
        std::cout << ss.str() << std::endl; \
    }

#define ERROR_LOG(msg) \
    if (g_error_mode) { \
        std::stringstream ss; \
        ss << "[ERROR] " << msg; \
        std::cerr << ss.str() << std::endl; \
    }

#define INFO_LOG(msg) \
    if (g_info_mode) { \
        std::stringstream ss; \
        ss << "[INFO] " << msg; \
        std::cout << ss.str() << std::endl; \
    }

// 전역 변수
static std::atomic<size_t> g_total_requests{0};
static size_t g_last_printed_count{0};
static std::chrono::steady_clock::time_point g_last_print_time;
static SOCKET g_serverSocket = INVALID_SOCKET;

class ThreadPool {
public:
    std::atomic<size_t> current_connections{0};  // 현재 총 연결 수
    std::atomic<size_t> active_threads{0};       // 현재 작업 중인 스레드 수

    size_t GetQueueSize() {
        std::lock_guard<std::mutex> lock(queue_mutex);
        return tasks.size();  // 대기 큐에 있는 클라이언트 수
    }

private:
    std::vector<std::thread> workers;
    std::queue<std::pair<SOCKET, sockaddr_in>> tasks;
    std::mutex queue_mutex;
    std::condition_variable condition;
    std::atomic<bool> stop{false};

    void HandleClient(SOCKET clientSocket, sockaddr_in clientAddr) {
        active_threads++;  // 작업 시작
        try {
            // 소켓 타임아웃 설정
            struct timeval timeout;
            timeout.tv_sec = IDLE_TIMEOUT / 1000;
            timeout.tv_usec = (IDLE_TIMEOUT % 1000) * 1000;
            
            if (setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0) {
                ERROR_LOG("Failed to set receive timeout");
            }
            if (setsockopt(clientSocket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout)) < 0) {
                ERROR_LOG("Failed to set send timeout");
            }

            char buffer[BUFFER_SIZE];
            char clientIP[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, INET_ADDRSTRLEN);
            
            DEBUG_LOG("Client connected - IP: " << clientIP 
                      << ", Port: " << ntohs(clientAddr.sin_port));

            int bytesReceived = recv(clientSocket, buffer, BUFFER_SIZE, 0);
            if (bytesReceived == SOCKET_ERROR) {
                if (WSAGetLastError() == WSAETIMEDOUT) {
                    ERROR_LOG("Client " << clientIP << " timed out");
                } else {
                    ERROR_LOG("Receive error from " << clientIP);
                }
            } else if (bytesReceived > 0) {
                buffer[bytesReceived] = '\0';
                DEBUG_LOG("Received from " << clientIP << ": " << buffer);

                // 요청 처리 카운터 증가
                g_total_requests.fetch_add(1, std::memory_order_relaxed);

                std::string response;
                if (strcmp(buffer, "sysinfo") == 0) {
                    SYSTEM_INFO sysInfo;
                    GetSystemInfo(&sysInfo);
                    response = "Processor count: " + std::to_string(sysInfo.dwNumberOfProcessors);
                }
                else if (strcmp(buffer, "netinfo") == 0) {
                    char hostname[256];
                    gethostname(hostname, sizeof(hostname));
                    response = "Hostname: " + std::string(hostname);
                }
                else if (strcmp(buffer, "diskinfo") == 0) {
                    DWORD sectorsPerCluster, bytesPerSector, freeClusters, totalClusters;
                    GetDiskFreeSpace(NULL, &sectorsPerCluster, &bytesPerSector, 
                                    &freeClusters, &totalClusters);
                    response = "Disk total space: " + 
                              std::to_string((double)totalClusters * sectorsPerCluster * 
                                           bytesPerSector / (1024 * 1024 * 1024)) + " GB";
                }
                else {
                    response = "Unknown command";
                }

                send(clientSocket, response.c_str(), response.length(), 0);
            }

            DEBUG_LOG("Client disconnected - IP: " << clientIP);
        } catch (const std::exception& e) {
            ERROR_LOG("Exception in HandleClient: " << e.what());
        }
        
        closesocket(clientSocket);
        current_connections--;
        active_threads--;  // 작업 완료
    }

public:
    ~ThreadPool() {
        Shutdown();
    }

    void Shutdown() {
        DEBUG_LOG("ThreadPool shutdown initiated");
        
        stop = true;
        condition.notify_all();

        // 스레드 종료 대기 시작 시간 기록
        auto start = std::chrono::steady_clock::now();
        bool timeout = false;

        // worker 스레드들의 종료를 기다림
        for (std::thread& worker : workers) {
            if (worker.joinable()) {
                try {
                    // 현재 경과 시간 체크
                    auto now = std::chrono::steady_clock::now();
                    auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - start).count();
                    if (elapsed >= 3) {  // 3초 초과
                        timeout = true;
                        break;
                    }

                    // 남은 시간만큼만 대기
                    worker.join();
                } catch (const std::exception& e) {
                    ERROR_LOG("Thread join error: " << e.what());
                }
            }
        }

        if (timeout) {
            ERROR_LOG("Thread shutdown timeout - forcing termination");
            // 남은 스레드들을 강제 분리
            for (std::thread& worker : workers) {
                if (worker.joinable()) {
                    worker.detach();
                }
            }
        }
        // 남은 작업들의 소켓을 정리
        std::unique_lock<std::mutex> lock(queue_mutex);
        while (!tasks.empty()) {
            auto& task = tasks.front();
            closesocket(task.first);
            tasks.pop();
        }
        current_connections = 0;
        DEBUG_LOG("ThreadPool shutdown completed");
    }

    // worker 스레드 로직 수정
    ThreadPool() {
        for (int i = 0; i < MAX_THREADS; ++i) {
            workers.emplace_back([this] {
                while (true) {
                    std::pair<SOCKET, sockaddr_in> task;
                    {
                        std::unique_lock<std::mutex> lock(queue_mutex);
                        condition.wait(lock, [this] { 
                            return stop || !tasks.empty(); 
                        });
                        
                        if (stop && tasks.empty()) {
                            DEBUG_LOG("Worker thread exiting");
                            return;
                        }
                        
                        if (!tasks.empty()) {
                            task = std::move(tasks.front());
                            tasks.pop();
                        }
                    }
                    
                    if (task.first != INVALID_SOCKET) {
                        HandleClient(task.first, task.second);
                    }
                }
            });
        }
    }

    bool EnqueueClient(SOCKET clientSocket, sockaddr_in clientAddr) {
        if (current_connections >= MAX_CONNECTIONS) {
            ERROR_LOG("Maximum connections reached");
            closesocket(clientSocket);
            return false;
        }
        current_connections++;

        std::unique_lock<std::mutex> lock(queue_mutex);
        
        if (tasks.size() >= MAX_QUEUE_SIZE) {
            char clientIP[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, INET_ADDRSTRLEN);
            ERROR_LOG("Queue is full. Connection rejected from " << clientIP 
                     << ":" << ntohs(clientAddr.sin_port));
            closesocket(clientSocket);
            current_connections--;  // 연결 거부 시 카운터 감소
            return false;
        }

        tasks.push({clientSocket, clientAddr});
        lock.unlock();
        condition.notify_one();
        return true;
    }
};


// ThreadPool 클래스 전방 선언
class ThreadPool;
static ThreadPool* g_threadPool = nullptr;
static std::atomic<bool> g_running{true};

// 모니터링 함수
void MonitorRequests() {
    while (true) {
        std::this_thread::sleep_for(std::chrono::seconds(5));
        
        if (g_threadPool != nullptr) {
            size_t current_count = g_total_requests.load();
            size_t current_conns = g_threadPool->current_connections.load();
            size_t active_threads = g_threadPool->active_threads.load();
            size_t waiting_clients = g_threadPool->GetQueueSize();

            INFO_LOG("Server Status:"
                    "\n    Total requests processed: " << current_count <<
                    "\n    Current connections: " << current_conns <<
                    "\n    Active threads: " << active_threads << "/" << MAX_THREADS <<
                    "\n    Waiting clients: " << waiting_clients);
        }
    }
}



void SignalHandler(int signal) {
    static std::atomic<bool> shutting_down{false};
    if (shutting_down.exchange(true)) {
        return;
    }
    INFO_LOG("Signal " << signal << " received. Shutting down...");
    g_running = false;
    try {
        if (g_threadPool) {
            g_threadPool->Shutdown();
            // delete g_threadPool;
            g_threadPool = nullptr;
        }
    } catch (const std::exception& e) {
        ERROR_LOG("Exception in SignalHandler: " << e.what());
    }

    if (g_serverSocket != INVALID_SOCKET) {
        closesocket(g_serverSocket);
        g_serverSocket = INVALID_SOCKET;
    }

    WSACleanup();
    std::exit(0);
}

void GracefulShutdown() {
    INFO_LOG("Initiating graceful shutdown");
    g_running = false;
    
    // 새로운 연결 거부
    if (g_serverSocket != INVALID_SOCKET) {
        shutdown(g_serverSocket, SD_BOTH);
        closesocket(g_serverSocket);
    }
}

int main() {
    signal(SIGINT, SignalHandler);
    signal(SIGTERM, SignalHandler);
    // 디버그 모드 설정 (환경변수나 커맨드 라인 인자로도 설정 가능)
    g_debug_mode = false;  // 또는 true

    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        ERROR_LOG("WSAStartup failed");
        return 1;
    }

    SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);
    g_serverSocket = serverSocket;  // 전역 변수에 저장

    int reuseAddr = 1; // 소켓 재사용 허용
    setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, 
               (const char*)&reuseAddr, sizeof(reuseAddr));

    struct linger lin;
    lin.l_onoff = 1;
    lin.l_linger = 0;  // 즉시 종료, TIME_WAIT 상태 방지
    setsockopt(serverSocket, SOL_SOCKET, SO_LINGER, 
               (const char*)&lin, sizeof(lin));

    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(SERVER_PORT);
    serverAddr.sin_addr.s_addr = INADDR_ANY;

    if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        ERROR_LOG("Bind failed");
        closesocket(serverSocket);
        WSACleanup();
        return 1;
    }

    if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) {
        ERROR_LOG("Listen failed");
        closesocket(serverSocket);
        WSACleanup();
        return 1;
    }

    // 타임아웃 설정
    struct timeval timeout;
    timeout.tv_sec = SOCKET_TIMEOUT / 1000;  // 초 단위
    timeout.tv_usec = (SOCKET_TIMEOUT % 1000) * 1000;  // 마이크로초 단위
    setsockopt(serverSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
    setsockopt(serverSocket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));

    // 모니터링 스레드 시작
    g_last_print_time = std::chrono::steady_clock::now();
    std::thread monitor_thread(MonitorRequests);
    monitor_thread.detach();  // 메인 스레드와 분리

    INFO_LOG("Server started on port " << SERVER_PORT);

    ThreadPool pool;
    g_threadPool = &pool;  // 전역 변수에 저장

    while (g_running) {
        sockaddr_in clientAddr;
        int clientAddrSize = sizeof(clientAddr);
        SOCKET clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientAddrSize);
        
        if (clientSocket == INVALID_SOCKET) {
            if (!g_running) break;  // 정상적인 종료 상황
            ERROR_LOG("Accept failed");
            continue;
        }

        if (!pool.EnqueueClient(clientSocket, clientAddr)) {
            char clientIP[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, INET_ADDRSTRLEN);
            ERROR_LOG("Connection rejected from " << clientIP 
                     << ":" << ntohs(clientAddr.sin_port));
        }
    }
    INFO_LOG("Server end.");
    // 정상 종료 처리
    closesocket(serverSocket);
    WSACleanup();
    return 0;
}

 

 

+ Recent posts