반응형

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;
}

 

 

반응형

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;
}

 

반응형
파이썬으로 투명 배경의 이미지로 만드는 방법

아래 코드에서 pixel 배열에 R,G,B 값이 들어있으므로 원하는 컬러를 투명하게 처리할 수 있다.

색깔 범위를 +- 5~10정도 주면 좀 더 배경을 잘 제거 할 수 도 있을 것이다. 

from PIL import Image

# 이미지 파일 열기
image = Image.open("image.png")

# 새로운 alpha channel 생성
alpha_data = []
for pixel in image.getdata():
    if pixel[0] == 255 \
        and pixel[1] == 255 \
        and pixel[2] == 255:
        alpha_data.append(0)
    else:  # 그 외의 경우 alpha 값을 255로 설정
        alpha_data.append(255)
alpha_channel = Image.new("L", image.size)
alpha_channel.putdata(alpha_data)

# alpha channel을 이용하여 이미지의 배경을 투명하게 만듦
new_image = image.convert("RGBA")
new_image.putalpha(alpha_channel)

# 이미지 파일 저장
new_image.save("transparent_image.png")

색상 범위를 쉽게 주려면?  아래 코드 참고

# 색상 범위 설정
lower_color = (245, 245, 245)  # 검색할 색상 범위의 최소값
upper_color = (255, 255, 255)  # 검색할 색상 범위의 최대값


    if lower_color <= pixel <= upper_color:
        alpha_data.append(0)
    else:  # 그 외의 경우 alpha 값을 255로 설정
        alpha_data.append(255)

'Python' 카테고리의 다른 글

Drawing Plot, 투명 이미지 datauri 생성  (0) 2023.03.20
set에 set 추가? frozenset  (1) 2021.02.24
Jupyter Notebook 소스 복구  (0) 2020.06.16
Docker python venv 패키지 유지  (0) 2020.06.07
딕셔너리에서 키삭제  (0) 2019.12.07
반응형
포인트 리스트를 가지고 그림을 드로잉하고, 이것을 원하는 크기로 Resize 하고, 투명 이미지 datauri 만들기...
pointlist = '[(10,10)(20,20)(40,50)(60,80)][(10,80)(24,70)(40,60)(60,40)]'

# image 필드에 데이터가 없는 목록만 뽑아서 배치 작업을 수행한다.
def to_datauri(sn):
    sn = sn.strip()
    # 정규표현식으로 포맷 문자열에서 점 정보 추출
    point_regex = r"\(([-+]?\d+),([-+]?\d+)\)"
    strokes = sn.replace('[', '')
    strokes = strokes.split(']')
    strokes = [ s for s in strokes if s!='' ]

    # 그래프 그리기
    plt.figure()
    fig, ax = plt.subplots(figsize=(3, 2))
    ax.set_facecolor('none') # 배경 투명
    fig.patch.set_alpha(0) # 배경 투명
    # 축과 눈금 숨기기
    ax.axis('off')

    # 각 획별로 분리하여 그리기
    for stroke in strokes:
        points = re.findall(point_regex, stroke)
        if len(points)==0:
            continue
        x_list = [int(point[0]) for point in points]
        y_list = [int(point[1]) for point in points]
        ax.plot(x_list, y_list, color='black', lw=2)

    plt.gca().invert_yaxis()  # y축 뒤집기

    # 그래프 저장
    buf = io.BytesIO()
    plt.savefig(buf, format='png', bbox_inches='tight')
    buf_resized = buf

    ## resize 작업
    buf.seek(0)
    img = Image.open(buf)
    img_resized = img.resize((150,100), Image.Resampling.LANCZOS)  
    # img_resized = img.resize((150,100), Image.ANTIALIAS)  
    # sampling=Image.ANTIALIAS deprecated 되었다. 버전에 따라 위 둘 중 하나를 사용.
    buf_resized = io.BytesIO()
    img_resized.save(buf_resized, format=img.format)

    # buf대신 buf_resized를 변환
    buf_resized.seek(0)
    data_uri = 'data:image/png;base64,'+base64.b64encode(buf_resized.read()).decode('utf-8')
    plt.close()
    plt.clf()
    plt.close('all')
    img.close()
    img_resized.close()
    buf.close()
    buf_resized.close()

    return data_uri

이 함수를 사용하여 드로잉 데이터를 넣으면, datauri 텍스트가 출력된다.

시작,끝 따옴표를 제거한 텍스트를 복사해서 브라우저 URL창에 넣으면 드로잉된 투명이미지가 나온다.. (드래그해 보면, 선만 움직이는 것을 확인할 수 있다. 배경투명)

y축을 뒤집은 이유는? 모니터 스크린 좌표계에서 보통 좌상단이 (0,0) 이다.  plot에서는 좌하단이 (0,0)이다. 따라서 스크린 좌표계로 보이게 하려고 하였다. 

datauri가 아니라 파일로 저장하려면 plt.close() 전에  plt.savefig('test.png') 로 저장하면 된다.

 

'Python' 카테고리의 다른 글

투명 배경 이미지 만들기  (0) 2023.03.22
set에 set 추가? frozenset  (1) 2021.02.24
Jupyter Notebook 소스 복구  (0) 2020.06.16
Docker python venv 패키지 유지  (0) 2020.06.07
딕셔너리에서 키삭제  (0) 2019.12.07
반응형
ViewPager가 ViewPager2 로 업데이트되면서 사용하는 방법이 바뀌었다.

상단에 탭 메뉴가 있어서 메뉴 클릭 또는 컨텐트를 좌우 슬라이드하며 다른 페이지로 변경한다.

- TabLayout을 쓰기 위해 Dependency에 별다르게 추가하지 않아도 기본으로 material이 들어가있다. 

'com.google.android.material:material:1.7.0'

- main activity layout에 tab layout 과 viewpager2 를 추가

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tablayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
    >

        <com.google.android.material.tabs.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Monday" />

        <com.google.android.material.tabs.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Tuesday" />

        <com.google.android.material.tabs.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Wednesday" />
    </com.google.android.material.tabs.TabLayout>

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        />

</LinearLayout>

- 각 탭별로 fragment xml 생성

frag_monday.xml, frag_tuesday.xml, frag_wednesday.xml 약간씩 바꿔서 확인.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="170dp"
        android:layout_marginTop="349dp"
        android:layout_marginEnd="170dp"
        android:text="Monday"
        android:textSize="20sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

- 각 fragment 클래스 생성. 이것도 이름만 약간씩 다르게..


public class FragMonday extends Fragment {
    private View view ;
    public static FragMonday newInstance() {
        FragMonday fragMonday = new FragMonday();
        return fragMonday;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.frag_monday, container,  false) ;
        return view ;
    }

}

- Adapter를 만들어야 한다. ViewPagerAdapter class


public class ViewPagerAdapter extends FragmentStateAdapter {
    private final List<Fragment> fragmnets = new ArrayList<Fragment>() ;
    private final List<String> titles = new ArrayList<>() ;

    public ViewPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
        super(fragmentActivity);
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        return fragmnets.get(position) ;
    }

    @Override
    public int getItemCount() {
        return fragmnets.size() ;
    }

    public void addFragment(@NonNull Fragment frag, String title) {
        fragmnets.add(frag) ;
        titles.add(title) ;
    }
    @NonNull
    public String getTitle(int position) {
        return titles.get(position) ;
    }

}

 

- 이제 MainActivity에서 모두 연결시킨다.


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(this);
        viewPagerAdapter.addFragment(FragMonday.newInstance(), "Monday");
        viewPagerAdapter.addFragment(FragTuesday.newInstance(), "Tuesday");
        viewPagerAdapter.addFragment(FragWednesday.newInstance(), "Wednesday");

        ViewPager2 viewPager = findViewById(R.id.viewpager);
        viewPager.setAdapter(viewPagerAdapter);

        TabLayout tabLayout = findViewById(R.id.tablayout);
        TabLayoutMediator tm = new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
            tab.setText(viewPagerAdapter.getTitle(position)) ;
        }) ;
        tm.attach();
    }
}

끝.

 

'Develop > Android' 카테고리의 다른 글

RecyclerView, Firebase DB, reload  (0) 2023.03.19
[Android] JSON 데이터 송수신  (1) 2023.03.18
반응형
Firebase DB를 사용하여 RecyclerView에 출력하고, 갱신 버튼을 눌러, DB를 reloading하여 출력해 보자.

 

1. dependency 추가

implementation 'androidx.recyclerview:recyclerview:1.3.0'
implementation 'com.github.bumptech.glide:glide:4.10.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'

2. activity_main.xml에 recyclerview 추가. reload 버튼 추가

<Button
    android:id="@+id/btn_Reload"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="reload"
    />

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recyclerview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />

3. 아이템 디자인 list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <ImageView
            android:id="@+id/iv_profile"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@mipmap/ic_launcher"
            />
        <LinearLayout
            android:layout_marginLeft="15dp"
            android:gravity="center_vertical"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="vertical"
            >
            <TextView
                android:id="@+id/tv_id"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="hello"/>
            <TextView
                android:id="@+id/tv_pw"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="hello"/>
            <TextView
                android:id="@+id/tv_userName"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="hello"/>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

4. 아이템 class. User class (getter and setter 는  alt+insert키로 쉽게 자동생성 하세요.. )


public class User {
    private String profile ;
    private String id ;
    private int pw ;
    private String userName ;

5. CustomAdapter class

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.CustomViewHolder> {
    private ArrayList<User> arrayList ;
    private Context context ;

    public CustomAdapter(ArrayList<User> arrayList, Context context) {
        this.arrayList = arrayList;
        this.context = context;
    }

    @NonNull
    @Override
    public CustomAdapter.CustomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false) ;
        CustomViewHolder holder = new CustomViewHolder(view) ;
        return holder; // 홀더 리턴
    }

    @Override
    public void onBindViewHolder(@NonNull CustomAdapter.CustomViewHolder holder, int position) {
        Glide.with(holder.itemView)
                .load(arrayList.get(position).getProfile())
                .into(holder.iv_profile) ;
        holder.tv_id.setText(arrayList.get(position).getId());
        holder.tv_pw.setText(String.valueOf(arrayList.get(position).getPw()));
        holder.tv_userName.setText(arrayList.get(position).getUserName());
    }

    @Override
    public int getItemCount() {
        return (arrayList!=null ? arrayList.size(): 0);
    }

    public class CustomViewHolder extends RecyclerView.ViewHolder {
        ImageView iv_profile ;
        TextView tv_id ;
        TextView tv_userName ;
        TextView tv_pw ;

        public CustomViewHolder(@NonNull View itemView) {
            super(itemView);
            iv_profile = itemView.findViewById(R.id.iv_profile) ;
            tv_id = itemView.findViewById(R.id.tv_id) ;
            tv_userName = itemView.findViewById(R.id.tv_userName) ;
            tv_pw = itemView.findViewById(R.id.tv_pw) ;

        }
    }
}

6. Firebase 설정.

Tools - firebase 선택.  (예전엔 수작업으로 한 거를 편리하게 다 해준다..)

우측에 Realtime Database 메뉴를 선택한다.  Get started with Realtime Database 로 가서, 차례로 진행한다.

connect your app to firebase ; 브라우저로 구글 로그인하여 firebase 콘솔로 가서 프로젝트 생성까지 진행됨.

(실시간 DB를 잘 만든다. 필드도  User class에 맞게 다 맞춘다. +를 눌러 User를 만들고 다시 +를 눌러 User_01을 만들고 다시 +를 눌러 profile, id, pw, userName 필드들을 만든다.   처음엔 사용법을 몰라 어렵다. 하위 노드를 위해 미리 +를 연속해서 눌러줘야 된다. 미리 만들고, 엔터치면 하위에 추가가 안되서 삽질.. )

Add the Realtime Database to your app ; dependency 등을 알아서 설정해 준다.

그러나,... 나의 경우는 빌드시 에러가...  찾아보니, 버전 문제가 또 있다... (어쩌라고.. 정말  막막하게 만드는  버전 호환성...)

build.gradle (project) 에서 4.3.10 으로 들어가 있는데 찾아보니 버전을 14로 올리면 해결된다고 해서 해보니 성공하였음.

classpath 'com.google.gms:google-services:4.3.14'

 

7.MainActivity에서 RecyclerView와 Adapter 연결 및 Firebase 연동...

public class MainActivity extends AppCompatActivity {
    private RecyclerView recyclerView ;
    private RecyclerView.Adapter adapter ;
    private RecyclerView.LayoutManager layoutManager ;
    private ArrayList<User> arrayList ;
    private FirebaseDatabase database ;
    private DatabaseReference databaseReference ;
    private ValueEventListener valueEventListener ;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = findViewById(R.id.recyclerview) ;
        recyclerView.setHasFixedSize(true);
        layoutManager = new LinearLayoutManager(this) ;
        recyclerView.setLayoutManager(layoutManager);
        arrayList = new ArrayList<User>() ;

        adapter = new CustomAdapter(arrayList, this) ;
        recyclerView.setAdapter(adapter);

        database = FirebaseDatabase.getInstance();
        databaseReference = database.getReference("User") ; // User table

        valueEventListener = new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                Log.e("AAA", "reload data") ;
                // 데이터를 수신.
                arrayList.clear();
                for (DataSnapshot ds : snapshot.getChildren()) {
                    User user = ds.getValue(User.class) ;
                    Log.e("AAA", user.getId()) ;
                    arrayList.add(user) ;
                }
                adapter.notifyDataSetChanged();
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                Log.e("AAA", error.toString()) ;
            }
        } ;

        databaseReference.addListenerForSingleValueEvent(valueEventListener);

        findViewById(R.id.btn_Reload).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                databaseReference.addListenerForSingleValueEvent(valueEventListener);
            }
        });

    }
}

- Reload를 어떻게 하나 궁금했는데 찾아보니 위와 같이 하니 되었다. databaseReference에 리스너를 또 등록하면 됨. 그러면 노드들을 다시 가져오고 갱신되었다. 

 

'Develop > Android' 카테고리의 다른 글

ViewPager2, TabLayout  (0) 2023.03.19
[Android] JSON 데이터 송수신  (1) 2023.03.18
반응형

안드로이드 스튜디오에서 okhttp3를 사용하여 JSON 데이터를 비동기 방식으로 전송하고 수신하는 코드는 다음과 같습니다. 

(참고 app: build.gradle의 dependencies에 추가)

implementation 'com.squareup.okhttp3:okhttp:4.9.3'

(manifest에 권한 추가)

<uses-permission android:name="android.permission.INTERNET"/>
import okhttp3.*;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

    private OkHttpClient client = new OkHttpClient();
    private MediaType JSON = MediaType.parse("application/json; charset=utf-8");

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // JSON 객체 생성
        JSONObject json = new JSONObject();
        try {
            json.put("name", "John Doe");
            json.put("age", 30);
        } catch (JSONException e) {
            e.printStackTrace();
        }

        // RequestBody 생성
        RequestBody body = RequestBody.create(json.toString(), JSON);

        // Request 생성
        Request request = new Request.Builder()
                .url("https://example.com/api")
                .post(body)
                .build();

        // 비동기 방식으로 요청 전송
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (!response.isSuccessful()) {
                    throw new IOException("Unexpected code " + response);
                }

                // 수신된 JSON 데이터 디버그 로그로 출력
                try {
                    String responseData = response.body().string();
                    JSONObject receivedJson = new JSONObject(responseData);
                    Log.d("Received JSON", receivedJson.toString());
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

 

* 에러 관련

not permitted by network security policy

  • 버전이 올라가면서 기본적으로 http는 거부한다.. 이런 에러를 만날 것이다.
android okhttp not permitted by network security policy
해결방안은... 
manifest, application 태그
android:usesCleartextTraffic="true"

  • 하지만 스토어에 등록은 안될 것이다!!. https에 SSL 인증까지 해야 등록가능..
  • 즉, 테스트 용도에서는 가능.

NetworkOnMainThreadException , StrictMode$AndroidBlockGuardPolicy

  • 메인 쓰레드에서 네트웍을 블로킹방식 ( execute() )으로 하게 되면 만나는 에러...
  • 쓰레드를 만들어 돌리든지, 비동기 방식으로 변경..
new Thread() {
    public void run() {
        try {
            listFile();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }
}.start();

 

'Develop > Android' 카테고리의 다른 글

ViewPager2, TabLayout  (0) 2023.03.19
RecyclerView, Firebase DB, reload  (0) 2023.03.19
반응형

원격 mysql 서버에 있는 DB의 스키마 작성 쿼리만 얻고 싶을 때?

 

mysqldump --no-data -h [서버IP] -P [포트] -u [사용자] -p [DB명]

  • 옵션 대문자 P와 소문자 p를 헛갈리지 않도록 주의!
  • 보통 mysql 디폴트 포트는 3306 이지만 관리자가 변경할 수 도 있다. 
  • --no-data 옵션으로 데이터는 받지 않고, 스키마만 받을 수 있다. (create table 만 나옴)
  • -p 옵션은 password를 프롬프트로 입력받겠다는 의미이다. -p를 생략하면 패스워드 없이 인증하려고 시도하여 인증실패 날 수 있다. 프롬프트로 패스워드를 받지 않고 커맨드에 패스워드를 직접 넣으려면 -p[패스워드]  이렇게 공백없이 붙여쓴다.   -p 옵션뒤에 공백을 넣고 패스워드를 넣으면  프롬프트로 패스워드 넣으라고 나오고, 뒤에 나온 스트링을 DB명으로 인식하여 찾게 된다.

 

mysql 클라이언트 옵션도 동일하다..

  • mysql  -h [서버IP] -P [포트] -u [사용자] -p
  • mysql  -h [서버IP] -P [포트] -u [사용자] -p[패스워드]
  • mysql  -h [서버IP] -P [포트] -u [사용자] -p [DB명]
  • mysql  -h [서버IP] -P [포트] -u [사용자] -p[패스워드] [DB명]

( -h 옵션 생략시 로컬 호스트 , -P 생략시 3306, -p 생략시 암호없음)

 

반응형

AI에게 이미지를 생성하라고 해 본 결과.....

아래 생성한 이미지의 프롬프트는...   

anime girl, long light gold hair, splendid white designer fashion show concept dress, red cosplay headwear, black scarf, body portrait, red eyes, pink ribbons, slight smile, black constellation motif, windy, concept art, mini tornado stickers, black fishnet wear, highly detailed, digital painting, artstation, concept art, sharp focus, illustration, art by WLOP and greg rutkowski and alphonse mucha and artgerm and yanjun chen

 

 

 

아.. 이건 처음으로 프롬프트 아무거나 달과 지구 사진을 달라고 테스트 해 본 거입니다.

 

 

'AI(DeepLearning)' 카테고리의 다른 글

[ChatGPT] 글쓰기  (0) 2023.02.09
[ChatGPT] 글쓰기 테스트  (1) 2023.02.09
XGBoost 요약  (0) 2019.12.06
처음해본 kaggle 도전기  (1) 2019.11.27
Custom EarlyStopping  (0) 2019.11.11

+ Recent posts