目录
【5】搭建etcd服务端,本文是在 win10 下启动etcd服务端
目前市面上满足c/c++的etcd-client-SDK不多,支持win的更寥寥无几,为了实现支持c/c++访问etcd的跨平台和快速移植的 sdk包,本博文在源码cetcd包基础上做进一步调整,实现满足win/linux支持静态编译的etcd-c-client-SDK包。
首先开源cetcd源码下载网址:https://gitee.com/mirrors/cetcd
该源码包已经实现了linux下可编译动静态库,但需要动态链接第三方库。下面给出具体改造过程:
【1】下载源码
1)下载开源cetcd源码,进入该目录,查看README.md文件,其说明依赖curl库和yajl第三库,创建一个新的子目录third-party,
下载curl:https://curl.haxx.se/download.html,到该子目录,本文下载:curl-7.70.0.zip
下载yajl:GitHub - lloyd/yajl: A fast streaming JSON parsing library in C.,到该子目录,本文下载:yajl-2.1.0.tar.gz
在本目录解压。
【2】编译准备
本文编译需要cmake工具,而curl需要3.0以上的cmake版本支持,确保win/linux的cmake支持3.0以上的版本
本文win安装了cmake-3.8.1版本,linux是在centos7上编译,更新cmake如下:
wget https://cmake.org/files/v3.3/cmake-3.3.2.tar.gz
tar xzvf cmake-3.3.2.tar.gz
cd cmake-3.3.2
./bootstrap
gmake
#su root
gmake install
#remove old and new config
yum remove cmake -y
ln -s /usr/local/bin/cmake /usr/bin/
cmake --version
【3】win编译第三方依赖curl和yajl
本文win的编译环境是在win10下安装了vs2015,采用64bit编译
1)编译curl,解压curl-7.70.0.zip,进入 cetcd\third-party\curl-7.70.0\winbuild目录,打开BUILD.WINDOWS.txt,里面有具体的编译选项
本文的编译命令为:
nmake /f Makefile.vc mode=static VC=14
编译输出结果在:
2)编译yajl
解压yajl-2.1.0.tar.gz,进入cetcd\third-party\yajl-2.1.0子目录,创建build-win64子目录,可以打开BUILDING.win32参考编译选项,
进入build-win64子目录,本文的编译命令:
cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release ..
nmake
编译结果就输出在本目录:
完成了win下的第三方依赖编译。
【4】win下编译cetcd
1)源码调整,回到cectd目录,在cetcd.h开头,我们调整winthread 的支持:
#ifdef __cplusplus
extern "C" {
#endif
#include <curl/curl.h>
#include <stdint.h>
#include "sds/sds.h"
#include "cetcd_array.h"
#define CETCD_VERSION release-v0.0.5
typedef sds cetcd_string;
/**以下调整**/
#ifdef WIN32
typedef HANDLE cetcd_watch_id;
#else
#include <pthread.h>
typedef pthread_t cetcd_watch_id;
#endif
/**以上调整**/
enum HTTP_METHOD {
ETCD_HTTP_GET,
ETCD_HTTP_POST,
ETCD_HTTP_PUT,
ETCD_HTTP_DELETE,
ETCD_HTTP_HEAD,
ETCD_HTTP_OPTION
};
在cetcd.c开头,我们调整winthread 的实现:
调整一:
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <curl/curl.h>
#include <yajl/yajl_parse.h>
//调整
#ifdef WIN32
#else
#include <sys/select.h>
#include <pthread.h>
#endif
调整二,在行数490左右,调整了win-thread的实现:
cetcd_watch_id cetcd_multi_watch_async(cetcd_client *cli, cetcd_array *watchers) {
void **args;
args = calloc(2, sizeof(void *));
args[0] = cli;
args[1] = watchers;
#ifdef WIN32
HANDLE thread =(HANDLE)_beginthread(cetcd_multi_watch_wrapper, NULL, args);
#else
pthread_t thread;
pthread_create(&thread, NULL, (void *(*)(void *))cetcd_multi_watch_wrapper, args);
#endif
return thread;
}
int cetcd_multi_watch_async_stop(cetcd_client *cli, cetcd_watch_id wid) {
(void) cli;
/* Cancel causes the thread exit immediately, so the resouce has been
* allocted won't be freed. The memory leak is OK because the process
* is going to exit.
* TODO fix the memory leaks
* */
#ifdef WIN32
WaitForSingleObject(wid, INFINITE);
#else
pthread_cancel(wid);
pthread_join(wid, 0);
#endif
return 0;
}
在cetcd/example/cetcd_get.c文件小调用方法测试sdk,为了实现win主机和虚拟系统centos7访问在win10 下部署的etcd服务,地址采用win主机的vm 地址:
cetcd_array_append(&addrs, "http://192.168.174.1:2379");
cetcd_client_init(&cli, &addrs);
//以下调整,添加写入
resp = cetcd_set(&cli, "/test","2",100);
if(resp->err) {
printf("error :%d, %s (%s)\n", resp->err->ecode, resp->err->message, resp->err->cause);
}
//以上调整
resp = cetcd_get(&cli, "/test");
if(resp->err) {
printf("error :%d, %s (%s)\n", resp->err->ecode, resp->err->message, resp->err->cause);
}
cetcd_response_print(resp);
cetcd_response_release(resp);
2)由于cetcd仅有linux的Makefile文件,因此全新构建构建CMakeLists.txt文件,使支持win/linux跨平台编译,
返回cetcd目录,创建CMakeLists.txt(已经涵盖了win/linux编译需求),具体如下,注意标注说明的地方
# CMake 最低版本号要求,主要切合curl最低版本要求
cmake_minimum_required(VERSION 3.0)
# 项目信息
project (etcd-api)
#
if(WIN32)
message(STATUS "windows compiling...")
add_definitions(-D_PLATFORM_IS_WINDOWS_)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
# SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8")
set(WIN_OS true)
else(WIN32)
message(STATUS "linux compiling...")
add_definitions( -D_PLATFORM_IS_LINUX_)
set(UNIX_OS true)
set(_DEBUG true)
endif(WIN32)
#库输出目录和测试程序输出
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# 指定源文件的目录,并将名称保存到变量
SET(lib_h
${PROJECT_SOURCE_DIR}/sds/sds.h
${PROJECT_SOURCE_DIR}/cetcd_json_parser.h
${PROJECT_SOURCE_DIR}/cetcd_array.h
${PROJECT_SOURCE_DIR}/cetcd.h
)
SET(lib_cpp
${PROJECT_SOURCE_DIR}/sds/sds.c
${PROJECT_SOURCE_DIR}/cetcd_array.c
${PROJECT_SOURCE_DIR}/cetcd.c
)
#测试程序
SET(get_cpp
${PROJECT_SOURCE_DIR}/examples/cetcd_get.c
)
SET(lsdir_cpp
${PROJECT_SOURCE_DIR}/examples/cetcd_lsdir.c
)
#头文件目录
include_directories("${PROJECT_SOURCE_DIR}")
include_directories("${PROJECT_SOURCE_DIR}/sds")
if (${UNIX_OS})
add_definitions(
"-W"
"-fPIC"
"-Wno-unused-parameter"
"-DCURL_STATICLIB"
"-DHTTP_ONLY"
"-DBUILDING_LIBCURL"
)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -g -O3 -Wall")
include_directories("${PROJECT_SOURCE_DIR}/third-party/curl-7.70.0/include")
include_directories("${PROJECT_SOURCE_DIR}/third-party/yajl-2.1.0/build-linux/yajl-2.1.0/include")
link_directories(
"${PROJECT_SOURCE_DIR}/third-party/curl-7.70.0/build-linux/lib"
"${PROJECT_SOURCE_DIR}/third-party/yajl-2.1.0/build-linux/yajl-2.1.0/lib"
"${PROJECT_SOURCE_DIR}/lib"
"/usr/local/lib64"
)
# 指定生成目标
add_library(etcd-api STATIC ${lib_h} ${lib_cpp})
# 指定生成目标
add_executable(etcd-test ${lib_h} ${get_cpp})
#注意静态编译下,需要特定指定依赖的一些通用库,否则会链接失败
target_link_libraries(etcd-test
-lpthread -lcrypto -lssl -lz
etcd-api.a
libyajl_s.a
libcurl.a
)
endif()
if (${WIN_OS})
include_directories("${PROJECT_SOURCE_DIR}/third-party/curl-7.70.0/include")
include_directories("${PROJECT_SOURCE_DIR}/third-party/yajl-2.1.0/build-win64/yajl-2.1.0/include")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4819")
#注意需要一些特定的定义
add_definitions(
"-D_CRT_SECURE_NO_WARNINGS"
"-D_WINSOCK_DEPRECATED_NO_WARNINGS"
"-DNO_WARN_MBCS_MFC_DEPRECATION"
"-DWIN32_LEAN_AND_MEAN"
"-D_CRT_NONSTDC_NO_DEPRECATE"
"-D_CRT_SECURE_NO_WARNINGS"
"-DCURL_STATICLIB"
"-DHTTP_ONLY"
"-DBUILDING_LIBCURL"
)
link_directories(
"${PROJECT_SOURCE_DIR}/third-party/curl-7.70.0/builds/libcurl-vc14-x64-release-static-ipv6-sspi-winssl/lib"
"${PROJECT_SOURCE_DIR}/third-party/yajl-2.1.0/build-win64/yajl-2.1.0/lib"
"${PROJECT_SOURCE_DIR}/lib"
)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
# 指定生成目标
add_library(etcd-apid STATIC ${lib_h} ${lib_cpp})
# 指定生成目标
add_executable(etcd-testd ${lib_h} ${get_cpp})
#注意静态编译下,需要特定指定依赖的一些通用库
target_link_libraries(etcd-testd
Crypt32.lib Normaliz.lib ws2_32.lib winmm.lib wldap32.lib
libcurl_a.lib
yajl_s.lib
etcd-apid.lib
)
else(CMAKE_BUILD_TYPE)
# 指定生成目标
add_library(etcd-api STATIC ${lib_h} ${lib_cpp})
# 指定生成目标
add_executable(etcd-test ${lib_h} ${get_cpp})
target_link_libraries(etcd-test
Crypt32.lib Normaliz.lib ws2_32.lib winmm.lib wldap32.lib
libcurl_a.lib
yajl_s.lib
etcd-api.lib
)
endif (CMAKE_BUILD_TYPE)
endif()
3)在cetcd目录,创建build-win64子目录,进入该目录
cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release ..
#cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Debug ..
nmake
【5】搭建etcd服务端,本文是在 win10 下启动etcd服务端
先下载:Releases · etcd-io/etcd · GitHub
解压后进入etcd-v3.4.7-windows-amd64目录,
先构建配置文件conf.yml:
listen-client-urls: http://127.0.0.1:2379,http://192.168.174.1:2379
advertise-client-urls: http://127.0.0.1:2380,http://192.168.174.1:2380
enable-v2: true
然后在命令行启动:
etcd.exe --config-file conf.yml
如果需要搭建win下开机启动的etcd服务,请参考本人另一篇博文:
win10下搭建etcd的开机启动服务_py_free的博客-CSDN博客
【6】 win的客户端测试
进入cetcd/bin目录下,启动etcd-test.exe,程序逻辑为先向服务put数据,然后又get数据,具体输入结果类似下图:
【7】 linux静态编译第三依赖库
1)对于yajl编译,由于本文是采用将源码挂载到虚拟机上,因此编译会无法创建ln错误,因此本文直接取消动态库创建和软连接的编译:
进入cetcd\third-party\yajl-2.1.0目录,打开 CMakeLists.txt,取消了test源码的编译:
ADD_SUBDIRECTORY(src)
#ADD_SUBDIRECTORY(test)
ADD_SUBDIRECTORY(reformatter)
ADD_SUBDIRECTORY(verify)
ADD_SUBDIRECTORY(example)
ADD_SUBDIRECTORY(perf)
INCLUDE(YAJLDoc.cmake)
# a test target
#ADD_CUSTOM_TARGET(test
# ./run_tests.sh ${CMAKE_CURRENT_BINARY_DIR}/test/parsing/yajl_test
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test/parsing)
#ADD_CUSTOM_TARGET(test-api ${CMAKE_CURRENT_SOURCE_DIR}/test/api/run_tests.sh
# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test/api)
进入cetcd\third-party\yajl-2.1.0\src目录,打开 CMakeLists.txt,注销动态库、软连接、pkg等编译选项:
SET (SRCS yajl.c yajl_lex.c yajl_parser.c yajl_buf.c
yajl_encode.c yajl_gen.c yajl_alloc.c
yajl_tree.c yajl_version.c
)
SET (HDRS yajl_parser.h yajl_lex.h yajl_buf.h yajl_encode.h yajl_alloc.h)
SET (PUB_HDRS api/yajl_parse.h api/yajl_gen.h api/yajl_common.h api/yajl_tree.h)
# useful when fixing lexer bugs.
#ADD_DEFINITIONS(-DYAJL_LEXER_DEBUG)
# Ensure defined when building YAJL (as opposed to using it from
# another project). Used to ensure correct function export when
# building win32 DLL.
ADD_DEFINITIONS(-DYAJL_BUILD)
# set up some paths
SET (libDir ${CMAKE_CURRENT_BINARY_DIR}/../${YAJL_DIST_NAME}/lib)
SET (incDir ${CMAKE_CURRENT_BINARY_DIR}/../${YAJL_DIST_NAME}/include/yajl)
#SET (shareDir ${CMAKE_CURRENT_BINARY_DIR}/../${YAJL_DIST_NAME}/share/pkgconfig)
# set the output path for libraries
SET(LIBRARY_OUTPUT_PATH ${libDir})
ADD_LIBRARY(yajl_s STATIC ${SRCS} ${HDRS} ${PUB_HDRS})
#ADD_LIBRARY(yajl SHARED ${SRCS} ${HDRS} ${PUB_HDRS})
#### setup shared library version number
#SET_TARGET_PROPERTIES(yajl PROPERTIES
# DEFINE_SYMBOL YAJL_SHARED
# SOVERSION ${YAJL_MAJOR}
# VERSION ${YAJL_MAJOR}.${YAJL_MINOR}.${YAJL_MICRO})
#### ensure a .dylib has correct absolute installation paths upon installation
IF(APPLE)
MESSAGE("INSTALL_NAME_DIR: ${CMAKE_INSTALL_PREFIX}/lib")
SET_TARGET_PROPERTIES(yajl PROPERTIES
INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib")
ENDIF(APPLE)
#### build up an sdk as a post build step
# create some directories
FILE(MAKE_DIRECTORY ${libDir})
FILE(MAKE_DIRECTORY ${incDir})
# generate build-time source
SET(dollar $)
CONFIGURE_FILE(api/yajl_version.h.cmake ${incDir}/yajl_version.h)
#CONFIGURE_FILE(yajl.pc.cmake ${shareDir}/yajl.pc)
# copy public headers to output directory
FOREACH (header ${PUB_HDRS})
SET (header ${CMAKE_CURRENT_SOURCE_DIR}/${header})
EXEC_PROGRAM(${CMAKE_COMMAND} ARGS -E copy_if_different ${header} ${incDir})
ADD_CUSTOM_COMMAND(TARGET yajl_s POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${header} ${incDir})
ENDFOREACH (header ${PUB_HDRS})
INCLUDE_DIRECTORIES(${incDir}/..)
# at build time you may specify the cmake variable LIB_SUFFIX to handle
# 64-bit systems which use 'lib64'
#INSTALL(TARGETS yajl
# RUNTIME DESTINATION lib${LIB_SUFFIX}
# LIBRARY DESTINATION lib${LIB_SUFFIX}
# ARCHIVE DESTINATION lib${LIB_SUFFIX})
INSTALL(TARGETS yajl_s ARCHIVE DESTINATION lib${LIB_SUFFIX})
INSTALL(FILES ${PUB_HDRS} DESTINATION include/yajl)
INSTALL(FILES ${incDir}/yajl_version.h DESTINATION include/yajl)
#INSTALL(FILES ${shareDir}/yajl.pc DESTINATION share/pkgconfig)
回到third-party\yajl-2.1.0目录,创建build-linux子目录,并进入该目录
cmake ..
make
编译结果就在本目录下:
2)linux编译curl库
进入cetcd\third-party\curl-7.70.0,打开CMakeLists.txt文件,去关掉动态库编译,当然其他选项按实际需要设置即可,如下:
option(CURL_WERROR "Turn compiler warnings into errors" OFF)
option(PICKY_COMPILER "Enable picky compiler options" OFF)
option(BUILD_CURL_EXE "Set to ON to build curl executable." OFF)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
option(ENABLE_ARES "Set to ON to enable c-ares support" OFF)
创建子目录build-linux,进入该目录,编译:
cmake ..
make
#注意本文需要openssl支持,因此需要安装openssl,如果遇到需要升级可以参考本博文另一篇文章:
centos7编译libcoap时openssl版本过低,升级openssl_py_free的博客-CSDN博客_centos7下载openssl版本过低
编译完成,其编译结果libcurl.a 在本目录的lib子目录下。
【8】linux下编译cetcd
进入cetcd目录,由于在win编译时已经创建的CMakeLists.txt涵盖了linux的部分(详见【4】2)文段),因此,直接创建子目录build-linux,并进入该目录编译即可:
cmake ..
make
编译结果如下:
【9】 linux的客户端测试
服务端采用win搭建的etcd服务,win主机地址192.168.174.1,centos7虚拟机的地址192.168.174.130,
进入cetcd/bin目录, 启动etcd-test进行测试
【10】构建etcd-c-sdk包
win/Linux编译完成后,可以将相关头文件和静态库打包,构建出etcd-c-sdk包,具体头文件和库依赖请参考CMakeLists.txt文件,打包好的 SDK就可以放在项目中直接使用。