'Develop > C&CPP' 카테고리의 다른 글
[cpp] optional (0) | 2025.04.25 |
---|---|
[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] optional (0) | 2025.04.25 |
---|---|
[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 |
C++에서는 전통적으로 "값이 없음을 나타내는" 방법이 마땅치 않았어요.
이런 문제를 해결하기 위해 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;
}
[cpp] netstat TCP info (3) | 2025.07.29 |
---|---|
[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 |
#include <iostream>
#include <thread>
void say_hello() {
std::cout << "Hello from thread!" << std::endl;
}
int main() {
std::thread t(say_hello); // 쓰레드 시작
t.join(); // 메인 쓰레드가 t를 기다림
}
void print_sum(int a, int b) {
std::cout << a + b << std::endl;
}
int main() {
std::thread t(print_sum, 3, 4);
t.join();
}
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
}
std::thread t([](){
std::cout << "Running in a lambda thread!" << std::endl;
});
t.join();
class Worker {
public:
void run() {
std::cout << "Worker running!" << std::endl;
}
};
int main() {
Worker w;
std::thread t(&Worker::run, &w); // 객체 포인터 전달
t.join();
}
std::thread t([]() {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Detached thread done" << std::endl;
});
t.detach(); // join하지 않아도 됨
주의: detach한 쓰레드는 더 이상 제어할 수 없으며, 메인 함수가 먼저 종료되면 문제가 생길 수 있음.
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();
읽기: 병렬 허용
쓰기: 단독만 허용예: std::mutex와 std::shared_mutex의 차이
일반 std::mutex 사용 시
cppstd::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); // 단독 접근 // 쓰기 작업 }
→ 여러 쓰레드가 동시에 읽기 가능
→ 쓰기 쓰레드는 단독 접근만 허용 (읽기 중인 쓰레드가 끝날 때까지 대기)
#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();
}
[cpp] netstat TCP info (3) | 2025.07.29 |
---|---|
[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 |
#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;
}
std::atomic<int> value{5};
int expected = 5;
bool success = value.compare_exchange_strong(expected, 10);
// 성공 시 value는 10이 되고 true 반환
counter.fetch_add(1, std::memory_order_relaxed); // 약한 메모리 순서
하지만 대부분의 경우 기본 순서를 사용하는 것으로 충분하며, 성능 최적화가 필요한 경우만 조절합니다.
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);
}
항목 | mutex | atomic |
동기화 범위 | 임의의 코드 블록 | 변수 단위 |
오버헤드 | 상대적으로 큼 | 작음 |
복잡한 동작 | 가능 | 제한적 |
예외 안전성 | RAII로 안전 | 직접 관리 필요 |
[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 |
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이 호출되지 않아 데드락의 원인이 될 수 있음.
std::lock_guard는 예외 안전성을 위해 사용되는 RAII 스타일의 뮤텍스 관리 도구입니다.
void print_message(const std::string& msg) {
std::lock_guard<std::mutex> lock(mtx);
std::cout << msg << std::endl;
}
스코프를 벗어나면 자동으로 unlock()이 호출됨.
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(); // 수동 해제도 가능
}
같은 쓰레드에서 여러 번 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();
}
특정 시간 내에 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;
}
}
여러 뮤텍스를 동시에 안전하게 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은 내부적으로 양쪽 쓰레드에서 같은 방식으로 동기화를 해주기 때문에 데드락 없이 둘 다 잠금에 성공할 수 있어요.
std::mutex m1, m2;
void example() {
std::scoped_lock lock(m1, m2); // C++17: 데드락 방지 + RAII
// 임계 구역
}
[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++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로의 전환은 최신 컴파일러(GCC, Clang, MSVC 등)에서 모두 지원되며,
현대적인 C++ 코드를 작성하고자 한다면 적극적으로 권장됩니다
[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++11 대비 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의 기반 위에서 실질적인 코드 작성의 편의성과 성능, 타입 추론의 유연성을 크게 높였습니다
[cpp] mutex, lock (0) | 2025.04.20 |
---|---|
[cpp] cpp17에서 달라진 점 (0) | 2025.04.20 |
[service] 윈도우 서비스 프로그램 (0) | 2025.01.05 |
[server] ThreadPool, client 접속이 많을 때. TCP Server (0) | 2025.01.05 |
[Server] work, cache, lock, 동시접근문제, 성능문제 (0) | 2025.01.05 |
1. 서비스 관리 명령어
#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;
}
[cpp] cpp17에서 달라진 점 (0) | 2025.04.20 |
---|---|
[cpp] cpp14에서 추가된 것 (0) | 2025.04.20 |
[server] ThreadPool, client 접속이 많을 때. TCP Server (0) | 2025.01.05 |
[Server] work, cache, lock, 동시접근문제, 성능문제 (0) | 2025.01.05 |
[알고리즘] 정렬 알고리즘 비교/성능측정 (1) | 2019.12.16 |
- 클라이언트 접속이 많은 경우, 부하 조정. 쓰레드 풀을 만들어 성능 관리. 동시 실행 쓰레드 수를 제한하여 시스템 과부하 방지.
- 쓰레드 생성 오버헤드 제거. 동시 접속자수 제한으로 서버 안정성 확보. 쓰레드 풀 크기 조정을 하여 성능 조정.
- ^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, §orsPerCluster, &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;
}
[cpp] cpp14에서 추가된 것 (0) | 2025.04.20 |
---|---|
[service] 윈도우 서비스 프로그램 (0) | 2025.01.05 |
[Server] work, cache, lock, 동시접근문제, 성능문제 (0) | 2025.01.05 |
[알고리즘] 정렬 알고리즘 비교/성능측정 (1) | 2019.12.16 |
[알고리즘] 정렬5: quick sort (0) | 2019.12.15 |
client의 요청이 많을 때 동시접근 및 부하를 해결하기 위한 방안...
- 작업 중이면, 캐시 떠 놓은거 사용. 즉, 빠른 응답이 가능하도록.
- read lock, write lock 으로 구분. read 작업은 동시 접근 가능.
#include <iostream>
#include <vector>
#include <shared_mutex>
#include <optional>
#include <thread>
#include <chrono>
#include <atomic>
#include <string>
#include <windows.h>
#include <tlhelp32.h>
// 가정: PROCESS_INFO 구조체
struct PROCESS_INFO {
int pid;
std::string name;
};
std::vector<PROCESS_INFO> g_process; // 프로세스 목록
std::optional<std::vector<PROCESS_INFO>> g_process_cache; // 캐시된 프로세스 목록
std::shared_mutex process_mutex; // 동기화 도구
std::atomic<bool> is_updating{false}; // 업데이트 중인지 확인하는 플래그 추가
// 프로세스 목록을 갱신하는 함수
void update_process(int id) {
if (is_updating.exchange(true)) {
std::cout << "Update already in progress, skipping. : thread id=" << id << "\n";
return;
}
std::cout << "Update processing. : thread id=" << id << "\n";
std::vector<PROCESS_INFO> updated_process;
// 프로세스 스냅샷 생성
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot != INVALID_HANDLE_VALUE) {
PROCESSENTRY32W pe32;
pe32.dwSize = sizeof(pe32);
// 첫번째 프로세스 정보 가져오기
if (Process32FirstW(snapshot, &pe32)) {
do {
// 와이드 문자열을 일반 문자열로 변환
std::wstring wname(pe32.szExeFile);
std::string name(wname.begin(), wname.end());
// 프로세스 정보 추가
updated_process.push_back({
static_cast<int>(pe32.th32ProcessID),
name
});
} while (Process32NextW(snapshot, &pe32));
}
CloseHandle(snapshot);
}
// 실제 데이터 업데이트
{
std::unique_lock<std::shared_mutex> lock(process_mutex);
g_process = std::move(updated_process);
}
is_updating = false;
}
// 프로세스 목록을 출력하는 함수
void print_process(int id) {
std::shared_lock<std::shared_mutex> lock(process_mutex);
if (!g_process_cache || g_process_cache->size() != g_process.size()) {
// 캐시가 없거나 크기가 다르면 캐시 업데이트
g_process_cache = g_process;
}
lock.unlock(); // 캐시를 읽을 때는 잠금 해제
// 캐시된 데이터 사용
const auto& process_list = g_process_cache.value();
std::cout << "Current Process List: thread id=" << id << "\n";
for (const auto& proc : process_list) {
// std::cout << "PID: " << proc.pid << ", Name: " << proc.name << "\n";
}
std::cout << "Current Process Count : " << process_list.size() << " thread id=" << id << "\n";
}
// 클라이언트 스레드
void client_thread(int id) {
std::cout << "Client " << id << " started.\n";
for (int i=0; i<10; i++ ) {
update_process(id); // 프로세스 정보 갱신
print_process(id); // 최신 프로세스 정보 출력
std::this_thread::sleep_for(std::chrono::milliseconds(rand()%100));
}
std::cout << "Client " << id << " finished.\n";
}
int main() {
update_process(0);
// 여러 스레드에서 동작 시뮬레이션
int thread_count = 10;
std::vector<std::thread> threads;
for(int i=0; i<thread_count; i++) {
threads.push_back(std::thread(client_thread, i+1));
}
for(auto& thread : threads) {
thread.join();
}
return 0;
}
[service] 윈도우 서비스 프로그램 (0) | 2025.01.05 |
---|---|
[server] ThreadPool, client 접속이 많을 때. TCP Server (0) | 2025.01.05 |
[알고리즘] 정렬 알고리즘 비교/성능측정 (1) | 2019.12.16 |
[알고리즘] 정렬5: quick sort (0) | 2019.12.15 |
[알고리즘] 정렬4: merge sort (0) | 2019.12.15 |