zoukankan      html  css  js  c++  java
  • HPSocket介绍与使用

    一、HPSocket介绍

    HP-Socket是一套通用的高性能TCP/UDP/HTTP 通信框架,包含服务端组件、客户端组件和Agent组件,广泛适用于各种不同应用场景的TCP/UDP/HTTP通信系统,提供C/C++、C#、Delphi、E(易语言)、Java、Python等编程语言接口。HP-Socket对通信层完全封装,应用程序不必关注通信层的任何细节;HP-Socket提供基于事件通知模型的API接口,能非常简单高效地整合到新旧应用程序中。

    1.编译

    下载Hp-socket库:

    git  clone  https://github.com/ldcsaa/HP-Socket.git

    这个库有多个系统的版本,我们这里选用linux分析就好了。所以进入到Linux下,看readme大概知道编译流程。

    ./compile.sh 
    sudo ./install.sh

    大概就是分两个脚本,一个编译脚本和一个安装脚本。-h参数可以分别看到他们的使用说明。

    compile.sh脚本

    $ ./compile.sh -h
    Usage: compile.sh [...O.P.T.I.O.N.S...]
    ----------------------+-------------------------------------------------
      -d|--with-debug-lib : compile debug libs (default: true)
      -j|--use-jemalloc   : use jemalloc in release libs
                          : (x86/x64 default: true, ARM default: false)
      -u|--udp-enabled    : enable UDP components (default: true)
      -t|--http-enabled   : enable HTTP components (default: true)
      -s|--ssl-enabled    : enable SSL components (default: true)
      -z|--zlib-enabled   : enable ZLIB related functions (default: true)
      -i|--iconv-enabled  : enable ICONV related functions (default: true)
      -c|--compiler       : compiler (default: g++)
      -p|--platform       : platform: x86 / x64 / ARM
                          : (default: current machine arch platform)  
      -e|--clean          : clean compilation intermediate temp files
      -r|--remove         : remove all compilation target files
      -v|--version        : print hp-socket version
      -h|--help           : print this usage message
    ----------------------+-------------------------------------------------

    大概就是选择某个模块进行编译。

    看看脚本逻辑

    # 1.首先加载script/env.sh脚本里的变量和函数
    source $PACKAGE_PATH/$SCRIPT_DIR/env.sh
    
    # 2.解析参数:设置对应的编译模块的变量值并得到一个控制状态ACTION_NAME
    parse_args "$@"
    
    # 3.显示上一步解析得到的配置结果
    print_config
    
    # 4.不同action的操作
    if [ $EXEC_FLAG -eq 1 ]; then
     do_clean
    elif [ $EXEC_FLAG -eq 2 ]; then
     do_remove
    else
     do_build

    一般情况下我们用的是编译,所以就是do_build函数

    HPSOCKET_LIB_NAME=hpsocket
    HPSOCKET4C_LIB_NAME=hpsocket4c
    do_build()
    {
    	 # 设置一些编译的变量
    	 C_LAN_OPTS="-c -x c -I $DEPT_INC_DIR -Wall -Wswitch -Wno-deprecated-declarations -Wempty-body -Wconversion -Wreturn-type -Wparentheses -Wno-pointer-sign -Wno-format -Wuninitialized -Wunreachable-code -Wunused-function -Wunused-value -Wunused-variable -fno-strict-aliasing -fpic -fvisibility=hidden -fexceptions -std=c11"
    	 CPP_LAN_OPTS="-c -x c++ -I $DEPT_INC_DIR -Wall -Wno-class-memaccess -Wno-reorder -Wswitch -Wno-deprecated-declarations -Wempty-body -Wconversion -Wreturn-type -Wparentheses -Wno-format -Wuninitialized -Wunreachable-code -Wunused-function -Wunused-value -Wunused-variable -fno-strict-aliasing -fpic -fthreadsafe-statics -fvisibility=hidden -fexceptions -frtti -std=c++14"
    	 LINK_OPTS="-Wl,--no-undefined -Wl,-L$DEPT_LIB_DIR -L$DEPT_LIB_DIR -Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack -shared -Wl,-Bsymbolic"
    	 RELEASE_CFG_OPTS="-g0 -O3 -fomit-frame-pointer -DNDEBUG"
    	 DEBUG_CFG_OPTS="-g2 -gdwarf-2 -O0 -fno-omit-frame-pointer -DDEBUG -D_DEBUG"	
    	
    	 if [ -d $HPSOCKET_LIB_TARGET_DIR ]; then
    		  rm -rf $HPSOCKET_LIB_TARGET_DIR
    	 fi
    	  if [ -d $HPSOCKET4C_LIB_TARGET_DIR ]; then
      		rm -rf $HPSOCKET4C_LIB_TARGET_DIR
    	 fi
    	 
    	 # 编译
     	do_compile $HPSOCKET_LIB_NAME $CFG_RELEASE
     	do_compile $HPSOCKET4C_LIB_NAME $CFG_RELEASE
     
     	if [ $WITH_DGBUG_LIB -eq 1 ]; then
     		do_compile $HPSOCKET_LIB_NAME $CFG_DEBUG
      		do_compile $HPSOCKET4C_LIB_NAME $CFG_DEBUG
     	fi
     	update_hp_def
    }	
    
    do_compile()
    {
    	 _LIB_NAME=$1
    	 _CFG_NAME=$2
    	 
    	 parse_compile_args
    	 
    	 do_compile_step_1  
    	 do_compile_step_2
    	 do_compile_step_3
    }
    
    #这个函数大概就是递归编译生成一堆*.o文件
    do_comepile_file()
    {
        	local _CMD="$CC $_LAN_OPTS $_FULL_FILE_NAME $_CFG_OPTS $_CL_OPTS -o $_OBJ_TARGET_DIR/	$_OBJ_NAME"
     	$_CMD
    }
    
    #这个函数连接.o生成.so库
    do_compile_step_2()
    {
    	 local _LIB_FILE_NAME="lib$_LIB_NAME$_LIB_NAME_SUFFIX.so"
    	 local _SONAME_OPT="-Wl,-soname,$_LIB_FILE_NAME.$VER_MAJOR"
    	 local _OBJ_TARGET_DIR=$_LIB_TARGET_DIR/$OBJ_DIR/$_CFG_NAME
    	 local _OBJ_FILES=($(find $_OBJ_TARGET_DIR -name *.o | xargs ls))
    	 local _CMD="$CC -o $_LIB_TARGET_DIR/$_LIB_FILE_NAME $LINK_OPTS $_SONAME_OPT ${_OBJ_FILES[@]} $_LN_OPTS"
    	 
    	 echo "> $_CMD"
    }
    
    #调用 post-link.sh脚本打包.a库
    do_compile_step_3()
    {
    	 local _LIB_FILE_NAME="lib$_LIB_NAME$_LIB_NAME_SUFFIX"
    	 local _LIB_PATH=$PACKAGE_PATH/$_LIB_TARGET_DIR
    	 local _CMD="$SCRIPT_DIR/post-link.sh $_AR_FLAG $_LIB_PATH $_LIB_FILE_NAME $PLATFORM $_CFG_NAME $VER_MAJOR $VER_MINOR $VER_REVISE"
    	 
    	 echo "> $_CMD"
    	 
    	 $_CMD
    	}

    install.sh就不说了。无非就是将库拉倒系统目录或者指定目录,把.h拉到系统目录中去。

    parse_args "$@"
    print_config
    
    if [ $IS_UNINSTALL -eq 0 ]; then
     do_install
    else
     do_uninstall
    fi
    
    ldconfig > /dev/null 2>&1
    
    do_install()
    {
    	 mkdir -p $PREFIX_PATH/$DEST_LIB_DIR
    	 
    	 # copy *.a
     	cp_lib_a $HPSOCKET_LIB_TARGET_DIR
     	cp_lib_a $HPSOCKET4C_LIB_TARGET_DIR
    	
    	 # copy *.so
     	cp_lib_so $HPSOCKET_LIB_TARGET_DIR
    	cp_lib_so $HPSOCKET4C_LIB_TARGET_DIR
    	
    	mkdir -p $PREFIX_PATH/$DEST_INC_DIR
    	
    	# copy include dir
     	cp_inc $INC_DIR $PREFIX_PATH/$DEST_INC_DIR
    
    	if [[ $INSTALL_DEMO -eq 1 && -d $DEM_DIR/$PLATFORM && "$(ls -A $DEM_DIR/$PLATFORM)" != "" ]]; then
    		  # copy demo .exe files
    		  mkdir -p $PREFIX_PATH/$DEST_BIN_DIR
    		  cp_bin_exe
    		  
    		  # copy demo ssl cert files
    		  mkdir -p $PREFIX_PATH/$DEST_BIN_DIR/$DEST_CER_DIR
    		  cp_bin_cert
    	fi
    }

    至此,库的编译就完成了。

    2.demo的编译

    库的应用代码在demo目录下,demo这里是用sln组织的。

    我之前用testecho-http这个demo来学习这个库的使用。

    但这里有个大坑,我实际编译的时候重写成makefile,死活告诉我链接不到某几个方法,但我已经加入库了。

    后来知道是为什么了,用

    nm libsocket.so | grep 链接不到的函数名

    发现前面的是小写?然后帮助文档里有句话叫做

    If the symbol is loacal(no-external), the symbol’s type is instead represented by the corresponding lowercase letter.

    所以就是不能调用了哦。

    然后这里有几种解决方法:

    1. 链接到.a可以解决,因为.a是把.o打包
    2. 不要链接.so直接链接.o
    3. 修改代码,换一种获取类的方式

    二、使用

    HP-Socket 官方库项目的地址
    https://github.com/ldcsaa/HP-Socket

    环境:ubuntu
    本文基于其readme中的C++程序来做分析
    git中提供的《HP-Socket网络通信框架开发指南》还是需要反复好好看的


    1.工作流程

    1)创建监听器

    2)创建通信组件(同时绑定监听器)

    3)启动通信组件

    4)连接到目标主机(Agent组件)

    5)处理通信事件(OnConnect/OnReceive/OnClose等)

    6)停止通信组件(可选:在第7步销毁通信组件时会自动停止组件)

    7)销毁通信组件

    8)销毁监听器

    示例代码
    #include <hpsocket/HPSocket.h>
    
    /* Listener Class */
    class CListenerImpl : public CTcpPullServerListener
    {
    public:
    	// 5. process network events
    	virtual EnHandleResult OnPrepareListen(ITcpServer* pSender, SOCKET soListen);
    	virtual EnHandleResult OnAccept(ITcpServer* pSender, CONNID dwConnID, UINT_PTR soClient);
    	virtual EnHandleResult OnHandShake(ITcpServer* pSender, CONNID dwConnID);
    	virtual EnHandleResult OnReceive(ITcpServer* pSender, CONNID dwConnID, int iLength);
    	virtual EnHandleResult OnSend(ITcpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength);
    	virtual EnHandleResult OnClose(ITcpServer* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode);
    	virtual EnHandleResult OnShutdown(ITcpServer* pSender);
    };
    
    int main(int argc, char* const argv[])
    {
    	// 1. Create listener object
    	CListenerImpl s_listener;
    	
    	// 2. Create component object (and binding with listener object)
    	CTcpPullServerPtr s_pserver(&s_listener);
    	
    	// 3. Start component object
    	if(!s_pserver->Start("0.0.0.0", 5555))
    		exit(1);
    	
    	/* wait for exit */
    	// ... ... 
    		
    	// 6. (optional) Stop component object
    	s_pserver->Stop();
    
    	return 0;
    	
    	// 7. Destroy component object automatically
    	// 8. Destroy listener object automatically
    }

    这里首先是两点

    a.创建监听器 CListenerImpl s_listener;

    b.创建通信组件(同时绑定监听器) CTcpPullServerPtr s_pserver(&s_listener);
    后续的通信的启动、配置等功能,均是通过 s_pserver 来进行的

    2.关于 CListenerImpl

    class CListenerImpl : public CTcpPullServerListener
    {
    public:
    	// 5. process network events
    	virtual EnHandleResult OnPrepareListen(ITcpServer* pSender, SOCKET soListen);
    	virtual EnHandleResult OnAccept(ITcpServer* pSender, CONNID dwConnID, UINT_PTR soClient);
    	virtual EnHandleResult OnHandShake(ITcpServer* pSender, CONNID dwConnID);
    	virtual EnHandleResult OnReceive(ITcpServer* pSender, CONNID dwConnID, int iLength);
    	virtual EnHandleResult OnSend(ITcpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength);
    	virtual EnHandleResult OnClose(ITcpServer* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode);
    	virtual EnHandleResult OnShutdown(ITcpServer* pSender);
    };

    1)其中OnPrepareListen等方法,需要自己去具体实现,他们分别会在各自OnXXXX的情况下触发
    2)关于基类 CTcpPullServerListener

    在 Socketinterface.h 中
    /************************************************************************
    名称:PUSH 模型服务端 Socket 监听器抽象基类
    描述:定义某些事件的默认处理方法(忽略事件)
    ************************************************************************/
    class CTcpServerListener : public ITcpServerListener
    /************************************************************************
    名称:PULL 模型服务端 Socket 监听器抽象基类
    描述:定义某些事件的默认处理方法(忽略事件)
    ************************************************************************/
    class CTcpPullServerListener : public CTcpServerListener

    逐级向上,此处可以结合pdf中 Server组件接口去看

    3.关于 CTcpPullServerPtr

    同样我们可以跟踪到

    在HPSocket.h中
    typedef CHPSocketPtr<ITcpPullServer, ITcpServerListener, TcpPullServer_Creator>	CTcpPullServerPtr;

    其实此处有很多类似的
    HPSocket.h
    这里我们就会了解到,其实(在HPSocket。h开头)具体的使用方法在开头做了说明

    Usage:
    		方法一:
    		--------------------------------------------------------------------------------------
    		0. 应用程序包含 HPTypeDef.h / SocketInterface.h / HPSocket.h 头文件
    		1. 调用 HP_Create_Xxx() 函数创建 HPSocket 对象
    		2. 使用完毕后调用 HP_Destroy_Xxx() 函数销毁 HPSocket 对象
    
    		方法二:
    		--------------------------------------------------------------------------------------
    		0. 应用程序包含 SocketInterface.h 和 HPSocket.h 头文件
    		1. 创建 CXxxPtr 智能指针,通过智能指针使用 HPSocket 对象
    
    Release:
    		<-- 动态链接库 -->
    		1. x86/libhpsocket.so			- (32位/MBCS/Release)
    		2. x86/libhpsocket_d.so			- (32位/MBCS/DeBug)
    		3. x64/libhpsocket.so			- (64位/MBCS/Release)
    		4. x64/libhpsocket_d.so			- (64位/MBCS/DeBug)
    
    		<-- 静态链接库 -->
    		1. x86/static/libhpsocket.a		- (32位/MBCS/Release)
    		2. x86/static/libhpsocket_d.a	- (32位/MBCS/DeBug)
    		3. x64/static/libhpsocket.a		- (64位/MBCS/Release)
    		4. x64/static/libhpsocket_d.a	- (64位/MBCS/DeBug)

    这里使用的是方法二,也就是CXxxPtr 智能指针。
    HP-Socket 的 TCP 组件支持 PUSH、 PULL 和 PACK 三种接收模型

    1、PUSH 模型:组件接收到数据时会触发监听器对象的 OnReceive(pSender, dwConnID,pData, iLength) 事件,把数据“推”给应用程序。
    
    2、 PULL 模型: 组件接收到数据时会触发监听器对象的 OnReceive(pSender, dwConnID,iTotalLength) 事件,告诉应用程序当前已经接收到多少数据,应用程序检查数据的长度,如果满足需要则调用组件的 Fetch(dwConnID, pData, iDataLength) 方法把需要的数据“拉”出来。
    
    3、 PACK 模型: PACK 模型系列组件是 PUSH 和 PULL 模型的结合体,应用程序不必处理分包(如: PUSH)与数据抓取(如: PULL), 组件保证每个 OnReceive 事件都向应用程序提供一个完整数据包。

    对于CXxxPtr 智能指针的使用,以CTcpServerPtr为例,我们可以看到有如下方法

    typedef CHPSocketPtr<ITcpServer, ITcpServerListener, TcpServer_Creator>  CTcpServerPtr;
    下面考察主要的  ITcpServer 

    以其实对于通信socket的启动关闭设置等功能均可以在此处进行

    回顾下上面的示例代码
    s_pserver->Start("0.0.0.0", 5555)
    s_pserver->Stop();

    三、总结

    从简单的使用来说,3个环节
    1、创建监听器, 创建通信组件(同时绑定监听器)

    2、填充OnXXXX等函数,比如OnReceive就打印出来,或者调用

    virtual EnHandleResult OnReceive
    (ITcpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override
    	{
    		printf("OnReceive
    "); 
    		if(pSender->Send(dwConnID, pData, iLength))
    			return HR_OK;		
    		return HR_ERROR;
    	}
    
    /***********************************/
    EnHandleResult OnReceive
    (ITcpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
    中的 ITcpServer* pSender 进行
    pSender->Send(dwConnID, pData, iLength)   将数据发送出去

    3、主程序中,使用 CXxxPtr 智能指针进行基本的设置、启动、关闭等动作

    ps
    可以在主程序return前,执行while1,这样程序会一直停留准备进行响应

    pps

    1)使用g++编译时,需要添加选项 -std=c++11
    2)如果出现编译时候一些基本类型有问题, 建议添加 #include <cstdlib>

    使用HPSocket通讯不需要考虑逻辑结构,直接按照方法调用就可以使用了,简单明了。

                                      改变自己,从现在做起-----------久馆

  • 相关阅读:
    97. 交错字符串-7月18日
    如何判断一个区块链项目的好坏?
    不知道这10点,千万别用SaaS
    数字人民币应用的五大猜想!你最关心哪个?
    什么是人工智能核心?这2个功能上线
    大数据的七大核心具体价值
    机器学习操作正在兴起
    每个大数据架构师都需要的6个基本技能
    数据之美:可视化会给你意想不到的答案!
    如何采用人工智能创建自动化运营的数据中心
  • 原文地址:https://www.cnblogs.com/zzw19940404/p/14083286.html
Copyright © 2011-2022 走看看