프로그램 개요
**UbiAnyCard Ver2**는 KICC ED785 단말기를 통한 카드 결제 시스템의 TCP 서버 애플리케이션입니다.
주요 기능
- TCP 서버: 외부 클라이언트로부터 TCP 연결을 받아 결제 명령 처리
- 카드 결제: 승인/취소/조회 등의 결제 명령을 KICC ED785 단말기와 통신하여 수행
- 전자화폐 지원: 티머니, 캐시비, 레일플러스 등 전자화폐 결제 지원
- 연결 모델: 거래당 연결 모델로 안정적인 거래 처리
- Keep-Alive: PING 명령을 통한 연결 유지 기능
시스템 특징
- 동기 처리: TCP 요청부터 ED785 응답까지 동기적으로 처리
- 스레드 안전성: 원자적 연산과 뮤텍스로 동시성 제어
- 리소스 관리: RAII 패턴으로 자동 정리
- 인코딩 지원: UTF-8 ↔ EUC-KR 변환 처리
1. 전체 아키텍처 개요
시스템 구성 요소
주요 설계 원칙
- 단일 책임 원칙: 각 클래스가 명확한 역할
- 동기 처리: TCP 요청 → ED785 응답까지 동기적으로 처리
- 스레드 안전성: 원자적 연산과 뮤텍스로 동시성 제어
- 리소스 관리: RAII 패턴으로 자동 정리
2. 데이터 흐름 분석
2.1 전체 처리 플로우
2.2 거래당 연결 모델 시퀀스 (승인/취소 등)
2.3 PING Keep-Alive 시퀀스
2.4 세부 처리 단계
A. TcpServer (TCP 통신 계층)
void acceptLoop() {
while (m_running.load()) {
SOCKET client = ::accept(m_listen, ...);
std::thread([this, client]() {
}).detach();
}
}
특징:
- 거래당 연결 모델: 각 거래마다 새로운 TCP 연결
- PING Keep-Alive: PING 명령만 연결 유지
- KICC 프로토콜 준수: ACK 패킷 (0x06 0x06 0x06) 처리
- 요청 종료 규칙: 개행(
)으로 요청 종료 판단. 세미콜론(;)은 데이터 내 K=V 구분자로만 사용
B. ProtocolAdapter (프로토콜 변환 계층)
std::string onCommand(const std::string &cmdline) {
auto [cmd, arg] = splitOnce(line, ' ');
if (cmd == "APPROVE") {
return sendAndWait(0x14, 0x04, kvs, 60000);
}
}
특징:
- 동기 처리: std::promise/future로 응답 대기
- 트랜잭션 관리: 각 요청마다 고유 ID 할당
- 타임아웃 처리: 요청별 다른 타임아웃 설정
- 상태 이벤트 분리 처리: 0x14/0x09(단말기 상태) 응답은 비동기 이벤트로만 로깅하고 트랜잭션 매칭에서 제외
C. ED785Worker (하드웨어 통신 계층)
void workerThread() {
while (m_running.load()) {
}
}
특징:
- 단일 스레드: KICC DLL 요구사항 준수
- 폴링 방식: 200ms 간격으로 이벤트 확인
- 윈도우 메시지: 숨겨진 윈도우로 DLL 메시지 처리
3. 주요 명령어 처리 흐름
3.1 APPROVE 명령 (카드 승인)
"APPROVE S01=D4;S12=12345678;S13=20231201;..."
std::future<TxResponse> future = pending->promise.get_future();
auto status = future.wait_for(std::chrono::milliseconds(60000));
ParseAndAppendElectronicMoney(kv);
"RC=RC_SUCCESS;S01=D4;S12=12345678;EM_TYPE=TMONEY;..."
static std::vector< std::pair< std::string, std::string > > ParseKvOrdered(const std::string &s)
Definition ProtocolAdapter.cpp:31
Definition ED785Worker.h:22
int jcd
Definition ED785Worker.h:25
std::vector< byte > payload
Definition ED785Worker.h:26
int gcd
Definition ED785Worker.h:24
int cmd
Definition ED785Worker.h:23
3.2 CANCEL 명령 (승인 취소)
"CANCEL S12=원승인번호;S13=원승인일자;..."
"ERR=MISSING_APPROVAL_INFO"
"RC=RC_SUCCESS;..."
3.3 TERMINFO 명령 (단말기 정보)
"TERMINFO"
"RC=RC_SUCCESS;DATA=단말기정보문자열"
3.4 PING 명령 (연결 유지)
"PING"
return "PONG";
keepAlive = true;
do {
} while (keepAlive);
3.5 DISPLAY 명령 (표시)
"DISPLAY <ASCII_PAYLOAD>"
"RC=RC_SUCCESS;DATA=..."
3.6 PRINT 명령 (출력)
"PRINT <ASCII_PAYLOAD>"
"RC=RC_SUCCESS;DATA=..."
3.7 READ_SNO 명령 (시리얼/식별 정보 읽기)
"READ_SNO"
"READ_SNO R"
req.
payload = (arg ==
"R") ?
"R" :
"";
"RC=RC_SUCCESS;SNO=<HEX_STRING>"
3.8 INIT 명령 (단말기 초기화)
3.9 단말기 상태 이벤트 (비동기)
4. 스레드 모델 분석
4.1 스레드 구성
Main Thread (AppMain)
├── TcpServer::acceptThread (연결 수락)
├── ED785Worker::workerThread (하드웨어 통신)
└── Client Threads (거래당 연결, 동적 생성)
4.2 동시성 제어
std::atomic<bool> m_running{false};
std::atomic<uint64_t> m_txSeq{0};
std::mutex m_queueMutex;
std::mutex m_pendingMutex;
std::mutex m_currentMutex;
std::scoped_lock lock(m_queueMutex);
5. 문자열 인코딩 처리
5.1 인코딩 변환 체인
Client (UTF-8) → TcpServer (UTF-8) → ProtocolAdapter (UTF-8)
↓
ED785Worker (EUC-KR) ← ED785 DLL (EUC-KR) ← KICC 단말기 (EUC-KR)
5.2 변환 함수들 (Windows,하드웨어요구사항,TCP인코딩에 따른 변환 유틸리티)
int wlen = ::MultiByteToWideChar(949, 0, euckr.c_str(), ...);
std::wstring wstr(wlen);
::MultiByteToWideChar(949, 0, euckr.c_str(), ..., wstr.data(), wlen);
int u8len = ::WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), ...);
std::string utf8(u8len);
::WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), ..., utf8.data(), u8len);
return utf8;
}
int wlen = ::MultiByteToWideChar(CP_UTF8, 0, response.c_str(), ...);
std::wstring wResp(wlen);
::MultiByteToWideChar(CP_UTF8, 0, response.c_str(), ..., wResp.data(), wlen);
static std::string ConvertEucKrToUtf8(const std::string &euckr)
Definition ProtocolAdapter.cpp:5
6. 에러 처리 및 로깅
6.1 에러 처리 전략
if (!m_running.load()) return false;
int result = m_ed785->KReqCmd(...);
if (result != 0) {
return false;
}
auto status = future.wait_for(std::chrono::milliseconds(timeoutMs));
if (status == std::future_status::timeout) {
return "ERR=TIMEOUT";
}
6.2 로깅 레벨
Logger::info(L"정상 처리");
Logger::warn(L"경고 상황");
Logger::error(L"에러 발생");
Logger::debug(L"디버그 정보");
Logger::pay(L"결제 성공");
7. 성능 최적화 요소
7.1 메모리 관리
std::unique_ptr<CED785> m_ed785;
std::unique_ptr<PendingTx> pending;
m_callback = std::move(callback);
req.
payload.assign(asciiPayload.begin(), asciiPayload.end());
7.2 네트워크 최적화
BOOL reuse = TRUE;
::setsockopt(m_listen, SOL_SOCKET, SO_REUSEADDR, ...);
std::thread([this, client]() { ... }).detach();
7.3 폴링 최적화
static int pollCount = 0;
if (++pollCount % 50 == 0) {
Logger::debug(...);
}
static constexpr uint32_t POLL_INTERVAL_MS
Definition ED785Worker.cpp:8
8. 설정 및 초기화
8.1 설정 로드
HANDLE hMutex = CreateMutexW(nullptr, TRUE, L"UbiAnyCard_Ver2.Instance");
AppSettings LoadSettings()
Definition Settings.h:23
8.2 컴포넌트 초기화 순서
1. Logger 초기화
2. 설정 로드
6. 메인 루프 시작
Definition ED785Worker.h:14
Definition ProtocolAdapter.h:12
Definition TcpServer.h:11
9. 주요 클래스 상세 분석
9.1 ED785Worker 클래스
역할: KICC ED785 단말기와의 통신을 담당하는 워커 클래스
핵심 특징:
- 동기 방식, 단일 스레드: KICC DLL이 단일 스레드 요구사항이 있어 모든 DLL 호출을 하나의 워커 스레드에서 처리
- 윈도우 메시지 처리: DLL 내부에서 윈도우 메시지를 사용하므로 핸들을 넘겨줘야 함
- 폴링 방식: 요청 큐 → KReqCmd → KGetEvent 폴링 → 응답 콜백
데이터 구조:
struct Request {
int cmd;
int gcd;
int jcd;
std::vector<byte> payload;
};
struct Response {
int cmd;
int gcd;
int jcd;
int rcd;
std::vector<byte> data;
size_t dataLen;
std::vector<byte> hex;
size_t hexLen;
};
std::deque 사용 이유:
- 성능상의 이점: 양방향에서 O(1) 성능
- 직접적인 인터페이스 접근: 불필요한 래핑 오버헤드 제거
- 메모리 효율성: 블록 단위로 메모리 할당
- 디버깅 및 모니터링 용이성: 큐 크기 직접 접근
9.2 ProtocolAdapter 클래스
역할: 서버 텍스트 명령을 ED785 요청으로 변환하고 응답을 처리
핵심 특징:
- 동기 처리: std::promise/future로 응답 대기
- 트랜잭션 관리: 각 요청마다 고유 ID 할당
- 전자화폐 파싱: 티머니, 캐시비, 레일플러스 자동 감지 및 파싱
주요 메서드:
std::string onCommand(const std::string &cmdline);
std::string sendAndWait(int gcd, int jcd, const std::string &asciiPayload, uint32_t timeoutMs);
Definition ED785Worker.h:31
9.3 TcpServer 클래스
역할: KICC 프로토콜 준수 TCP 서버
핵심 특징:
- 거래당 연결 모델: 각 거래마다 새로운 TCP 연결
- PING Keep-Alive: PING 명령만 연결 유지
- KICC 프로토콜 준수: ACK 패킷 (0x06 0x06 0x06) 처리
처리 흐름:
클라이언트 연결 수락
→ 요청 수신 (개행 종료 판단)
→ PING 명령 확인
→ ACK 전송 (0x06 0x06 0x06)
→ 핸들러 호출 (동기 처리)
→ 응답 전송
→ ACK 대기 (선택적)
→ PING이면 계속 요청 대기, 아니면 연결 종료
10. 정리
계층화된 설계 동기 처리: 신뢰성 있는 거래 처리를 보장 스레드 안전성
주요 장점:
- 모듈화: 각 계층이 명확히 분리되어 유지보수성 향상
- 안정성: 동기 처리와 타임아웃으로 안정적인 거래 처리
- 확장성: 새로운 명령어나 전자화폐 추가 용이
- 성능: 효율적인 메모리 관리와 네트워크 최적화
기술적 특징:
- C++17 표준: RAII, 스마트 포인터, 원자적 연산 활용
- Windows API: 소켓, 윈도우 메시지, 문자 인코딩 처리
- KICC DLL 연동: 단일 스레드 요구사항 준수