# TCmalloc (Thread-Caching memory allocation)

https://code.google.com/p/gperftools/

- 구글이 만든 성능 도구에 포함되어 있는 힙 메모리 할당자로서 크롬 및 많은 프로젝트에 사용 

- 기존의 malloc 으로 대표되는 할당자의 경우 멀티쓰레드의 최적화가 고려되지 않은 상황의 구현체이기 때문에, Thread가 난무하는 현실 세계에서는 많은 성능 저하가 있다고 함 (메모리 할당, 관리도 동시성을 고려해야 하므로..)

- 이쪽 계열 라이브러리 중에서는 가장 많이 알려져 있고, 기본 할당기에 비해 비약적인 성능 향상이 있다고 함.

- 그리고 적용 방법도 드라마틱 하게, 인상적임. 



# 리눅스(Ubuntu, 14.10) 환경에서의 TCmalloc 

- 패키지로 설치 하던가, 소스를 받아서 설치 하면 됨  

sudo apt-get install libtcmalloc-minimal4


* 사용하는 방법 3가지 

1. tc_malloc 및 tc_free 등의 함수를 사용하여 malloc 이나 C++의 기본 할당자를 직접 대체하는 방법


2. 라이브러리를 링크하여 C 및 C++ 기본 할당자는 대체하는 방법

TCmalloc는 라이브러리를 링크하는 것 만으로, C 및 C++의 기본 할당자를 tmalloc의 할당자로 대체 해줌.(Patch)

g++ -o mybin mybin.cpp -ltcmalloc_minimal 


3. LD_PRELOAD를 이용하여 모듈을 먼저 로드(후킹)시킨 후, 기본 할당기를 대체하는 방식 

허나 구글에서는 이 방법에 대해서는 tricky 한 방법이므로, 2번 방법을 사용 하도록 권고함

LD_PRELOAD=/usr/lib/libtcmalloc_minimal.so mybin

http://gperftools.googlecode.com/git/doc/tcmalloc.html

 

* 성능 

- 극단적인 할당과 해제를 반복하는 멀티쓰레드 환경에서, 기본 할당자 보다 3배이상 빠른 결과를 보임

- 물론 일반적인 로직의 경우 메모리 할당, 해제에 관련해서 비중이 얼마나 많이 차이나느냐에 따라 다르겠지만, 메모리 할당에서의 성능 향상 뿐만 아니라, 지속적이 서비스로 인한 메모리 단편화를 감소시켜 바꾸는 것만으로 20% 정도의 성능 향상을 보인 다고 함.

  


# 윈도우 환경에서의 TCmalloc 

- 소스 받아서 MSVC로 설치 하면 됨. 친절하게 MSVC 프로젝트 파일로 제공 됨  

- 솔루션에 많은 프로젝트들이 보이는데, libtcmalloc_minimal 라고 되어 있는 프로젝트만 Release 로 빌드하면, 동적링크 모듈(DLL)로 빌드되고 정적으로 바꾸고 빌드해서 사용해도 무방한 것으로..


* 사용하는 방법 2가지 

1. 위의 리눅스의 예와 같음. TCmalloc의 커스텀 할당자로 구현 하면 됨


2. 위도우 환경에서는 Patch 방식이 되지 않으므로, 라이브러리만 링크하는 것으로 끝나지는 않고 강제로 링크 심볼을 정의 함으로서 기본 할당자가 교체 되도록 설계 함 (이것도 멋짐!!)

// 소스에 아래와 같이 추가하여 라이브러리 참조 및 링크 심볼을 주입 함 

#pragma comment (lib, "libtcmalloc_minimal")

#pragma comment (linker, "/include:__tcmalloc") 


// 프로젝트 속성 환경에서 추가 할때는,  

// 구성속성-링커-입력-추가종속성 에 명시하여 링크를 추가하고, 

// 밑에 "강제 기호 참조"에 __tcmalloc 를 넣어주면 됨


* 성능 

- 극단적인 환경에서는 30% 이상 효과가 있고, 일반적인 업무 로직에서는 그보다는 작겠지만 어느정도 성능 향상 효과가 있을 것으로 판단.

- 기본적으로 MSVC 환경에서의 메모리 할당자가 리눅스쪽의 것보다는 좋아 보임.


--


# JEMalloc 

- Jason Evans가 만들었고, 페이스북이나 파이어폭스에서 사용 한다고 함

- 성능은 TCmalloc 보다 약간 좋다고는 (주장)하나, 개인적으로 테스트 할때는 TCmalloc 보다 약간 부족 했음.

- 구글링의 결과로는 보다 다이나믹한 멀티쓰레드 환경에서 최적화 되었다는데 반복적인 할당과 해제를 반복하는 예제에서는 그다지 많은 차이를 보이지 않음.


# 리눅스 환경에서의 JEMalloc

http://www.canonware.com/jemalloc/

- TCmalloc 과 같이 소스 및 패키지 설치 지원 

sudo apt-get install libjemalloc-dev


* 사용하는 방법 

- 위의 TCmalloc과 같이 JEMalloc의 커스컴 할당기(je_malloc, je_free) 를 사용 하던가 LD_PRELOAD를 이용하여 사용하는 방법이 있음 

- JEMalloc도 링크 만으로 기본 할당자를 교체 해주나, C언어의 기본 할당자 malloc 계열만 교체 해주고 C++의 것은 교체 해주지 않음. 그래서 C++에서 사용하려면(STL 등에서) 사용자 정의 할당자를 만들고 그 곳에 JEMalloc으로 할당 하는 방식으로 처리해야 함

- 리눅스의 경우 C, C++ 할당자를 모두 교체 지원 함


- 윈도우의 경우도 링크 참조만으로 기본 할당기를 교체해주지는 않고, header 파일을 이용해서 매크로로 C언어 기본 할당자를 교체 해주는 방식으로 처리 (#include <jemalloc/jemalloc.h>)

- Mingw (gcc 4.9.2)에서는 컴파일이 잘되나, MSVC(2013) 에서는 컴파일이 몇가지 문제를 일으킴 

Github에 Win32용 포팅 버전이 있으므로 그것을 받아서 빌드 하면 됨  


# 기타

- nedmalloc : 윈도우즈 환경에 더 최적화 되었다는 nedmalloc 이런것도 있음. nedmalloc의 경우 자체 C언어 계열 커스텀 할당 함수와 C++의 allocator 를 대체할 클래스도 제공.


- tbbmalloc (Threading Building Blocks) : 인텔에서 만든 쓰레딩관련 라이브러리에 포함 되어 있는 힙 메모리 할당기 

- 사이트에서 다운 받으면 OS별 바이너리가 들어 있음.

// linux 

sudo apt-get install libtbb-dev

g++ -o mybin mybin.cpp -ltbbmalloc_proxy


// MSVC, tcmalloc 과 같은 방식

#pragma comment (lib, "tbbmalloc_proxy")

#pragma comment (linker, "/include:___TBB_malloc_proxy") 



Detours Express 3.0Detours is a library for intercepting arbitrary Win32 binary functions on x86 machines.

// 설치 후, 설치 디렉토리에서 nmake 로 빌드 후 사용 

// dll 프리로드 후킹 (Windows)

withdll.exe /d:libtcmalloc_minimal.dll mybin




# 추가, 윈도우 테스트 결과 (참고용) 

소스 : https://gist.github.com/cdecl/e7b414e8eff4a67c9396


* vc++ 기본 (msvc 2013)

[std::string alloc]

1585, 1656, 1600, 1707, 1628,

[malloc()]

1451, 1411, 1381, 1489, 1399,


* tcmalloc 

[std::string alloc]

1156, 1150, 1195, 1045, 1184,

[malloc()]

560, 586, 564, 571, 564,


* tbbmalloc (Threading Building Blocks)

[std::string alloc]

1003, 973, 963, 998, 1000,

[malloc()]

811, 782, 780, 813, 807,



# 추가, 리눅스 테스트 결과 (참고용) 

- 소스 : malloc 반복횟수 및 옵티마이저 안되게 끔 위의 소스 수정 


* 기본 (g++ 4.9.1)

[std::string alloc]

3931, 3904, 3945, 3902, 4044,

[malloc()]

1434, 1422, 1447, 1457, 1411,


* tcmalloc

[std::string alloc]

1112, 1125, 1161, 1099, 1091,

[malloc()]

355, 333, 328, 351, 334,


* jemalloc

[std::string alloc]

1451, 1442, 1396, 1427, 1411,

[malloc()]

438, 483, 432, 462, 457,


* tbbmalloc

[std::string alloc]

1856, 1843, 1820, 1868, 1842,

[malloc()]

750, 701, 765, 764, 752,



참고 : 

one malloc to rule them all

http://blog.reverberate.org/2009/02/one-malloc-to-rule-them-all.html


Scalable memory allocation using jemalloc

https://www.facebook.com/notes/facebook-engineering/scalable-memory-allocation-using-jemalloc/480222803919


Benchmarks of the Lockless Memory Allocator

http://locklessinc.com/benchmarks_allocator.shtml


C++ memory allocation mechanism performance comparison (tcmalloc vs. jemalloc)

http://stackoverflow.com/questions/7852731/c-memory-allocation-mechanism-performance-comparison-tcmalloc-vs-jemalloc

cdecl/asb 개발 중..


1. wg/wrk (Modern HTTP benchmarking tool)

- wrk는 커맨드 라인의 옵션으로, 간단한게 Http 서비스의 성능 평가를 할 수 있는 툴.

- 윈도우 환경에서도 쓸 수 있지 않을까 했지만, Linux의 aio(Asynchronous I/O)으로 구현 되어 있어 포팅 불가.

- 비슷한 툴로 Apache ab, JoeDog/siege 등이 있으며, github에 "benchmark" 등의 단어로 검색하면 더 많은 언어로 작성되어 있는 오픈소스를 찾아 볼 수 있음.


2. Boost Asio

- 윈도우 환경이라면 IOCP로 작성 가능 하겠지만, 기본적으로 작성해야 할 내용이 많고 크로스 플랫폼을 지원하고 성능적인 부분에서 검증이 되어 있는 Asio로 작성 

- boost 라이브러리는 소스 및 필요한 바이너리만 컴파일 하여 사용. (system, regex, date-time, thread) 

- regex나 thread의 경우 이미 C++11에 포함 되어 있지만 Asio 구현체가 Boost 것을 사용 함. 


3. Visual C++ 2013 

- 가장 C++11이 적합한 VC++ 컴파일러.  

- boost 및 openssl 의 경우 NuGet 으로 편하게 라이브러리를 추가해서 사용 할 수 있음. 

- boost는 NuGet의 버전이 낮고, 위에 언급한 이유로 별도로 빌드 하여 사용 함. 그리고 x64 staitc 빌드에서 crt 관련 링크 에러가 발생? 


4. g++ 4.9.1 

- ubuntu 환경에서 g++로 컴파일. 

- hyper-v 환경에서 ubuntu 14.04로 개발 테스트를 하다, 이후 14.10으로 변경. 

- g++가 문법적으로는 C++11을 VC++에 비해 더 잘 지원하는것 같으나, 기본 라이브러리가 아직 C++11에 미흡한 것들이 있음.

- std::regex 는 아직 지원을 하지 않아 boost::regex를 사용. 찾아보니 버전 5. 이상에서 지원할 것이라 추측.

- std::stringstream 의 swap 및 move ctor를 지원하지 않음.  

- OpenSSL의 경우 apt-get 패키지로 설치. 


5. Git

- 윈도우 버전의 경우 Git과 같이 배포되는 *nix 의 커맨드라인 툴이 유용함.

- tortoies-git 을 사용하는데, 몇가지 명령어는 svn 식으로 표현 된것 같음.


6. SSL (OpenSSL)

- asio에서 지원하는 ssl 코드를 사용해서 ssl 내부적인 내용까지는 구현이 필요 없었음. (다행히도..)


7. 성능 

- 환경 Laptop Intel i5(4세대), 4 Core, 8G RAM, Windows 8.1 64bit (Ubuntu 14.04 on hyper-v , 4 core, 1G RAM)  


- 윈도우 환경에서는, 최적의 옵션의 경우, IIS(local, static html) 초당 40,000 이상 호출 결과.

- 윈도우 환경 최대 연결 개수는 약 26,000 클라이언트 개수까지 테스트 

  

- Ubuntu (on hyper-v) 환경에서는, Nginx(local, static html) 호출, 초당 35,000 이상 호출 결과.

- Ubuntu 환경 최대 연결 개수를 약 30,000 클라이언트 개수까지 테스트 

- wrk 에 비해 성능이 10~20% 정도 떨어지는 것 같음. (상황에 따라 좀 다른 결과)


8. 전략 

- 기본적인 Thread 생성 개수는, 메인 + io_service 개수 + 사용자 Thread 개수가 생성이 됨

- io_service를 Thread 별로 생성을 한다면, -t 4 옵션의 경우 9개가 생성됨

- io_service는 한개만 생성하고 io_service::run() 를 사용자 Thread 에서 실행한다면, -t 4 옵션의 경우 5개 생성

- 테스트 결과 io_service per thread VS io_service::run() per thread 성능상 별 차이 없어 보여서 적게 생성되는 방향 정리.



* 기타 

- 개발 중, Http Client 모듈 수정하여 http_client 라이브러리 만듬 



cdecl/asbHttp benchmarking and load test tool for windows, posix

- wg/wrk (Modern HTTP benchmarking tool)과 기능이 비슷한, Windows 버전으로 만들게 된 커맨드 라인 툴.

- boost::asio 와 asio 에서 지원하는 ssl(openssl)을 이용하여 구현.

- VC++ 2013 환경에서 개발하고, 라이브러리 패키지 NuGet으로 관리.

- Posix 환경은 g++ (4.8 이상, -std=c++11), boost 라이브러리 및 libssl-dev (openssl) 패키지 설치  


# 테스트 결과 

- 테스트 환경 ; Laptop Intel i5(4세대), 4 Core, 8G RAM, Windows 8.1 64bit  

- 성능 좋은 결과가 나오기 위해서는 대체적으로 Core 1/2 개수의 Thread에, Core 10배수 정도의 Connection이 무난해 보임 (로컬 환경에서 테스트 할때)

성능 좋은 결과가 나오기 위해서는 대체적으로 Core 개수의 Thread에, Core 20배수 정도의 Connection이 무난해 보임

asb -d 10 -t 2 -c 40 http://localhost/index.html

> Start and Running 10s (2015-03-18.17:36:10)

  http://localhost/index.html

    40 connections and 2 Threads

> Duration         : 10000ms

    Latency        : 0.02ms

    Requests       : 426149

    Response       : 426109

    Transfer       : 147.11MB

> Per seconds

    Requests/sec   : 42610.90

    Transfer/sec   : 14.71MB

> Response Status

    HTTP/1.1 200   : 426109


# Windows 에서 최대 Connection 만들기 

- OS의 소켓 최대 포트 번호를 늘림 (초기값 : 1500, 변경: 50000)

   HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters 의 MaxUserPort 값 변경  

https://support.microsoft.com/ko-kr/kb/196271?wa=wsignin1.0

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters

편집 메뉴에서 새로 만들기를 클릭하고 다음 레지스트리 항목을 추가합니다.

값 이름: MaxUserPort

값 형식: DWORD

값 데이터: 65534

유효 범위: 5000-65534(십진수)

기본값: 0x1388(십진수 5000)

설명: 이 매개 변수는 프로그램이 시스템에서 사용 가능한 사용자 포트를 요청할 때 사용되는 최대 포트 번호를 제어합니다. 일반적으로 수명이 짧은 임시(ephemeral) 포트에는 1024에서 5000 사이의 값이 할당됩니다. 보안 공지 MS08-037 릴리스 후 Windows Server 2003의 동작이 Windows Server 2008 및 Windows Vista의 동작과 보다 근접하게 일치하도록 변경되었습니다. Microsoft 보안 공지 MS08-037에 대한 자세한 내용은 다음 문서 번호를 클릭하여 Microsoft 기술 자료 문서를 참조하십시오.

- IIS의 응용프로그램의 큐 길이를 늘림 (초기값 : 1000, 변경: 3000), Worker Thread 개수 늘림 (초기값: 1, 변경: 2)

- IIS의 경우, 경로를 http://localhost/ 이렇게 줘서 Default 파일을 찾는 과정이 생각보다 느림 (20% 성능)

- 현재 조건(랩탑)에서는 최대 26,000 연결 정도 까지 가능하고 27,000 연결 이상 테스트 하면 문제가 발생함 (리소스가 부족한 것으로 추측)

- http://sockettools.com/kb/maximum-socket-connections/

asb -d 20 -t 3 -c 26000 http://localhost/index.html
> Start and Running 20s (2015-03-18.18:14:41)
  http://localhost/index.html
    26000 connections and 3 Threads
> Duration         : 20038ms
    Latency        : 0.09ms
    Requests       : 239424
    Response       : 213426
    Transfer       : 76.26MB
> Per seconds
    Requests/sec   : 10671.30
    Transfer/sec   : 3.81MB
> Response Status
    HTTP/1.1 200   : 213426




# Ubuntu 에서 최대 Connection 만들기 
- 테스트 환경 : Ubuntu 14.04 on hyper-v , (4 core, 1G RAM)
- 현재 상태 확인 : ulimit -a       
- OS의 소켓(파일) 개수를 늘려 줌 (/etc/security/limits.conf 파일 수정)
# 추가 
*               soft    nofile            65535
*               hard    nofile            65535
*               soft    nproc            65535
*               hard    nproc            65535
 
- nginx 설정 수정 
# nginx.conf 
worker_processes auto;
worker_rlimit_nofile 100000;
 
events {
    # worker_connections 1024;
    # 1024 x 16 
    worker_connections 16384;
    use epoll;
    multi_accept on;
}

# sites-available/default
# 모니터링용 페이지 노출 
location /nginx_status {
# Turn on stats
stub_status on;
access_log   off;
# only allow access from 192.168.1.5 #
# all
allow all;
}

- 클라이언트의 운영방식 인지, 서버의 운영방식 때문인지는 모르겠지만 4000 이상은 안올라 가는 것 같음 ( wrk 도 비슷한 결과 )
- 가상머신 임에도 불구하고, 30,000 연결 까지는 가능한 듯 (nginx 작업 프로세스 수 변경). 물론 처리량은 형편없이 떨어지지만.. 
- 이론상 서버 연결 가능한 수 :  worker_connections x worker_processes = 16384 x 4 = 65536
asb$ asb -d 20 -t 3 -c 30000 http://localhost/index.html
> Start and Running 20s (2015-03-18.14:50:05)
  http://localhost/index.html
    30000 connections and 3 Threads
> Duration         : 68805ms
    Latency        : 2.49ms
    Requests       : 56867
    Response       : 27585
    Transfer       : 28.10MB
> Per seconds
    Requests/sec   : 1379.25
    Transfer/sec   : 1.40MB
> Response Status
    HTTP/1.1 200   : 27585



# http://localhost/nginx_status 
Active connections: 30002 
server accepts handled requests
 30010 30010 36553 
Reading: 0 Writing: 1 Waiting: 30001 


Bonobo - Simple git server for Windows

- Git 서버를 윈도우즈 환경에서 운영하기 위한 플랫폼 

- IIS 7 이상, .NET Framework 4.5, ASP.NET MVC 4 환경 



# 설치 

- IIS 설치 : Windows 7 이상이면 "프로그램 및 기능" - "Windows 기능 겨키/끄기" 에서 설치 가능 

  * 응용 프로그램 개발 기능에서 .NET 4.5 확장성 및 ASP.NET 4.5 활성화 

- .NET Framework 4.5 설치 (Windows 8.1 이상은 이미 설치 됨) 

   https://www.microsoft.com/ko-KR/download/details.aspx?id=30653

- ASP.NET MVC 4 (http://www.asp.net/mvc/mvc4)


- Bonobo Git Server 다운로드 후, 압축을 풀고 해당 디렉토리를 IIS 응용프로그램으로 가상디렉토리 설정 

- 해당 디렉토리를 IIS 계정 (IIS_IUSER) 쓰기 권한 부여 

- 필요하다면 ASP.NET의 IIS 등록 

  (관리자 권한 : %windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe -ir )


Github : https://github.com/philsquared/Catch

Tutorial : https://github.com/philsquared/Catch/blob/master/docs/tutorial.md


# 특징

- CppUnit, Google Test, Boost.Test 등등 다른 프레임웍크에 비해 의존성이 없는 헤더파일(catch.hpp) 하나면 사용 할 수 있음 

  * C++ Test Framework 비교 

- 별도 빌드 모듈로 운영 할 수도 있음

- 크로스 플랫폼 지원


# 사용 

- TEST_CASE 라는 기본 단위 테스트에 SECTION이라는 작은 부분으로 나누어서 테스트 할 수 있음

- 기본적으로 REQUIRE 와 CHECK 의 평가식을 이용하여 로직 검증. 

- REQUIRE 와 CHECK 차이점은, REQUIRE는 평가 실패 시 진행을 중지하고 다음 SECTION 이나 TEST_CASE 로 이동하는 반면, CHECK는 평가 결과와 관계없이 바로 다음 평가를 실행 

- REQUIRE_FALSE, CHECK_FALSE - 실패 평가 (NOT 조건)  

- REQUIRE_THROWS, CHECK_THROWS - 예외가 발생하면 성공 

- REQUIRE_NOTHROW, CHECK_NOTHROW - 예외가 발생하지 않으면 성공 

- INFO, WARN, FAILD - 각 실패 단계에 따른 메세지 출력

- CAPTURE - 주어진 식과 값 출력 (테스트 실패시 노출)

#define CATCH_CONFIG_MAIN - 메인함수 자동으로 생성 및 실행 


# 결론

- 진입장벽 낮으며 사용하기 편리하고 다른 테스트 프레임워크 만큼 많은 기능 제공하는듯

- 살짝 아쉬운점은 출력이 좀더 반듯? 하게 나왔으면 하는 생각. 


# 예제

 
#include <iostream>
#include <string>
#include <memory>
using namespace std;
 
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
 
TEST_CASE("std::string", "test")
{
string s = "abcedf";
 
REQUIRE(s.capacity() > 0);
 
SECTION("TEST 1") {
REQUIRE(s.length() > 0);
REQUIRE(s[0] == 'a');
string ss(move(s));
CAPTURE(ss);
 
CAPTURE(s.capacity());
REQUIRE(s.capacity() == 0); // FAILED
REQUIRE(ss.length() > 0);
}
 
s = "ab";
 
SECTION("TEST 2") {
REQUIRE_THROWS([]{ throw "error"; }());
REQUIRE_THROWS(s.at(5) = 'e');
REQUIRE_NOTHROW(s.at(0) = 's');
 
}
}
 
 
TEST_CASE("std::shared_ptr", "test")
{
auto sp = std::make_shared<string>("init");
CHECK(sp);
 
auto sp2 = sp;
CHECK(sp.use_count() == 2);
 
weak_ptr<string> wp = sp;
CHECK(sp.use_count() == 2);
 
SECTION("WeakPtr 1") {
auto spp = wp.lock();
CAPTURE(sp.use_count());
CAPTURE(spp.use_count());
CHECK(spp.use_count() == 3);
}
 
sp.reset();
sp2.reset();
 
SECTION("WeakPtr 2") {
auto spp = wp.lock();
CAPTURE(sp.use_count());
CAPTURE(spp.use_count());
CHECK(spp); // FAILED
}



# 결과




'Dev > C++' 카테고리의 다른 글

cdecl/asb 개발 중..  (0) 2015.04.01
cdecl/asb - Http benchmarking and load test tool for windows, posix  (0) 2015.03.18
C++ REST SDK (casablanca) 간단 샘플  (0) 2015.02.17
[C++11] Variadic template  (0) 2014.11.02
boost 설치  (0) 2013.11.20

2021년 신규 작성 

C++ REST SDK(cpprestsdk) Sample

 

C++ REST SDK(cpprestsdk) Sample

Introduction https://github.com/Microsoft/cpprestsdk Microsoft에서 만든 클라이언트, 서버용 C++ HTTP 통신 모듈이며, JSON URI, 비동기, 웹소켓, oAuth 등을 지원 C++11의 비동기, 병렬 프로그램 모델 지원 크로스 플랫

cdecl.github.io

--

 

https://casablanca.codeplex.com/

 

The C++ REST SDK is a Microsoft project for cloud-based client-server communication in native code using a modern asynchronous C++ API design. This project aims to help C++ developers connect to and interact with services.

 

What's in the SDK:

  • Features - HTTP client/server, JSON, URI, asynchronous streams, WebSockets client, oAuth
  • PPL Tasks - A powerful model for composing asynchronous operations based on C++ 11 features
  • Platforms - Windows desktop, Windows Store, Windows Phone, Ubuntu, OS X, iOS, and Android
  • Support for Visual Studio 2012, 2013, and 2015 with debugger visualizers
  • NuGet package with binaries for Windows and Android platforms

 

- Microsoft에서 만든 클라이언트, 서버용 C++ HTTP 통신 모듈이며, JSON URI, 비동기, 웹소켓, oAuth 등을 지원 

- C++11의 비동기, 병렬 프로그램 모델 지원

- 크로스 플랫폼 지원 등..

- XML 파서도 지원 해줬으면 하는 아쉬움이...

 

 

# 설치방법

https://casablanca.codeplex.com/documentation

- openssl 및 boost 라이브러리 필요 

- 리눅스(g++)는 소스를 다운 받아 CMake를 이용하여 make 파일 생성, 빌드 및 설치 

  ** 설치시 Release/include/cpprest/details/http_constants.dat 파일이 누락이 되어 수동으로 복사 해줌 (Ubuntu 14.04)

- 윈도우(VC++)는 프로젝트 파일 포함, VS로 열어서 컴파일 하면 됨. 

  그러나 그냥 Nuget 패키지로 설치하면 바이너리와 include 파일을 쉽게 받음.

- 기본적으로 동적 링크(dll, so) 파일로 생성 

 

 

# 간단 예제 소스 

- U("") 는 _T("") 와 비슷한, UNICODE 및 MBCS 환경의 문자열 타입을 스위칭 해주는 매크로. 

  허나 U는 _T와는 다르게 _WIN32 환경이면 기본으로 _UTF16_STRINGS으로 정의되어 있어 프로젝트의 문자집합의 세팅과 관계 없이 UNICODE와 같은 환경으로 동작 

  리눅스 환경에서는 다른 설정을 해주지 않는 이상 char, std::string으로 정의 

utility::string_t은 std::string과 std::wstring을 스위칭 해주는 타입 

- 대체적인 패턴은, 동기는 .get(), 비동기는 then().wait() 조합으로 사용

 

 

#include <iostream>
using namespace std;
 
#include <cpprest/http_client.h>
#include <cpprest/filestream.h>
 
#pragma comment(lib, "cpprest120_2_4") // windows only
using namespace utility; // Common utilities like string conversions
using namespace web; // Common features like URIs.
using namespace web::http; // Common HTTP functionality
using namespace web::http::client; // HTTP client features
using namespace concurrency::streams; // Asynchronous streams
 
 
void GetHttp()
{
http_client client(U("http://en.cppreference.com/w/"));
auto resp = client.request(U("GET")).get();
 
wcout << U("STATUS : ") << resp.status_code() << endl;
wcout << "content-type : " << resp.headers().content_type() << endl;
wcout << resp.extract_string(true).get() << endl;
}
 
void GetHttpAsync()
{
http_client client(U("http://en.cppreference.com/w/"));
 
client.request(U("GET")).then([](http_response resp){
wcout << U("STATUS : ") << resp.status_code() << endl;
wcout << "content-type : " << resp.headers().content_type() << endl;
 
resp.extract_string(true).then([](string_t sBoby){
wcout << sBoby << endl;
}).wait();
 
}).wait();
 
}
 
 
void GetJson()
{
http_client client(U("http://date.jsontest.com/"));
 
http_request req(methods::GET);
 
client.request(req).then([=](http_response r){
wcout << U("STATUS : ") << r.status_code() << endl;
wcout << "content-type : " << r.headers().content_type() << endl;
 
//{
// "time": "11:25:23 AM",
// "milliseconds_since_epoch" : 1423999523092,
// "date" : "02-15-2015"
//}
 
r.extract_json(true).then([](json::value v) {
wcout << v.at(U("date")).as_string() << endl;
wcout << v.at(U("time")).as_string() << endl;
}).wait();
 
}).wait();
 
}
 
int main(int argc, char* argv[])
{
wcout.imbue(locale("kor")); // windows only
 
GetHttp();
GetHttpAsync();
GetJson();
 
return 0

}

 

 

# Variadic template
  - template 에서 타입의 인자를 가변으로 처리 할 수 있는, 새로운 ... 표현식이이 생겼다 

  - http://en.wikipedia.org/wiki/Variadic_template 


  - 그리고 또 다른 설명인 MSDN의 링크 
     
http://msdn.microsoft.com/ko-kr/library/dn439779.aspx

variadic 템플릿 함수를 통합하는 대부분의 구현은 일부 양식의 재귀를 사용하지만 전통적인 재귀와는 약간 다릅니다. 전통적인 재귀는 같은 서명을 사용하여 자신을 호출하는 함수를 포함합니다. (오버로드하거나 템플릿화할 수 있지만 매번 동일한 시그너처가 선택됩니다.) Variadic 재귀에는 다른 수의 인수(거의 항상 줄어듦)를 사용하여 variadic 함수 템플릿을 호출하는 작업이 포함되므로 매번 다른 서명을 사용하지 않게 됩니다. "기본 사례"는 여전히 필요하지만 재귀적 특성은 다릅니다.

그렇다 대부분 재귀로 구현을 하게끔 되어 있고, 메타 프로그램의 기법과 같다.


  - template <typename...Ty> 라는 형식으로 사용 되며 이렇게 사용된 형식은 가변적으로(0~N개) 파라미터를 받아 드릴수 있다

  - 이렇게 받은 파라미터는 재귀함수 형식으로 다시 자기 함수를 호출 (Ty...var) 하게 되며, 파라미터가 앞에서 부터 하나씩 제거 되는 형식으로 다음 함수에 전달 되게 된다

  - 그리고 마지막 end 조건되 되는 별도의 비가변 template 함수를 호출함으로서 종료 되게 끔 동작 함 

 

# 예제 
  - 첫번째로 만들어 본 예제는 vector에 모들 가변 파라미터를 넣어 보는것
  - 재귀 개념으로 실행 되며 마지막에 실행 되는 end 역활로 비가변 템플릿 함수가 실행 되고 끝냄
  - vector의 성능 향상을 위해 C++11 에 포함된 rvalue 레퍼런스를 이용한 move semantics 를 사용하고 마지막 조건 부터 역으로 push 되는 방식을 사용하여 데이터가 template 전달 역순으로 배열 됨.

  - 정상적인 순으로 받아드리게 할려면 별도의 파라미터 vector<T> 로 받아서 넣게 하면 될듯.

  - vector를 사용하여 구조상 한개의 타입으로 한정 지을 수 밖에(다른 타입을 대입하면 vector에서 에러 분출..) 없지만 variant 타입을 사용 한다면 문제가 없고, C++11의 구현으로 실제 std::tuple의 경우 Variadic template으로 구현 되어 있음 


#include <iostream> #include <vector> #include <string> using namespace std; template <typename Ty> vector<Ty> MakeVector(const Ty& p) { vector<Ty> v; v.push_back(p); return std::move(v); } template <typename Ty, typename...VARS> vector<Ty> MakeVector(const Ty& p, VARS...vs) { vector<Ty> v = MakeVector(vs...); v.push_back(p); return std::move(v); } int main() { auto v = MakeVector(1, 3, 5, 7, 9, 11, 13); for (int &n : v) { cout << n << endl; } }


# 결과 (재귀의 역순으로 push 를 해서 역으로 결과가 나옴)

13
11
9
7
5
3
1





# 응용
  - C++에서 COM 을 사용하기 위한 방법 중, 디스패치 인터페이스를 사용하기 위해서는 약간의 수고가 필요한데 그것을 좀 더 편하게 사용하게 사용하기 위한 클래스를 만든 적이 있다. 그 클래스는 동적인 파라미터를 처리하기 위해 40개의 오버로딩으로 처리한 이력이 있음. ( 이것 때문이었을까?, VB 6 의 경우 파라미터 개수가 30개가 MAX 인듯)
  - 물론 printf 류 함수에서 사용하는 것을 사용 할 수 있지만, 이것은 타입을 정확히 알아야 하고 안정성 측면에 보장 살 수가 없음.
  - 기존 구현은 ATL의 _variant_t 를 사용하여 40개의 오버로딩 까지 구현 했으나, C++11 을 새로운 기능인 Variadic template을 이용하여 2개 오버로딩으로 끝냄.

  - _variant_t 특성상 여러 타입을 대입 할 수 있어 vector<_variant_t> 가 템플릿 가변 인수를 처리 할 수 있음


# COMDispatchV.h


// COMDispatch.h: interface for the IDispatch class.
//////////////////////////////////////////////////////////////////////
 
#ifndef _DISPATCH_H_20011012_BY_CDECL_
#define _DISPATCH_H_20011012_BY_CDECL_
 
#include <comdef.h>
#include <vector>
 
 
#define BYREF(x) GLASS::COMDispatch::ToRefParam((x))
 
namespace GLASS {
 
 
class COMDispatch
{
public:
typedef std::vector<_variant_t> vec_param;
typedef _variant_t PT;
 
// Proxy Class
class Proxy
{
public:
void SetParent(COMDispatch *p) { pDisp_ = p; }
void SetPropName(const _bstr_t &bstrName) { bstrName_ = bstrName; }
 
_variant_t operator=(const _variant_t &var) const
{
pDisp_->PutProperty(bstrName_, var);
return var;
}
 
operator _variant_t() const {
return pDisp_->GetProperty(bstrName_);
}
 
_variant_t operator() ()
{
vec_param vecParams;
return pDisp_->Call(bstrName_, vecParams);
}
 
template <typename...VARS>
_variant_t operator() (VARS...param)
{
vec_param vecParams = Param(param...);
return pDisp_->Call(bstrName_, vecParams);
}
private:
 
template <typename Vartype>
vec_param Param(Vartype p)
{
vec_param v;
v.push_back(p);
 
return std::move(v);
}
 
template <typename Vartype, typename...VARS>
vec_param Param(Vartype p, VARS...vs)
{
vec_param v = Param(vs...);
v.push_back(p);
 
return std::move(v);
}
 
private:
COMDispatch *pDisp_;
_bstr_t bstrName_;
};
 
public:
COMDispatch() : spDispatch_(NULL) { proxy_.SetParent(this); }
COMDispatch(const COMDispatch &disp) : spDispatch_(disp.spDispatch_) { proxy_.SetParent(this); }
explicit COMDispatch(const _variant_t &var) : spDispatch_(var) { proxy_.SetParent(this); }
COMDispatch(const char *) = delete;
COMDispatch(const wchar_t *) = delete;
virtual ~COMDispatch() { Release(); }
 
COMDispatch& operator=(const COMDispatch &disp);
COMDispatch& operator=(const _variant_t &var);
 
public:
_variant_t Call(const _bstr_t &bstrMethodName, vec_param &vecParams);
 
void PutProperty(const _bstr_t &strPropName, const _variant_t &var, bool bRef = false);
_variant_t GetProperty(const _bstr_t &strPropName);
 
virtual Proxy& operator[](const _bstr_t &bstrPropName);
 
virtual void CreateObject(const _bstr_t &bstrProgID);
virtual void Release();
 
public:
virtual void GetIDsOfNames(const _bstr_t &bstrMethodName, DISPID &lDispID);
 
protected:
virtual void Invoke(const DISPID lDispID, vec_param &vecParams, _variant_t &varRetVal, 
const WORD wFlags = DISPATCH_METHOD);
 
public:
static _variant_t ToRefParam(_variant_t &var);
 
protected:
Proxy proxy_;
IDispatchPtr spDispatch_;
};
 
 
 
 
 
//////////////////////////////////////////////////////////////////////
// Dispatch implementation
 
COMDispatch& COMDispatch::operator=(const COMDispatch &disp)
{
if (this != &disp) {
spDispatch_ = disp.spDispatch_;
proxy_.SetParent(this);
}
 
return *this;
}
 
 
COMDispatch& COMDispatch::operator=(const _variant_t &var)
{
spDispatch_ = var;
proxy_.SetParent(this);
 
return *this;
}
 
void COMDispatch::Release()
{
if (spDispatch_ != NULL) {
spDispatch_.Release();
}
}
 
void COMDispatch::CreateObject(const _bstr_t &bstrProgID)
{
HRESULT hr = spDispatch_.CreateInstance((LPCSTR)bstrProgID);
 
if (FAILED(hr)) {
_com_raise_error(hr);
}
}
 
_variant_t COMDispatch::Call(const _bstr_t &bstrMethodName, vec_param &vecParams)
{
DISPID lDispID;
GetIDsOfNames(bstrMethodName, lDispID);
 
_variant_t varRetVal;
Invoke(lDispID, vecParams, varRetVal);
 
return varRetVal;
}
 
 
COMDispatch::Proxy& COMDispatch::operator[](const _bstr_t &bstrPropName)
{
proxy_.SetPropName(bstrPropName);
return proxy_;
}
 
 
void COMDispatch::PutProperty(const _bstr_t &strPropName, const _variant_t &var, bool bRef)
{
DISPID lDispID;
GetIDsOfNames(strPropName, lDispID);
 
vec_param vecParams;
vecParams.push_back(var);
 
_variant_t varRetVal;
Invoke(lDispID, vecParams, varRetVal, bRef ? DISPATCH_PROPERTYPUTREF : DISPATCH_PROPERTYPUT);
}
 
_variant_t COMDispatch::GetProperty(const _bstr_t &strPropName)
{
DISPID lDispID;
GetIDsOfNames(strPropName, lDispID);
 
vec_param vecParams;
 
_variant_t varRetVal;
Invoke(lDispID, vecParams, varRetVal, DISPATCH_PROPERTYGET);
 
return varRetVal;
}
 
 
void COMDispatch::GetIDsOfNames(const _bstr_t &bstrMethodName, DISPID &lDispID)
{
BSTR bstrMN = static_cast<BSTR>(bstrMethodName);
 
HRESULT hr = spDispatch_->GetIDsOfNames(IID_NULL, &bstrMN, 1, LOCALE_SYSTEM_DEFAULT, &lDispID);
 
if (FAILED(hr)) {
_com_raise_error(hr);
}
}
 
 
void COMDispatch::Invoke(const DISPID lDispID, vec_param &vecParams, _variant_t &varRetVal, const WORD wFlags)
{
DISPPARAMS dispparams = { 0 };
DISPID dsipid_put = DISPID_PROPERTYPUT;
 
if (DISPATCH_PROPERTYGET != wFlags) {
dispparams.cArgs = vecParams.size();
dispparams.rgvarg = (vecParams.size() == 0) ? NULL : &vecParams.front();
 
if ((DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF) & wFlags) {
dispparams.rgdispidNamedArgs = &dsipid_put;
dispparams.cNamedArgs = 1;
}
}
 
HRESULT hr = spDispatch_->Invoke(
lDispID,
IID_NULL,
LOCALE_USER_DEFAULT,
wFlags,
&dispparams,
DISPATCH_PROPERTYPUT == wFlags ? NULL : &varRetVal,
NULL,
NULL
);
 
if (FAILED(hr)) {
_com_raise_error(hr);
}
}
 
 
_variant_t COMDispatch::ToRefParam(_variant_t &var)
{
_variant_t varRet;
varRet.vt = VT_BYREF | VT_VARIANT;
varRet.pvarVal = &var;
 
return varRet;
}
 
 
} // end namespace
 
  

#endif // _DISPATCH_H_20011012_BY_CDECL_



# Mybatis 를 콘솔 어플리케이션 환경에서 테스트 

  - 한글소개 : http://mybatis.github.io/mybatis-3/ko/

  - 다운로드 : https://github.com/mybatis/mybatis-3/releases

  - SQL Server용 Microsoft JDBC Driver 4.0 : http://www.microsoft.com/ko-kr/download/details.aspx?id=11774



# mybatis.xml

  - mybatis 의 기본 설정 파일

  - 다중 DB 연결 관리(environment) 및 다중 매퍼파일 등록을 관리 

  - 다중 DB 연결 테스트를 위해 같은 DBMS의 DB 만 다른 설정 


<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="Glass"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/> <property name="url" value="jdbc:sqlserver://localhost:1433;DatabaseName=Glass"/> <property name="username" value="user"/> <property name="password" value="pass"/> </dataSource> </environment> <environment id="SecureDB"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/> <property name="url" value="jdbc:sqlserver://localhost:1433;DatabaseName=SecureDB"/> <property name="username" value="user"/> <property name="password" value="pass"/> </dataSource> </environment> </environments> <mappers> <mapper resource="net/cdecl/mapper.xml"/> <mapper resource="net/cdecl/mapper_securedb.xml"/> </mappers> </configuration>



# mapper.xml

  - 매퍼파일

  - 결과매핑 클래스에 대한 정의 및 각 쿼리 및 파라미터, 결과 타입에 대한 정의 

  - 다중 파라미터 및 매핑된 구조(이거 꽤 귀찮을 일 일듯;;) 형태로 결과를 받지 않는다면 HashMap 구조로 처리 가능

  - 각각의 쿼리는 select (혹은 update, delete) 엘러먼트의 id 로 선택을 하며, 디폴트의 경우 이름만 적어줘도 문제 없는듯 하나 mapper namespace 와 더불러 full 경로를 적어 주는 것이 문제가 없어 보임   


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  
<mapper namespace="net.cdecl.CodeDAO">
  
    <resultMap id="result" type="net.cdecl.Code">
        <result property="code" column="code"/>
        <result property="codename" column="codename"/>   
    </resultMap>
 
    <select id="selectAll" resultMap="result">
        SELECT * FROM tCode;
    </select>
    
    <select id="select"  parameterType="String" resultMap="result">
        SELECT * FROM tCode Where code = #{code}
    </select>
    
    
    <select id="select2" parameterType="hashmap" resultMap="result">
        SELECT * 
        FROM tCode 
        Where code In ( #{s1} ) 
    </select>
    
    <select id="selectMap" resultType="hashmap">
        SELECT * FROM tCode;
    </select>
    
    
</mapper>



# mapper_securedb.xml

  - 다른 매퍼파일


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  
<mapper namespace="net.cdecl">
  
    <select id="selectSHA256" parameterType="String" resultType="hashmap">
        select [dbo].[xfn_IntCrypt_SHA1_Encoding](#{in}) as sha1, [dbo].[xfn_IntCrypt_SHA256_Encoding](#{in}) as sha256
    </select>
    
</mapper>




# Code.java

  - tCode 테이블의 데이터 클래스


package net.cdecl;

public class Code {
    private String code;
    private String codename;

    /*
    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getCodeName() {
        return codename;
    }

    public void setCodeName(String codename) {
        this.codename = codename;
    }
    */

    public String toString() {
        return "code: " + code + ", codename: " + codename;
    }
}


# CodeDAO.java

  - 매퍼 클래스와 매퍼 XML 구조를 매핑하는 Data Access Object 


package net.cdecl;

import java.util.ArrayList;
import java.util.Map;

import net.cdecl.*;

public interface CodeDAO {
 
    public ArrayList<Code> selectAll(); // 모든 행 가져오기
    public ArrayList<Code> select(String s);
    public ArrayList<Code> select2(Map<String, Object> m);

}




# MyBaApp.java 

  - 테스트 어플리케이션 


import java.io.IOException; import java.io.InputStream; import java.util.*; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.*; import net.cdecl.*; public class MyBaApp { public static void main(String[] args) { try (SqlSession session = GetSqlSession("Glass")) { session.getConfiguration().getMappedStatementNames(); Mybatis(); Mybatis_HashMap(); } catch (Exception e) { e.printStackTrace(); } } public static SqlSession GetSqlSession(String env) throws IOException { String resource = "net/cdecl/mybatis.xml"; SqlSessionFactory sp = null; try (InputStream inputStream = Resources.getResourceAsStream(resource)) { sp = new SqlSessionFactoryBuilder().build(inputStream, env); } return sp.openSession(); } public static void Mybatis_HashMap() { try (SqlSession session = GetSqlSession("SecureDB") ){ System.out.println("======== SHA256 DB조회"); List list = session.selectList("net.cdecl.selectSHA256", "abcedfg"); System.out.println(list); for (int i = 0; i < list.size(); ++i) { HashMap<String, Object> m = (HashMap<String, Object>)list.get(i); String s = String.format("abcedfg - SHA1 : %s, SHA256 : %s", m.get("sha1"), m.get("sha256")); System.out.println(s); } } catch (Exception ex) { ex.printStackTrace(); } } public static void Mybatis() { try (SqlSession session = GetSqlSession("Glass")) { CodeDAO code = session.getMapper(CodeDAO.class); System.out.println("======== 데이터 매핑 리턴 "); ArrayList<Code> Codes = code.selectAll(); for (Code c : Codes) { System.out.println(c.toString()); } System.out.println("======== 데이터 매핑 리턴, 다중 파라미터(HashMap) "); Map<String, Object> m = new HashMap<String, Object>(); m.put("s1", "0"); Codes = code.select2(m); for (Code c : Codes) { System.out.println(c.toString()); } System.out.println("======== List 리턴 List<HashMap<String, Object>> "); List list = session.selectList("net.cdecl.CodeDAO.selectMap"); System.out.println(list); for (int i = 0; i < list.size(); ++i) { m = (HashMap<String, Object>)list.get(i); String s = String.format("%s %s", m.get("Code"), m.get("CodeName")); System.out.println(s); } } catch (Exception ex) { ex.printStackTrace(); } } }



# 결과 
[select2, selectAll, selectSHA256, select, net.cdecl.CodeDAO.select, net.cdecl.CodeDAO.selectAll, net.cdecl.CodeDAO.select2, selectMap, net.cdecl.CodeDAO.selectMap, net.cdecl.selectSHA256]
======== 데이터 매핑 리턴 
code: 0, codename: zero
code: 1, codename: one
code: 2, codename: two
code: 3, codename: three
code: 4, codename: 4444
======== 데이터 매핑 리턴, 다중 파라미터(HashMap) 
code: 0, codename: zero
======== List 리턴 List<HashMap<String, Object>> 
[{CodeName=zero, Code=0}, {CodeName=one, Code=1}, {CodeName=two, Code=2}, {CodeName=three, Code=3}, {CodeName=4444, Code=4}]
0 zero
1 one
2 two
3 three
4 4444
======== SHA256 DB조회
[{sha1=470e2dac6d8e0b17412cceb91442659a9d561e02, sha256=34541528206d252e76bb2597687112b53aff7f70dd1da1d763dab4c59095bf89}]
abcedfg - SHA1 : 470e2dac6d8e0b17412cceb91442659a9d561e02, SHA256 : 34541528206d252e76bb2597687112b53aff7f70dd1da1d763dab4c59095bf89






'Dev > Java' 카테고리의 다른 글

JVM GC 모니터링(Tomcat), VisualVM 사용  (1) 2016.04.13

SHA1 해쉬 함수의 경우 crypto++ 라이브러리로 사용 가능 하지만, 간단히(?) boost 라이브러리도 가능.

http://www.cryptopp.com/



#include <iostream>
#include <string>
using namespace std;

#include <boost/uuid/sha1.hpp>
#include <boost/format.hpp>
 
 
string ToSHA1(const string s)
{
    boost::uuids::detail::sha1 sh;
    sh.process_bytes(s.c_str(), s.length());

    unsigned int digest[5];
    sh.get_digest(digest);

    string r;
    for(int i = 0; i < 5; ++i) {
        r += str(boost::format("%08x") % digest[i]);
    }

    return r;
}

int main()
{
    
    try {
        cout << ToSHA1("abcedfg") << endl;

    }
    catch (exception &e) {
        cout << e.what() << endl;
    }
    catch (...) {
        cout << "unknown error" << endl;
    }

    return 0;
}


'Dev > Encryption' 카테고리의 다른 글

디피-헬만 키 교환, C++ 예제  (0) 2014.10.17
디피-헬만 키 교환  (0) 2014.10.16
SSL 키 교환 방식  (0) 2014.10.15
해쉬 함수 SHA1CryptoServiceProvider, SHA1Managed 차이  (0) 2014.09.26
Crypto++ 사용하기, 예제  (2) 2009.10.14


# 큰수를 나타내기 위해 boost::multiprecision 의 cpp_int 클래스 이용
http://www.boost.org/doc/libs/1_56_0/libs/multiprecision/doc/html/boost_multiprecision/tut/ints/cpp_int.html


# 큰수를 나타내는 boost 지원 3개의 클래스 비교
http://www.boost.org/doc/libs/1_56_0/libs/multiprecision/doc/html/boost_multiprecision/tut/ints.html


# 소수 판별을 위한 boost::multiprecision 의 miller_rabin_test 함수 사용 


# 밀러 라빈 소수 판별법 
http://ko.wikipedia.org/wiki/%EB%B0%80%EB%9F%AC-%EB%9D%BC%EB%B9%88_%EC%86%8C%EC%88%98%ED%8C%90%EB%B3%84%EB%B2%95


# Random 클래스는 C++11의 표준을 사용함 



#include <iostream>
#include <algorithm>
#include <limits>
#include <random>
#include <sstream>
using namespace std;

#include <boost/format.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/multiprecision/miller_rabin.hpp>
namespace bmp = boost::multiprecision;
typedef bmp::cpp_int INT;


INT RND_INT(int bytes)
{
    random_device rd;   
    std::mt19937 rnd(rd());
    INT r;

    if (bytes < 8) {
        std::uniform_int_distribution<uint32_t> dist(1, numeric_limits<uint32_t>().max());
        r = dist(rnd);
    }
    else {
        std::uniform_int_distribution<uint64_t> dist(1, numeric_limits<uint64_t>().max());
        int loop = bytes / 8;

        for (int i = 0; i < loop; ++i) {
            r |= (INT(dist(rnd)) << (64 * i));
        }
    }

    return r;
}

INT RND_Prime(int bytes)
{
    INT r(0);

    for (int i = 0; i < 1024; ++i) {
        INT n = RND_INT(bytes);

        bool b = bmp::miller_rabin_test(n, 25);
        if (b) {
            r = n;
            cout << "find : " << i << ", prime nmber is " <<  endl;
            cout << str(boost::format("%X") % n) << endl << endl;
            break;
        }   
    }

    return r;
}



void DHC_TEST()
{
    INT P = RND_Prime(16);
    if (P == 0) {
        throw logic_error("DHC_TEST : prime number failed ");
    }
    const INT G = RND_INT(8);

    const INT privateA = RND_INT(8);
    const INT privateB = RND_INT(8);

    cout << "ali private : " << endl <<  str(boost::format("%X") % privateA) << endl;
    cout << "bob private : " << endl <<  str(boost::format("%X") % privateB) << endl;
    cout << endl;

    auto publicA = bmp::powm(G, privateA, P);       
    auto publicB = bmp::powm(G, privateB, P);

    cout << "ali public : " << endl <<  str(boost::format("%X") % publicA) << endl;
    cout << "bob public : " << endl <<  str(boost::format("%X") % publicB) << endl;
    cout << endl;

    cout << "ali block key : " << endl <<  str(boost::format("%032X") % bmp::powm(publicB, privateA, P)) << endl;
    cout << "bob block key : " << endl <<  str(boost::format("%032X") % bmp::powm(publicA, privateB, P)) << endl;

}


int main()
{
    
    try {
        DHC_TEST(); 
    }
    catch (exception &e) {
        cout << e.what() << endl;
    }
    catch (...) {
        cout << "unknown error" << endl;
    }

    return 0;
}


+ Recent posts