boost에서 OpenSSL 사용하기 (2)

빌드된 OpenSSL을 프로젝트 설정에 넣고 다시 빌드해본다.

include를 해야한다.

#include <boost/asio/ssl.hpp>

를 하면 ssl 코드들을 가져온다.

라이브러리 설정에는 빌드된 OpenSSL 폴더의 lib 폴더 경로를 포함시켜줘야한다.

 

이렇게하고 빌드를 해보니…

빌드에 오류가 생겼다.

에러 메시지를 구글에 다시 검색해보니,

https://stackoverflow.com/questions/37522654/linking-with-openssl-lib-statically

링크에서 답을 찾을 수 있었다. http://wiki.openssl.org/index.php/Compilation_and_Installation 에 가서 enable-capieng 이라는 옵션에 대해 찾아보라는 얘기였다.

해당 문서에서 enable-capieng 으로 검색해보니

Enables the Microsoft CAPI engine on Windows platforms. Used to access the Windows Certificate Store. Also see Using Windows certificate store through OpenSSL on the OpenSSL developer list.

라는 내용이 있었다.

빌드 옵션에 추가한다.

perl Configure VC-WIN64A –D:\ –openssldir=D:\Library\OpenSSL enable-capieng no-shared no-asm threads

이렇게 하고 다시 openssl을 빌드해본다.

 

이 옵션으로 해결되지 않아서 -d 옵션 추가. -d 옵션을 추가했더니 nmake 과정에서 알 수 없는 옵션이라는 에러메시지가 나오는 것을 확인했다.

 

빌드옵션을 변경했다.

perl Configure VC-WIN64A –prefix=D:\Library\OpenSSL –openssldir=D:\Library\OpenSSL –debug no-shared no-idea no-mdc2 no-rc5 no-asm no-capieng threads enable-ssl-trace enable-static-engine

 

이렇게 빌드했더니 오류가 절반 정도 사라졌다. 나머지 링킹 오류는 라이브러리 파일을 인식하는 위치에서 추가적인 라이브러리 파일을 불러들여야 했다.

.lib 파일을 불러들이는 곳에서 다음을 추가한다. (혹은 프로젝트 설정에서 해도 된다.)

#pragma comment(lib, "advapi32.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "libcrypto.lib")
#pragma comment(lib, "libssl.lib")

이렇게하고 빌드하니 오류 없이 빌드가 되었다.

 

참고했던 자료들

  • https://github.com/openssl/openssl/blob/OpenSSL_1_1_0-stable/INSTALL
  • https://www.lesstif.com/pages/viewpage.action?pageId=6291508
  • http://www.programmersought.com/article/8711142745/
  • https://github.com/libevent/libevent/issues/59

boost에서 OpenSSL 사용하기 (1) : 윈도우에서 OpenSSL 빌드

boost에서 OpenSSL을 사용하기 위해서는 먼저 OpenSSL 설치가 필요하다. boost의 ssl 관련 파일들에서 openssl 관련 파일들을 인클루드해오기 때문이다.

검색엔진을 찾아보다가, https://jethro.tistory.com/entry/CC-Windows에서-OpenSSL-써먹기 를 참고했다.

 

Step 1. ActivePerl 설치

https://www.activestate.com/products/activeperl/ 로 이동해서 ActivePerl 을 설치한다.

 

Step 2. OpenSSL 소스 다운로드

https://www.openssl.org/source/ 에서 소스코드를 다운로드한다. 난 1.1.1j 버전을 다운로드했다.

 

Step 3. Configure 설정

perl Configure VC-WIN32 –openssldir=C:\OpenSSL-x86-debug no-shared no-asm threads

를 입력하라고 하는데 64비트 윈도우이니 VC-WIN64A 로 입력. (VC-WIN64I 도 있긴한데 아마 Itanium 아키텍쳐일 때 쓰라는 것일 것 같다.)

다음과 같은 오류가 발생.

dmake나 nmake가 필요한 것 같다.

안내 받은대로 ppm install dmake 를 입력하면, 이런저런 패키지들을 설치한다.

 

다시 configure 명령어 입력.

perl Configure VC-WIN64A –openssldir=D:\Library\OpenSSL no-shared no-asm threads

별 문제 없이 끝났다.

 

Step 4. make

그런데 참고하고 있는 블로그 글에는 ms\do_ms.bat 파일이 있다고 했는데 그 위치에 해당 파일이 없다.

구글에 검색해보니…

https://stackoverflow.com/questions/39076244/why-there-is-no-ms-do-ms-bat-after-perl-configure-vc-win64a

1.1.0 버전부터는 그 명령어가 없어졌다고 한다. 그냥 nmake 를 입력하라고 되어있다.

nmake를 해보니 그런 명령어가 없다고 한다. 일반 cmd가 아니라 VS2015 x64 네이티브 도구 명령 프롬프트를 다시 실행해야한다.

 

다시 nmake 를 입력하면 빌드가 시작된다.

다 완료된 후 nmake test 를 입력하여 테스트. 테스트도 잘 완료되었다.

nmake install 을 입력했더니…

*** Installing runtime librariesCannot create directory C:/Program Files/OpenSSL: No such file or directoryNMAKE : fatal error U1077: 'C:\Perl64\bin\perl.exe' : '0x2' 반환 코드입니다.Stop.

에러로 중지된다. 으어…

구글에 검색해보니 나만 겪는 문제는 아닌듯하다. github에 이슈로 올라와있다.

https://github.com/openssl/openssl/issues/2034

댓글을 보고 configure 명령어에 prefix를 추가한다.

perl Configure VC-WIN64A –prefix=D:\Library\OpenSSL –openssldir=D:\Library\OpenSSL no-shared no-asm threads

다시 nmake clean, nmake, nmake test 실행.

다시 make install 을 실행하니 제대로 진행되었다.

 

빌드가 성공되면 prefix로 지정된 위치에 파일들이 생성된다.

OpenSSL 빌드는 끝.

boost 1.70.0 버전 업그레이드시 참고사항

boost를 1.60.0 버전을 계속 사용하다가 1.70.0으로 업그레이드하니 변경된 점이 많았다.

https://zepeh.tistory.com/498 를 참고한다.

ASIO에서 io_service가 io_context 로 변경되었다. 하지만 1.70.0 버전을 적용하면 io_service도 그대로 사용할 수 있긴 하다. cpp 파일과 h 파일도 그대로 들어있다. 비주얼스튜디오 인텔리센스에서도 io_context 가 바로 인식되지는 않는데, 이런 경우 전처리기 설정에서 BOOST_ASIO_NO_DEPRECATED 를 추가해주면 io_service 가 비활성화되고 io_context만 사용할 수 있게 된다.

io_context 로 코드를 바꾸고 나면 충돌사항들이 나오게 된다.

 

from_string 변경

from_string() 함수가 삭제되었다.

아래 코드를

boost::asio::ip::address_v4 address = boost::asio::ip::address_v4::from_string(server_ip, error_code);

아래처럼 변경한다.

boost::asio::ip::address_v4 address = boost::asio::ip::make_address_v4(server_ip, error_code);

이 문제도 해결.

 

strand.wrap() 변경

strand의 wrap() 함수도 변경되었다.

다음의 코드를

boost::asio::async_write(socket_,
            boost::asio::buffer(send_queue_.front().first, send_queue_.front().second),
            strand_.wrap(
                boost::bind(
                    &BasicSocket::OnSendHandler,
                    shared_from_this(),
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred
                )
            )
        );

아래처럼 변경한다.

boost::asio::async_write(socket_,
            boost::asio::buffer(send_queue_.front().first, send_queue_.front().second),
            boost::asio::bind_executor(strand_, boost::bind(
                &BasicSocket::OnSendHandler,
                shared_from_this(),
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred
            )
            )
        );

 

이 문제도 해결.

AppVeyor 사용기

Travis-CI 에서 윈도우 빌드가 되지 않아 고민하던 중…

새로운 CI 서비스를 찾았다. AppVeyor인데 앱베요인지 앱비요인지 앱베이요인지 뭐라고 읽어야할지 잘 모르겠다. 이 서비스는 Travis-CI와 같은 리눅스 빌드 외에도 윈도우에서 VisualStudio를 통한 빌드를 지원한다.

Appveyor는 Travis-CI처럼 yml 파일로도 설정해줄 수 있지만 웹사이트에서 바로 설정을 수정할 수 있기 때문에 아무래도 사용하기가 많이 편리했다.

Boost 라이브러리는 프로젝트 속성에 절대경로로 지정하던 것을 윈도우 시스템 환경변수로 뺐고, appveyor 에서는 프로젝트 설정에 환경변수로 넣어주었다.

MySQL 커넥터 라이브러리에서도 충돌이 생겼는데, appveyor 에는 MySQL ODBC 드라이버만 지원하고 있었다. 나는 리눅스에서도 빌드되게 하려고 따로 MySQL-C++-Connector를 사용하고 있다. 일단 이 라이브러리를 솔루션에 포함시키는 것으로 해결했다. 차후에는 ODBC 버전까지 같이 지원하도록 만드는 것이 나을 것 같다.

boost와 mysql을 해결하고 json과 jemalloc 라이브러리가 먼저 빌드되고 그다음 유틸리티 라이브러리, 네트워크 라이브러리 순서로 빌드하기 위해 솔루션 설정에서 프로젝트 종속성을 설정해주었다.

다시 빌드하자….

게임서버, 브릿지서버 둘다 빌드 성공.

확실히 GCC나 G++를 이용하는 빌드보다는 훨씬 쉬운 느낌이다.

MySQL 라이브러리 사용시 config.h 파일 문제

리눅스에서 빌드시 다음과 같은 오류가 있었다.

In file included from /root/DServer2/src/dserver/protocol/../../dserver/database.h:6:0,
from /root/DServer2/src/dserver/protocol/../../dserver/define.h:41,
from /root/DServer2/src/dserver/protocol/base_protocol.h:3,
from /root/DServer2/src/dserver/protocol/base_protocol.cpp:1:
/root/Library/mysql-connector-c++-1.1.9/cppconn/resultset.h:30:20: fatal error: config.h: 그런 파일이나 디렉터리가 없습니다
#include "config.h"
^

config.h 파일이 없다는 에러 메시지인데 cppconn 디렉토리를 살펴보면 config.h.cm 파일이 있다.

mysql 커넥터를 다운 받은 디렉토리로 가서 cmake를 실행한다. 실행이 되지 않는다면 yum 으로 cmake를 설치한다.

[root@localhost Library]# cmake mysql-connector-c++-1.1.9
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Could NOT find Boost
-- Could NOT find Boost
CMake Error at CMakeLists.txt:194 (MESSAGE):
Boost or some of its libraries found. If not in standard place please set
-DBOOST_ROOT:STRING=


-- Configuring incomplete, errors occurred!
See also "/root/Library/CMakeFiles/CMakeOutput.log".
[root@localhost Library]#

https://dev.mysql.com/doc/connector-cpp/en/connector-cpp-installation-source-unix.html 를 참조했다.

https://dev.mysql.com/doc/connector-cpp/en/connector-cpp-source-configuration-options.html 를 읽어봤더니 BOOST 경로를 설정해줘야한다.

/etc/profile 에 BOOST_ROOT 경로를 지정한다.

export BOOST_ROOT=/부스트경로/

다시 cmake를 실행했다.

[root@localhost mysql-connector-c++-1.1.9]# cmake .
-- Boost version: 1.65.1
-- BOOST_INCLUDE_DIRS=/root/Library/boost_1_65_1
-- You will link dynamically to the MySQL client library (set with -DMYSQLCLIENT_STATIC_LINKING=<bool>)
-- Searching for dynamic libraries with the base name(s) "mysqlclient_r mysqlclient"
CMake Error at FindMySQL.cmake:531 (message):
Could not find "mysql.h" from searching "/usr/include/mysql
/usr/local/include/mysql /opt/mysql/mysql/include
/opt/mysql/mysql/include/mysql /usr/local/mysql/include
/usr/local/mysql/include/mysql /MySQL/*/include /MySQL/*/include"
Call Stack (most recent call first):
CMakeLists.txt:252 (INCLUDE)


-- Configuring incomplete, errors occurred!
See also "/root/Library/mysql-connector-c++-1.1.9/CMakeFiles/CMakeOutput.log".
[root@localhost mysql-connector-c++-1.1.9]#

mysql 라이브러리 파일이 있어야한다고 나온다. CentOS 7에서는 MySQL이 빠지고 그대신 MariaDB가 들어가 있기 때문에 mariadb-libs 와 mariadb-devel 을 yum으로 설치해준다.

다시 cmake 하니…

[root@localhost mysql-connector-c++-1.1.9]# cmake .
-- Boost version: 1.65.1
-- BOOST_INCLUDE_DIRS=/root/Library/boost_1_65_1
-- You will link dynamically to the MySQL client library (set with -DMYSQLCLIENT_STATIC_LINKING=<bool>)
-- Searching for dynamic libraries with the base name(s) "mysqlclient_r mysqlclient"
-- mysql_config was found /usr/bin/mysql_config
-- MySQL client environment/cmake variables set that the user can override
--   MYSQL_DIR                   :
--   MYSQL_INCLUDE_DIR           : /usr/include/mysql
--   MYSQL_LIB_DIR               : /usr/lib64/mysql
--   MYSQL_CONFIG_EXECUTABLE     : /usr/bin/mysql_config
--   MYSQL_CXX_LINKAGE           :
--   MYSQL_CFLAGS                : -I/usr/include/mysql
--   MYSQL_CXXFLAGS              : -I/usr/include/mysql

(...중략...)

-- Configuring performance test - statement
-- Configuring bugs test cases - unsorted
-- Configuring unit tests - group template_bug
-- Configuring done
CMake Warning (dev) in driver/CMakeLists.txt:
  Policy CMP0022 is not set: INTERFACE_LINK_LIBRARIES defines the link
  interface.  Run "cmake --help-policy CMP0022" for policy details.  Use the
  cmake_policy command to set the policy and suppress this warning.

  Target "mysqlcppconn" has an INTERFACE_LINK_LIBRARIES property which
  differs from its LINK_INTERFACE_LIBRARIES properties.

  INTERFACE_LINK_LIBRARIES:

    mysqlclient;pthread;z;m;ssl;crypto;dl

  LINK_INTERFACE_LIBRARIES:



This warning is for project developers.  Use -Wno-dev to suppress it.

-- Generating done
-- Build files have been written to: /root/Library/mysql-connector-c++-1.1.9
[root@localhost mysql-connector-c++-1.1.9]#

이제서야 완료. config.h 파일이 정상적으로 생성되었다.

boost::property_tree의 위험성

.ini 파일을 파싱하기 위해 boost::property_tree 클래스를 사용했었다.

서버의 성능을 테스트하던 도중 어디사 CPU를 많이 잡아먹나 계속 테스트했는데 네트워크 전송 부분이 계속 문제였다. 아무리 수정을 해도 고쳐지지 않는 상황. 비주얼스튜디오의 성능프로파일러를 돌려보니 boost::property_tree 가 문제의 원인이었다. 환경설정에서 특정한 값을 가져오기 위해 쓰느 코드가 네트워크 속도를 느리게 만드는데 기여하고 있었다.

이 부분을 수정하고나니 CPU 점유율이 아주 낮아졌다.

boost를 마구 쓰면 안된다는걸 깨닫게되었네.

boost에서 메모리풀 사용

프로그램 구현 중 메모리풀이 필요한 경우가 있어 어떻게 할까 하다가 boost의 메모리풀을 찾아보게 되었다.

boost의 메모리풀은 4가지.

  1. pool
  2. singleton_pool
  3. pool_alloc
  4. object_pool

네가지 메모리풀 중에 어떤 것이 어떤 상황에 가장 적합한지는 많이 써봐야 알 수 있을듯하다. 찾아보니 내가 가지고 있던 몇몇 게임서버 소스는 singleton_pool을 사용하고 있는 케이스가 있었다. 이것부터 먼저 찾아보는 것으로.

singleton_pool은 다른 메모리풀 클래스들과는 다르게 스레드-세이프 특성을 지니고 있어서 가장 유용하게 쓰일 것 같다.

singleton_pool을 아래와 같은 코드로 좀더 편하게 사용 가능. 출처는 ‘아지크의 좌충우돌 IT 이야기’

#include <stdio.h>
#include <iostream>
#include <vector>
#include <boost/pool/singleton_pool.hpp>
#include <boost/pool/pool_alloc.hpp>


template<typename T,  unsigned int NEXT_SIZE = 32U, unsigned int  MAX_POOL_SIZE = 0U>
class CMemoryPoolT
{
public:
     static void* operator new(size_t size)
     {
          return boost::singleton_pool<T,
               sizeof(T),
               boost::default_user_allocator_new_delete,
               boost::details::pool::default_mutex,
               NEXT_SIZE,
               MAX_POOL_SIZE>::malloc();
     }

     static void operator delete(void* p)
     {
          boost::singleton_pool<T,
               sizeof(T),
               boost::default_user_allocator_new_delete,
               boost::details::pool::default_mutex,
               NEXT_SIZE,
               MAX_POOL_SIZE>::free(p);
     }
};


class CMemoryPoolTest  :public CMemoryPoolT<CMemoryPoolTest>
{
public:
     char Dumy[124] ;
};


int _tmain(int argc, _TCHAR* argv[])
{
     using std::vector ;

     CMemoryPoolTest *p = new CMemoryPoolTest() ;

     delete p ;

     printf("Using MemoryPool... \n") ;


     return 0;
}

프로그램 종료시에는 purge_memory()를 꼭 호출해주어야 한다고 한다.

혹은 다음과 같은 방법으로도 사용 가능하다.

struct SEND_BUFFER_TAG {};
typedef boost::singleton_pool<SEND_BUFFER_TAG, SEND_BUFFER_SIZE> SendBufferPool;

사용할 클래스의 헤더파일에서 위와 같이 선언. TAG를 여러개 사용하고 메모리풀마다 다르게 적용하면 서로 다른 메모리풀이 된다.

메모리를 할당 받고 싶다면,

char* send_data = static_cast<char*>(SendBufferPool::malloc()); // 메모리를 할당 받는다.

SendBufferPool::free(send_data); // 다 사용한 메모리를 해제한다.

종료시에는 당연히,

SendBufferPool::purge_memory();

를 호출해야한다.

출처와 참고사이트

 

윈도우에서 C++ Boost 사용하기

윈도우에서 C++ 작업시 유용한 Boost 라이브러리를 사용하기 위해서는 빌드를 해야한다. 파일시스템 등을 다루는 등의 운영체제에 의존하는 기능들을 사용하기 위해 빌드해야한다고 한다.

  1. http://www.boost.org 에 들어가서 Boost를 다운 받는다.
  2. 압축을 풀고 디렉토리에 들어가 bootstrap.bat 를 실행한다.

    2019. 05. 08.  추가
    boost 1.70 이 되면서 bootstrap.bat msvc 등처럼 컴파일러를 명시해서 입력해야 한다.
    지원되는 컴파일 옵션은 다음과 같다.

    Toolsets supported by this script are: borland, como, gcc, gcc-nocygwin, intel-win32, metrowerks, mingw, msvc, vc7, vc8, vc9, vc10, vc11, vc12, vc14, vc141

  3. b2 –help 를 실행해서 한번 도움말을 쭉 읽어본다.
  4. 윈도우 커맨드창을 열고 해당 다음의 명령을 입력하여 빌드를 시작한다. 64비트, 디버그/릴리즈 모두, 멀티쓰레드, MPI 사용 안함으로 빌드한다.
  5. b2 variant=debug,release link=static,shared threading=multi address-model=64 –without-mpi -j8 stage
  6. 빌드 옵션은 여러가지가 더 있다. 찾아봐서 자기가 필요한대로 설정한다.
  7. -j8은 몇개의 작업을 동시에 하는지를 결정한다. 4코어인 경우에는 -j4를 해주면 모든 코어를 다 사용하게 된다. 적당하게 설정하는 것이 좋다.

빌드에는 시간이 꽤 걸린다.

다음의 링크를 참조했다.