├── deps └── .gitkeep ├── .gitignore ├── examples ├── main.cc ├── all-demos.h └── all-demos.cc ├── docs ├── cpp-api.md ├── reply.md ├── dispatcher.md ├── poll.md ├── client.md ├── agent.md └── c-api.md ├── scripts ├── test ├── install ├── pull-deps └── build ├── android ├── java │ └── com │ │ └── rokid │ │ └── flora │ │ ├── MsgType.java │ │ ├── CapsException.java │ │ ├── Client.java │ │ └── Caps.java ├── jni │ ├── defs.h │ ├── onload.cpp │ ├── com_rokid_flora_Caps.cpp │ └── com_rokid_flora_Client.cpp └── demo │ ├── AndroidManifest.xml │ └── com │ └── rokid │ └── flora │ ├── DemoCallback.java │ └── Demo.java ├── src ├── conn.h ├── sock-conn.h ├── file-log.h ├── sock-adap.h ├── defs.h ├── adap.h ├── sock-poll.h ├── poll.cc ├── sock-adap.cc ├── sock-conn.cc ├── beep-sock-poll.h ├── cli.h ├── disp.h ├── sock-poll.cc ├── ser-helper.h └── flora-agent.cc ├── unit-tests ├── test-svc.h ├── test-svc.cc ├── test-cli.h ├── main.cc └── test-cli.cc ├── ndk-cli.jni.mk ├── test ├── main.cc ├── svc.h └── simple.cc ├── ndk-cli.mk ├── ndk-svc.mk ├── vsys.mk ├── .travis.yml ├── README.md ├── include ├── flora-svc.h ├── flora-agent.h └── flora-cli.h ├── config ├── monitor └── main.cc └── CMakeLists.txt /deps/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | deps/* 2 | !deps/.gitkeep 3 | 4 | out/ 5 | -------------------------------------------------------------------------------- /examples/main.cc: -------------------------------------------------------------------------------- 1 | #include "all-demos.h" 2 | 3 | int main(int argc, char **argv) { 4 | DemoAllInOne demo; 5 | demo.run(); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /docs/cpp-api.md: -------------------------------------------------------------------------------- 1 | # Classes 2 | 3 | [Agent](agent.md) 4 | 5 | [Dispatcher](dispatcher.md) 6 | 7 | [Poll](poll.md) 8 | 9 | [Client](client.md) (低级接口,Agent是Client的封装,更易使用,建议使用Agent。) -------------------------------------------------------------------------------- /scripts/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ex 3 | 4 | export LD_LIBRARY_PATH=$(pwd)/out/usr/lib 5 | export DYLD_LIBRARY_PATH=$(pwd)/out/usr/lib 6 | 7 | out/build/flora/flora-test unix:flora.sock 8 | -------------------------------------------------------------------------------- /android/java/com/rokid/flora/MsgType.java: -------------------------------------------------------------------------------- 1 | package com.rokid.flora; 2 | 3 | public class MsgType { 4 | public static final int INSTANT = 0; 5 | public static final int PERSIST = 1; 6 | public static final int REQUEST = 2; 7 | } 8 | -------------------------------------------------------------------------------- /android/java/com/rokid/flora/CapsException.java: -------------------------------------------------------------------------------- 1 | package com.rokid.flora; 2 | 3 | public class CapsException extends Exception { 4 | public CapsException(int code) { 5 | errCode = code; 6 | } 7 | 8 | public int errCode() { 9 | return errCode; 10 | } 11 | 12 | private int errCode; 13 | } 14 | -------------------------------------------------------------------------------- /scripts/install: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ex 3 | 4 | scripts/pull-deps --repo Rokid/aife-mutils --ref master --dest deps/mutils 5 | scripts/pull-deps --repo Rokid/aife-cmake-modules --ref master --dest deps/cmake-modules 6 | scripts/pull-deps --repo google/googletest --ref release-1.8.1 --dest deps/googletest 7 | -------------------------------------------------------------------------------- /android/jni/defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "caps.h" 4 | #include "jni.h" 5 | 6 | #define TAG "flora-cli.jni" 7 | 8 | typedef struct { 9 | std::shared_ptr caps; 10 | } CapsNativeHandle; 11 | 12 | int register_com_rokid_flora_Caps(JNIEnv *env); 13 | int register_com_rokid_flora_Client(JNIEnv *env); 14 | -------------------------------------------------------------------------------- /src/conn.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Connection { 6 | public: 7 | virtual ~Connection() = default; 8 | 9 | virtual bool send(const void *data, uint32_t size) = 0; 10 | 11 | virtual int32_t recv(void *data, uint32_t size) = 0; 12 | 13 | virtual void close() = 0; 14 | 15 | virtual bool closed() const = 0; 16 | }; 17 | -------------------------------------------------------------------------------- /android/jni/onload.cpp: -------------------------------------------------------------------------------- 1 | #include "defs.h" 2 | #include "rlog.h" 3 | 4 | extern "C" jint JNI_OnLoad(JavaVM *vm, void *reserved) { 5 | JNIEnv *env; 6 | 7 | if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) { 8 | KLOGE(TAG, "JNI_OnLoad failed"); 9 | return -1; 10 | } 11 | register_com_rokid_flora_Caps(env); 12 | register_com_rokid_flora_Client(env); 13 | return JNI_VERSION_1_4; 14 | } 15 | -------------------------------------------------------------------------------- /unit-tests/test-svc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "flora-svc.h" 4 | 5 | using namespace std; 6 | using namespace flora; 7 | 8 | class TestService { 9 | public: 10 | bool run(const char *uri, bool capi); 11 | 12 | void close(); 13 | 14 | private: 15 | shared_ptr dispatcher; 16 | shared_ptr fpoll; 17 | 18 | flora_dispatcher_t c_dispatcher = 0; 19 | flora_poll_t c_fpoll = 0; 20 | 21 | bool use_c_api = false; 22 | }; 23 | -------------------------------------------------------------------------------- /ndk-cli.jni.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_MODULE := libflora-cli.jni 5 | LOCAL_LDLIBS := -llog 6 | LOCAL_SRC_FILES := \ 7 | android/jni/com_rokid_flora_Caps.cpp \ 8 | android/jni/com_rokid_flora_Client.cpp \ 9 | android/jni/onload.cpp \ 10 | android/jni/defs.h 11 | LOCAL_C_INCLUDES := \ 12 | $(LOCAL_PATH)/android/jni 13 | LOCAL_SHARED_LIBRARIES := libflora-cli librlog libcaps 14 | include $(BUILD_SHARED_LIBRARY) 15 | -------------------------------------------------------------------------------- /docs/reply.md: -------------------------------------------------------------------------------- 1 | # Reply 2 | 3 | ## Methods 4 | 5 | ### write_code 6 | 7 | 设置远程函数返回码 8 | 9 | #### Parameters 10 | 11 | name | type | default | description 12 | --- | --- | --- | --- 13 | code | int32_t | 0 | 14 | 15 | ### write_data 16 | 17 | 设置远程函数返回值 18 | 19 | #### Parameters 20 | 21 | name | type | default | description 22 | --- | --- | --- | --- 23 | data | shared_ptr\<[Caps](https://github.com/Rokid/aife-mutils/blob/master/caps.md)>& | | 返回值 24 | 25 | ### end 26 | 27 | 销毁Reply对象并将返回码与返回值发送至flora服务,flora服务将发送给远程函数调用者 28 | 29 | #### No Parameter 30 | -------------------------------------------------------------------------------- /test/main.cc: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "svc.h" 3 | 4 | using namespace std; 5 | using namespace flora; 6 | 7 | string Service::floraUri; 8 | ThreadPool Service::thrPool{4}; 9 | 10 | int main(int argc, char** argv) { 11 | if (argc < 2) { 12 | printf("USAGE: %s ${floraUri}\n", argv[0]); 13 | return 1; 14 | } 15 | Service svc{argv[1]}; 16 | svc.start(); 17 | 18 | --argc; 19 | ++argv; 20 | testing::InitGoogleTest(&argc, argv); 21 | auto r = RUN_ALL_TESTS(); 22 | svc.stop(); 23 | Service::thrPool.clear(); 24 | return r; 25 | } 26 | -------------------------------------------------------------------------------- /docs/dispatcher.md: -------------------------------------------------------------------------------- 1 | # Dispatcher 2 | 3 | flora service消息分发模块,需与[Poll](poll.md)配合使用 4 | 5 | ## Methods 6 | 7 | ### (static) new_instance(bufsize) 8 | 9 | 创建Dispatcher对象 10 | 11 | #### Parameters 12 | 13 | name | type | default | description 14 | --- | --- | --- | --- 15 | bufsize | uint32_t | 0 | 消息缓冲区大小(决定了一个消息最大大小),最小值32K,小于32K的值会被改为32K。 16 | 17 | ### run(block) 18 | 19 | 开始消息分发循环 20 | 21 | #### Parameters 22 | 23 | name | type | default | description 24 | --- | --- | --- | --- 25 | block | bool | false | true: 阻塞式运行
false: 开启独立线程运行,run函数立即返回 26 | 27 | ### close 28 | 29 | 停止消息分发循环 -------------------------------------------------------------------------------- /ndk-cli.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_MODULE := libflora-cli 5 | LOCAL_LDLIBS := -llog 6 | LOCAL_CPP_EXTENSION := .cc 7 | LOCAL_SRC_FILES := \ 8 | include/flora-cli.h \ 9 | src/cli.h \ 10 | src/cli.cc \ 11 | src/conn.h \ 12 | src/tcp-conn.h \ 13 | src/tcp-conn.cc \ 14 | src/unix-conn.h \ 15 | src/unix-conn.cc \ 16 | src/defs.h \ 17 | src/ser-helper.h \ 18 | src/ser-helper.cc 19 | LOCAL_C_INCLUDES := \ 20 | $(LOCAL_PATH)/include 21 | LOCAL_SHARED_LIBRARIES := librlog libcaps 22 | LOCAL_STATIC_LIBRARIES := libmisc 23 | LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include 24 | include $(BUILD_SHARED_LIBRARY) 25 | -------------------------------------------------------------------------------- /android/demo/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/all-demos.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "flora-agent.h" 4 | #include "flora-svc.h" 5 | 6 | #define TAG "flora.demo" 7 | #define AGENT_NUMBER 4 8 | 9 | class DemoAllInOne { 10 | public: 11 | void run(); 12 | 13 | private: 14 | void start_service(); 15 | 16 | void stop_service(); 17 | 18 | void run_start_stop_service(); 19 | 20 | void run_post_msg(); 21 | 22 | void run_recv_msg(); 23 | 24 | void test_errors(); 25 | 26 | void test_reply_after_close(); 27 | 28 | void test_invoke_in_callback(); 29 | 30 | private: 31 | std::shared_ptr poll; 32 | std::shared_ptr dispatcher; 33 | flora::Agent agents[AGENT_NUMBER]; 34 | flora_agent_t cagents[AGENT_NUMBER]; 35 | }; 36 | -------------------------------------------------------------------------------- /test/svc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "thr-pool.h" 5 | #include "flora-svc.h" 6 | 7 | class Service { 8 | public: 9 | Service(const char* uri) { 10 | floraUri = uri; 11 | } 12 | 13 | void start() { 14 | disp = flora::Dispatcher::new_instance(FLORA_DISP_FLAG_MONITOR); 15 | poll = flora::Poll::new_instance(floraUri.c_str()); 16 | poll->start(disp); 17 | disp->run(false); 18 | } 19 | 20 | void stop() { 21 | disp->close(); 22 | poll->stop(); 23 | disp.reset(); 24 | poll.reset(); 25 | } 26 | 27 | public: 28 | static std::string floraUri; 29 | static ThreadPool thrPool; 30 | 31 | private: 32 | std::shared_ptr disp; 33 | std::shared_ptr poll; 34 | }; 35 | 36 | -------------------------------------------------------------------------------- /ndk-svc.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_MODULE := libflora-svc 5 | LOCAL_LDLIBS := -llog 6 | LOCAL_CPP_EXTENSION := .cc 7 | LOCAL_SRC_FILES := \ 8 | include/flora-svc.h \ 9 | include/flora-cli.h \ 10 | src/defs.h \ 11 | src/adap.h \ 12 | src/sock-adap.h \ 13 | src/sock-adap.cc \ 14 | src/poll.cc \ 15 | src/tcp-poll.h \ 16 | src/tcp-poll.cc \ 17 | src/unix-poll.h \ 18 | src/unix-poll.cc \ 19 | src/disp.h \ 20 | src/disp.cc \ 21 | src/reply-mgr.h \ 22 | src/reply-mgr.cc \ 23 | src/ser-helper.h \ 24 | src/ser-helper.cc 25 | LOCAL_C_INCLUDES := \ 26 | $(LOCAL_PATH)/include 27 | LOCAL_SHARED_LIBRARIES := librlog libcaps 28 | LOCAL_STATIC_LIBRARIES := libmisc 29 | LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include 30 | include $(BUILD_SHARED_LIBRARY) 31 | 32 | -------------------------------------------------------------------------------- /src/sock-conn.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "conn.h" 4 | #include 5 | #include 6 | 7 | class SocketConn : public Connection { 8 | public: 9 | SocketConn(uint32_t rtmo); 10 | 11 | ~SocketConn(); 12 | 13 | bool connect(const std::string &name); 14 | 15 | bool connect(const std::string &host, int32_t port); 16 | 17 | bool send(const void *data, uint32_t size) override; 18 | 19 | // return: -1 socket error 20 | // -2 read timeout 21 | int32_t recv(void *data, uint32_t size) override; 22 | 23 | void close() override; 24 | 25 | inline bool closed() const override { return !sock_ready; } 26 | 27 | inline int get_socket() const { return sock; } 28 | 29 | private: 30 | int sock = -1; 31 | uint32_t recv_timeout; 32 | bool sock_ready = false; 33 | std::mutex write_mutex; 34 | }; 35 | -------------------------------------------------------------------------------- /scripts/pull-deps: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | help=" 5 | " 6 | 7 | repo="" 8 | ref="" 9 | dest="" 10 | while [ $# -gt 0 ]; do 11 | case "$1" in 12 | --repo) 13 | repo="$2" 14 | shift 15 | ;; 16 | --ref) 17 | ref="$2" 18 | shift 19 | ;; 20 | --dest) 21 | dest="$2" 22 | shift 23 | ;; 24 | -h|--help) 25 | printf "$help" 26 | exit 27 | ;; 28 | --*) 29 | echo "Illegal option $1" 30 | ;; 31 | esac 32 | shift $(( $# > 0 ? 1 : 0 )) 33 | done 34 | 35 | work_dir=/tmp 36 | echo "Fetching $repo source codes on ref $ref" 37 | archive_url="https://github.com/$repo/archive/$ref.tar.gz" 38 | 39 | arvhice_path="$work_dir/${repo/\//-}-$ref.tar.gz" 40 | curl -sLo "$arvhice_path" "$archive_url" 41 | tar -C "$work_dir" -xzf "$arvhice_path" 42 | rm -f "$arvhice_path" 43 | 44 | rm -rf "$dest" 45 | mv "$work_dir/${repo//*\//}-$ref" "$dest" 46 | -------------------------------------------------------------------------------- /vsys.mk: -------------------------------------------------------------------------------- 1 | local-path := $(call my-dir) 2 | 3 | include $(clear-vars) 4 | local.module := flora-cli 5 | local.ndk-script := $(local-path)/ndk-cli.mk 6 | local.ndk-modules := mutils 7 | include $(build-ndk-module) 8 | 9 | include $(clear-vars) 10 | local.module := flora-cli.jni 11 | local.ndk-script := $(local-path)/ndk-cli.jni.mk 12 | local.ndk-modules := mutils flora-cli 13 | include $(build-ndk-module) 14 | 15 | include $(clear-vars) 16 | local.module := flora-svc 17 | local.ndk-script := $(local-path)/ndk-svc.mk 18 | local.ndk-modules := mutils 19 | include $(build-ndk-module) 20 | 21 | include $(clear-vars) 22 | local.module := flora-java 23 | local.src-paths := android/java 24 | include $(build-java-lib) 25 | 26 | include $(clear-vars) 27 | local.module := flora-demo 28 | local.src-paths := android/demo 29 | local.manifest := android/demo/AndroidManifest.xml 30 | local.jar-modules := flora-java 31 | local.ndk-modules := flora-cli.jni 32 | include $(build-java-app) 33 | -------------------------------------------------------------------------------- /src/file-log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef DEBUG_FOR_YODAV8 4 | 5 | #include 6 | #include 7 | #include 8 | #include "rlog.h" 9 | #include "defs.h" 10 | 11 | class FileLogWriter : public RLogWriter { 12 | public: 13 | bool init(void* arg) { 14 | return true; 15 | } 16 | 17 | void destroy() { 18 | } 19 | 20 | int32_t raw_write(const char* file, int line, RokidLogLevel lv, 21 | const char* tag, const char* fmt, va_list ap) { 22 | if (strcmp(tag, FILE_TAG) == 0) 23 | return 0; 24 | return 1; 25 | } 26 | 27 | bool write(const char* data, uint32_t size) { 28 | if (fileName.empty()) 29 | return false; 30 | auto fd = open(fileName.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0644); 31 | if (fd < 0) 32 | return false; 33 | auto r = ::write(fd, data, size); 34 | close(fd); 35 | return r > 0; 36 | } 37 | 38 | private: 39 | std::string fileName{"/data/flora-dispatcher.fatal.log"}; 40 | }; 41 | 42 | #endif // DEBUG_FOR_YODAV8 43 | -------------------------------------------------------------------------------- /android/demo/com/rokid/flora/DemoCallback.java: -------------------------------------------------------------------------------- 1 | package com.rokid.flora; 2 | 3 | import android.util.Log; 4 | 5 | public class DemoCallback implements Client.Callback { 6 | public DemoCallback(String id) { 7 | clientId = id; 8 | } 9 | 10 | public void receivedPost(String name, Caps msg, int type) { 11 | try { 12 | int i = msg.readInteger(); 13 | Log.i(Demo.TAG, "client " + clientId + " received post msg " + name + ", " + i); 14 | } catch (CapsException e) { 15 | } 16 | } 17 | 18 | public Client.Callback.Reply receivedGet(String name, Caps msg) { 19 | try { 20 | String s = msg.readString(); 21 | Log.i(Demo.TAG, "client " + clientId + " received get msg " + name + ", " + s); 22 | } catch (CapsException e) { 23 | } 24 | Client.Callback.Reply rep = new Client.Callback.Reply(); 25 | rep.retCode = Client.SUCCESS; 26 | rep.msg = new Caps(); 27 | rep.msg.writeString("world"); 28 | return rep; 29 | } 30 | 31 | public void disconnected() { 32 | } 33 | 34 | private String clientId; 35 | } 36 | -------------------------------------------------------------------------------- /docs/poll.md: -------------------------------------------------------------------------------- 1 | # Poll 2 | 3 | flora service连接管理,需与[Dispatcher](dispatcher.md)配合使用 4 | 5 | ``` 6 | shared_ptr disp = Dispatcher::new_instance(0); 7 | shared_ptr unix_poll = Poll::new_instance("unix:/var/run/flora.sock"); 8 | unix_poll->start(disp); 9 | shared_ptr tcp_poll = Poll::new_instance("tcp://0.0.0.0:38380/"); 10 | tcp_poll->start(disp); 11 | ``` 12 | 13 | ## Methods 14 | 15 | ### (static) new_instance(uri) 16 | 17 | 创建Poll对象 18 | 19 | #### Parameters 20 | 21 | name | type | default | description 22 | --- | --- | --- | --- 23 | uri | const char* | | uri - 服务侦听地址
支持unix domain socket及tcp socket 24 | 25 | ### start(dispatcher) 26 | 27 | 启动服务地址侦听 28 | 29 | #### Parameters 30 | 31 | name | type | default | description 32 | --- | --- | --- | --- 33 | dispatcher | shared_ptr\<[Dispatcher](dispatcher.md)>& | | 34 | 35 | #### Returns 36 | 37 | Type: int32_t 38 | 39 | value | description 40 | --- | --- 41 | FLORA_POLL_SUCCESS | 成功 42 | FLORA_POLL_ALREADY_START | 重复start 43 | FLORA_POLL_SYSERR | socket等系统调用失败 44 | FLORA_POLL_INVAL | 参数非法 45 | 46 | --- 47 | 48 | ### stop() 49 | 50 | 停止服务地址侦听 51 | -------------------------------------------------------------------------------- /scripts/build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ex 3 | 4 | test_build=false 5 | while [ $# -gt 0 ]; do 6 | case "$1" in 7 | --test) 8 | test_build=true 9 | shift 10 | ;; 11 | -h|--help) 12 | printf "$help" 13 | exit 14 | ;; 15 | --*) 16 | echo "Illegal option $1" 17 | ;; 18 | esac 19 | shift $(( $# > 0 ? 1 : 0 )) 20 | done 21 | 22 | project_dir=$(pwd) 23 | 24 | cd deps/mutils 25 | build_dir="$project_dir/out/build/mutils" 26 | ./config \ 27 | --build-dir="$build_dir" \ 28 | --cmake-modules="$project_dir/deps/cmake-modules" \ 29 | --prefix="$project_dir/out/usr" 30 | cd $build_dir 31 | make 32 | make install 33 | 34 | cd $project_dir 35 | build_dir="$project_dir/out/build/flora" 36 | additional_opt="" 37 | 38 | if test $test_build = true; then 39 | additional_opt="--test" 40 | cd deps/googletest 41 | cmake . -DCMAKE_INSTALL_PREFIX=$project_dir/out/usr 42 | make install 43 | cd $project_dir 44 | fi 45 | 46 | ./config \ 47 | "$additional_opt" \ 48 | --build-dir="$build_dir" \ 49 | --cmake-modules="$project_dir/deps/cmake-modules" \ 50 | --find_root_path="$prjroot/out" \ 51 | --prefix="$project_dir/out/usr" 52 | cd $build_dir 53 | make 54 | make install 55 | -------------------------------------------------------------------------------- /src/sock-adap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "adap.h" 4 | #include 5 | 6 | #define HEADER_SIZE 8 7 | 8 | #define SOCK_ADAPTER_SUCCESS 0 9 | // socket已关闭或出错 10 | #define SOCK_ADAPTER_ECLOSED -10001 11 | // 读取的数据不足 12 | #define SOCK_ADAPTER_ENOMORE -10002 13 | // 数据协议格式错误 14 | #define SOCK_ADAPTER_EPROTO -10003 15 | // buf空间不足 16 | #define SOCK_ADAPTER_ENOBUF -10004 17 | 18 | class SocketAdapter : public Adapter { 19 | public: 20 | SocketAdapter(int sock, uint32_t bufsize, uint32_t flags, uint32_t wto); 21 | 22 | ~SocketAdapter(); 23 | 24 | // return: = 0 success 25 | // < 0 error 26 | int32_t read() override; 27 | 28 | int32_t next_frame(Frame &frame) override; 29 | 30 | // return: 31 | // 0 success 32 | // -1 socket error 33 | // -2 write timeout 34 | int32_t write(const void *data, uint32_t size) override; 35 | 36 | void close() override; 37 | 38 | bool closed() override; 39 | 40 | int socket() const { return socketfd; } 41 | 42 | private: 43 | void close_nolock(); 44 | 45 | private: 46 | int socketfd; 47 | int8_t *buffer = nullptr; 48 | uint32_t buf_size; 49 | uint32_t cur_size = 0; 50 | uint32_t frame_begin = 0; 51 | std::mutex write_mutex; 52 | }; 53 | -------------------------------------------------------------------------------- /src/defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define FLORA_VERSION 4 4 | 5 | // client --> server 6 | #define CMD_AUTH_REQ 0 7 | #define CMD_SUBSCRIBE_REQ 1 8 | #define CMD_UNSUBSCRIBE_REQ 2 9 | #define CMD_POST_REQ 3 10 | #define CMD_REPLY_REQ 4 11 | #define CMD_DECLARE_METHOD_REQ 5 12 | #define CMD_REMOVE_METHOD_REQ 6 13 | #define CMD_CALL_REQ 7 14 | #define CMD_PING_REQ 8 15 | // server --> client 16 | #define CMD_AUTH_RESP 101 17 | #define CMD_POST_RESP 102 18 | #define CMD_REPLY_RESP 103 19 | #define CMD_CALL_RESP 104 20 | #define CMD_MONITOR_RESP 105 21 | #define CMD_PONG_RESP 106 22 | 23 | #define MSG_HANDLER_COUNT 9 24 | 25 | // subtype of CMD_MONITOR_RESP 26 | #define MONITOR_LIST_ALL 0 27 | #define MONITOR_LIST_ADD 1 28 | #define MONITOR_LIST_REMOVE 2 29 | #define MONITOR_SUB_ALL 3 30 | #define MONITOR_SUB_ADD 4 31 | #define MONITOR_SUB_REMOVE 5 32 | #define MONITOR_DECL_ALL 6 33 | #define MONITOR_DECL_ADD 7 34 | #define MONITOR_DECL_REMOVE 8 35 | #define MONITOR_POST 9 36 | #define MONITOR_CALL 10 37 | #define MONITOR_SUBTYPE_NUM 11 38 | 39 | #define DEFAULT_MSG_BUF_SIZE 32768 40 | #define CLEAR_SUBSCRIPTION_TIME_THRESHOLD 10 41 | #define CLEAR_SUBSCRIPTION_THRESHOLD 50 42 | 43 | #ifdef __APPLE__ 44 | #define SELECT_BLOCK_IF_FD_CLOSED 45 | #endif 46 | 47 | #define TAG "flora" 48 | #define FILE_TAG "flora.filelog" 49 | -------------------------------------------------------------------------------- /unit-tests/test-svc.cc: -------------------------------------------------------------------------------- 1 | #include "test-svc.h" 2 | 3 | bool TestService::run(const char *uri, bool capi) { 4 | use_c_api = capi; 5 | if (capi) { 6 | c_dispatcher = flora_dispatcher_new(0, 0); 7 | if (c_dispatcher == 0) 8 | return false; 9 | if (flora_poll_new(uri, &c_fpoll) != FLORA_POLL_SUCCESS) { 10 | flora_dispatcher_delete(c_dispatcher); 11 | c_dispatcher = 0; 12 | return false; 13 | } 14 | if (flora_poll_start(c_fpoll, c_dispatcher) != FLORA_POLL_SUCCESS) { 15 | flora_poll_delete(c_fpoll); 16 | c_fpoll = 0; 17 | flora_dispatcher_delete(c_dispatcher); 18 | c_dispatcher = 0; 19 | return false; 20 | } 21 | flora_dispatcher_run(c_dispatcher, 0); 22 | } else { 23 | dispatcher = Dispatcher::new_instance(0, 0); 24 | fpoll = Poll::new_instance(uri); 25 | if (fpoll.get() == nullptr) 26 | return false; 27 | if (fpoll->start(dispatcher) != FLORA_POLL_SUCCESS) 28 | return false; 29 | dispatcher->run(false); 30 | } 31 | return true; 32 | } 33 | 34 | void TestService::close() { 35 | if (use_c_api) { 36 | flora_poll_stop(c_fpoll); 37 | flora_poll_delete(c_fpoll); 38 | c_fpoll = 0; 39 | flora_dispatcher_delete(c_dispatcher); 40 | c_dispatcher = 0; 41 | } else { 42 | fpoll->stop(); 43 | fpoll.reset(); 44 | dispatcher.reset(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/adap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct { 8 | void *data; 9 | uint32_t size; 10 | } Frame; 11 | 12 | class AdapterInfo { 13 | public: 14 | AdapterInfo() { id = ++idseq; } 15 | 16 | uint32_t id; 17 | int32_t pid; 18 | std::string name; 19 | std::set declared_methods; 20 | uint32_t flags = 0; 21 | 22 | static uint32_t idseq; 23 | 24 | public: 25 | bool declare_method(const std::string &name) { 26 | auto r = declared_methods.insert(name); 27 | return r.second; 28 | } 29 | 30 | void remove_method(const std::string &name) { declared_methods.erase(name); } 31 | 32 | bool has_method(const std::string &name) { 33 | return declared_methods.find(name) != declared_methods.end(); 34 | } 35 | }; 36 | 37 | class Adapter { 38 | public: 39 | Adapter(uint32_t flags) : serialize_flags(flags) {} 40 | 41 | virtual ~Adapter() = default; 42 | 43 | virtual int32_t read() = 0; 44 | 45 | virtual int32_t next_frame(Frame &frame) = 0; 46 | 47 | virtual int32_t write(const void *data, uint32_t size) = 0; 48 | 49 | virtual void close() = 0; 50 | 51 | virtual bool closed() = 0; 52 | 53 | public: 54 | uint32_t serialize_flags; 55 | AdapterInfo *info = nullptr; 56 | // unix socket adapter: pid 57 | // tcp socket adapter: 58 | // high 32 bits: 0x80000000 | ipv4port 59 | // low 32 bits: ipv4addr 60 | uint64_t tag = 0; 61 | #ifdef FLORA_DEBUG 62 | uint32_t recv_times = 0; 63 | uint32_t recv_bytes = 0; 64 | #endif 65 | }; 66 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - test 3 | - name: deploy 4 | if: tag IS present 5 | 6 | language: c 7 | os: linux 8 | dist: trusty 9 | 10 | install: 11 | - scripts/install 12 | 13 | script: 14 | - scripts/build --test 15 | - scripts/test 16 | 17 | .deploy_job_template: &deploy_job_template 18 | script: 19 | - scripts/build 20 | - tar -czf flora-${TRAVIS_TAG}-$(uname)-$(uname -m).tar.gz -C out ./usr 21 | deploy: 22 | provider: releases 23 | api_key: 24 | secure: wVdyP2Idczya/s6G/gwkJsF/v8QeYdBCC6AxdcyAHNKUZsxaRsM15+eji2sYfA9eZj6dSv4wsRQXGYp0nvuC5b0JBpnTH8wFyURq7DJh4xOb3D3LQsULBWgHVBjwWF3tOJ1jxKralxs8E2A9H9lsT8ee0RCKeX+gGtOUKiU9rOdCezfoPUJjErMGlgYVCSJWg9HQlXiPxyFqDNbY+Z642zKJrWwcBT9Ao3422UJO6zBAVg9xI6PJV8e0+owxY1iHarKycih+MxSTFXHIIq6p9O9ZmZHNXw9Xt34W1m2c/riDnhB1WmhKo74KDIBQJEano6oHCQ4fDahRwiUczTwG3dx5hb6gfEI5SJv7fSmoBh4s9sdozAZ6bnV0NiWhftxUTAgJcoH6L/Sw0tFckb2faYR5CKnRF32VjoAICR4ZBPOiZT9Yq+RLnD+64d2gJG7z4txzRptPiJ3XRx7QQ4LLDERCTcvgfhMxcYaNUyicGy/o+oxDQBws6Mb0eQWibmB8cidJarz3gm+pmwr4RvXZFz0HvkMn0fV0VFqVj7l8oOgHEkosSFUifsI0bhFqF3ibmFr7MJqCnVItaciJuLKtk4buMiQkkAIXql50DLRRiKcxgC3IMs7u8vDP8rtjncZYfybc7g4VHSuMQyGW+xig+8aSJ7SXf3AaVnGxcnfgsFE= 25 | file_glob: true 26 | skip_cleanup: true 27 | file: "./flora-*.tar.gz" 28 | on: 29 | repo: yodaos-project/flora 30 | tags: true 31 | 32 | jobs: 33 | include: 34 | - stage: test 35 | os: linux 36 | - stage: test 37 | os: osx 38 | 39 | - stage: deploy 40 | <<: *deploy_job_template 41 | os: linux 42 | - stage: deploy 43 | <<: *deploy_job_template 44 | os: osx 45 | -------------------------------------------------------------------------------- /src/sock-poll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "disp.h" 4 | #include "flora-svc.h" 5 | #include "sock-adap.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace flora { 15 | namespace internal { 16 | 17 | typedef std::map> AdapterMap; 18 | 19 | class SocketPoll : public flora::Poll { 20 | public: 21 | explicit SocketPoll(const std::string &name); 22 | 23 | SocketPoll(const std::string &host, int32_t port); 24 | 25 | ~SocketPoll(); 26 | 27 | int32_t start(std::shared_ptr &disp); 28 | 29 | void stop(); 30 | 31 | virtual void config(uint32_t opt, ...) {} 32 | 33 | protected: 34 | virtual int32_t do_poll(fd_set *rfds, int max_fd); 35 | 36 | virtual std::shared_ptr do_accept(int lfd); 37 | 38 | virtual bool do_read(std::shared_ptr &adap); 39 | 40 | private: 41 | void run(); 42 | 43 | bool init_unix_socket(); 44 | 45 | bool init_tcp_socket(); 46 | 47 | int get_listen_fd(); 48 | 49 | std::shared_ptr new_adapter(int fd); 50 | 51 | void delete_adapter(std::shared_ptr &adap); 52 | 53 | private: 54 | std::shared_ptr dispatcher; 55 | int listen_fd = -1; 56 | int max_fd = 0; 57 | fd_set all_fds; 58 | uint32_t max_msg_size = 0; 59 | std::thread run_thread; 60 | std::mutex start_mutex; 61 | std::condition_variable start_cond; 62 | AdapterMap adapters; 63 | uint32_t type; 64 | // for unix socket 65 | std::string name; 66 | // for tcp socket 67 | std::string host; 68 | int32_t port; 69 | }; 70 | 71 | } // namespace internal 72 | } // namespace flora 73 | -------------------------------------------------------------------------------- /unit-tests/test-cli.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "caps.h" 4 | #include "flora-cli.h" 5 | 6 | #define FLORA_MSG_COUNT 32 7 | 8 | using namespace std; 9 | using namespace flora; 10 | 11 | typedef struct { 12 | char name[8]; 13 | std::shared_ptr args; 14 | caps_t c_args; 15 | } FloraMsg; 16 | 17 | class TestClient : public ClientCallback { 18 | public: 19 | bool init(const char *uri, bool capi); 20 | 21 | void do_subscribe(); 22 | 23 | void do_post(); 24 | 25 | void do_call(int32_t clinum); 26 | 27 | void reset(); 28 | 29 | void close(); 30 | 31 | void recv_post(const char *name, uint32_t msgtype, shared_ptr &msg); 32 | 33 | void recv_call(const char *name, shared_ptr &msg, 34 | shared_ptr &reply); 35 | 36 | private: 37 | void c_recv_post(const char *name, uint32_t msgtype, caps_t args); 38 | 39 | void c_recv_call(const char *name, caps_t args, flora_call_reply_t reply); 40 | 41 | public: 42 | static void static_init(bool capi); 43 | 44 | static FloraMsg flora_msgs[FLORA_MSG_COUNT]; 45 | 46 | static flora_cli_callback_t flora_callback; 47 | 48 | private: 49 | static void recv_post_s(const char *name, uint32_t msgtype, caps_t msg, 50 | void *arg); 51 | 52 | static void recv_call_s(const char *name, caps_t msg, void *arg, 53 | flora_call_reply_t reply); 54 | 55 | public: 56 | int8_t subscribe_flags[FLORA_MSG_COUNT]; 57 | int8_t declare_method_flags[FLORA_MSG_COUNT]; 58 | int32_t post_counter[FLORA_MSG_COUNT]; 59 | int32_t call_counter[FLORA_MSG_COUNT]; 60 | int32_t recv_post_counter[FLORA_MSG_COUNT]; 61 | int32_t recv_call_counter[FLORA_MSG_COUNT]; 62 | 63 | private: 64 | shared_ptr flora_cli; 65 | flora_cli_t c_flora_cli = 0; 66 | bool use_c_api = false; 67 | }; 68 | -------------------------------------------------------------------------------- /android/demo/com/rokid/flora/Demo.java: -------------------------------------------------------------------------------- 1 | package com.rokid.flora; 2 | 3 | import java.util.ArrayList; 4 | import android.app.Service; 5 | import android.content.Intent; 6 | import android.os.IBinder; 7 | import android.util.Log; 8 | 9 | public class Demo extends Service { 10 | public void onCreate() { 11 | client1 = new Client(); 12 | client2 = new Client(); 13 | } 14 | 15 | public int onStartCommand(Intent intent, int flags, int startId) { 16 | String extra1 = "foo1"; 17 | String extra2 = "foo2"; 18 | client1.destroy(); 19 | client1.connect(FLORA_SERVICE_URI + "#" + extra1, new DemoCallback(extra1), 0); 20 | client2.destroy(); 21 | client2.connect(FLORA_SERVICE_URI + "#" + extra2, new DemoCallback(extra2), 0); 22 | client1.subscribe("foo2.post", MsgType.INSTANT); 23 | client2.subscribe("foo1.get", MsgType.REQUEST); 24 | Caps msg = new Caps(); 25 | ArrayList replys = new ArrayList(); 26 | msg.writeString("hello"); 27 | client1.get("foo1.get", msg, replys, 0); 28 | msg.destroy(); 29 | Log.i(TAG, "foo1.get reply size = " + replys.size()); 30 | Client.Reply rep = replys.get(0); 31 | Log.i(TAG, "foo.get reply code = " + rep.retCode); 32 | Log.i(TAG, "foo.get reply extra = " + rep.extra); 33 | if (rep.retCode == Client.SUCCESS) { 34 | try { 35 | String s = rep.msg.readString(); 36 | rep.msg.destroy(); 37 | Log.i(TAG, "foo.get reply msg = " + s); 38 | } catch (CapsException e) { 39 | } 40 | } 41 | msg = new Caps(); 42 | msg.writeInteger(1); 43 | client2.post("foo2.post", msg, MsgType.INSTANT); 44 | msg.destroy(); 45 | return START_NOT_STICKY; 46 | } 47 | 48 | public IBinder onBind(Intent intent) { 49 | return null; 50 | } 51 | 52 | private Client client1; 53 | private Client client2; 54 | 55 | private static final String FLORA_SERVICE_URI = "tcp://localhost:2517/"; 56 | 57 | public static final String TAG = "flora-demo.android"; 58 | } 59 | -------------------------------------------------------------------------------- /src/poll.cc: -------------------------------------------------------------------------------- 1 | #include "beep-sock-poll.h" 2 | #include "flora-svc.h" 3 | #include "uri.h" 4 | 5 | using namespace std; 6 | using namespace rokid; 7 | 8 | shared_ptr flora::Poll::new_instance(const char *uri) { 9 | Uri urip; 10 | if (!urip.parse(uri)) 11 | return nullptr; 12 | if (urip.scheme == "unix") { 13 | return static_pointer_cast( 14 | make_shared(urip.path)); 15 | } else if (urip.scheme == "tcp") { 16 | return static_pointer_cast( 17 | make_shared(urip.host, urip.port)); 18 | } 19 | return nullptr; 20 | } 21 | 22 | int32_t flora_poll_new(const char *uri, flora_poll_t *result) { 23 | if (result == nullptr) 24 | return FLORA_POLL_INVAL; 25 | Uri urip; 26 | if (!urip.parse(uri)) 27 | return FLORA_POLL_INVAL; 28 | if (urip.scheme == "unix") { 29 | *result = reinterpret_cast( 30 | new flora::internal::SocketPoll(urip.path)); 31 | return FLORA_POLL_SUCCESS; 32 | } else if (urip.scheme == "tcp") { 33 | *result = reinterpret_cast( 34 | new flora::internal::SocketPoll(urip.host, urip.port)); 35 | return FLORA_POLL_SUCCESS; 36 | } 37 | return FLORA_POLL_UNSUPP; 38 | } 39 | 40 | void flora_poll_delete(flora_poll_t handle) { 41 | if (handle) 42 | delete reinterpret_cast(handle); 43 | } 44 | 45 | int32_t flora_poll_start(flora_poll_t handle, flora_dispatcher_t dispatcher) { 46 | if (handle == 0 || dispatcher == 0) 47 | return FLORA_POLL_INVAL; 48 | flora::Poll *fpoll = reinterpret_cast(handle); 49 | flora::Dispatcher *disp = reinterpret_cast(dispatcher); 50 | shared_ptr pdisp(disp, [](flora::Dispatcher *) {}); 51 | return fpoll->start(pdisp); 52 | } 53 | 54 | void flora_poll_stop(flora_poll_t handle) { 55 | if (handle) 56 | reinterpret_cast(handle)->stop(); 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flora 2 | 3 | [![Build Status](https://travis-ci.com/yodaos-project/flora.svg?branch=master)](https://travis-ci.com/yodaos-project/flora) 4 | 5 | 作者: chen.zhang@rokid.com 6 | 7 | 版本: 1 8 | 9 | 更新时间: 2018.08.09 15:44 10 | 11 | ## 概述 12 | 13 | 跨进程/设备消息广播协议 14 | 15 | ## 编译 16 | 17 | * 需要cmake 3.0以上 18 | 19 | * 支持交叉编译 20 | 21 | ``` 22 | ./config <参数> 配置编译参数,生成makefiles 23 | cd ${makefiles生成目录} 24 | make 25 | make install 26 | ``` 27 | 28 | ### 依赖模块 29 | 30 | [mutils](https://github.com/Rokid/aife-mutils) 结构体序列化工具及log工具 31 | 32 | [cmake-modules](https://github.com/Rokid/aife-cmake-modules) cmake脚本功能模块 33 | 34 | ### 编译配置参数说明 35 | 36 | * --build-dir=\* 指定cmake生成makefiles的目录 37 | 38 | * --prefix=\* 指定安装目录 39 | 40 | * --cmake-modules=\* 指定[cmake-modules](https://github.com/Rokid/aife-cmake-modules)仓库目录 41 | 42 | * --find-root-path=\* 附加的动态库/静态库/头文件搜索路径 43 | 44 | * --mutils=\* 指定[mutils](https://github.com/Rokid/aife-mutils)动态库/头文件搜索路径 45 | 46 | * --toolchain=\* 交叉编译工具链安装目录 47 | 48 | * --cross-prefix=\* 交叉编译命令前缀 49 | 50 | ### 编译命令示例 51 | 52 | ``` 53 | 以a113平台交叉编译为例 54 | 假定工具链编译器路径为/home/codefarmer/a113/toolchain/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-g++ 55 | mutils-caps库路径为/home/codefarmer/buildroot/a113/usr/lib/libcaps.so 56 | mutils-rlog库路径为/home/codefarmer/buildroot/a113/usr/lib/librlog.so 57 | mutils头文件路径为/home/codefarmer/buildroot/a113/usr/include/caps/caps.h 58 | /home/codefarmer/buildroot/a113/usr/include/log/rlog.h 59 | (注: 编译mutils时指定./config --prefix=/home/codefarmer/buildroot/a113/usr,make install后即为此种状态) 60 | cmake-modules仓库路径为/home/codefarmer/cmake-modules 61 | 62 | ./config \ 63 | --build-dir=a113-build \ 64 | --cmake-modules=/home/codefarmer/cmake-modules \ 65 | --toolchain=/home/codefarmer/a113/toolchain/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu \ 66 | --cross-prefix=aarch64-linux-gnu- \ 67 | --find-root-path=/home/codefarmer/buildroot/a113 \ 68 | --prefix=你喜欢的安装路径(如不指定,默认为/usr) 69 | 70 | cd a113-build 71 | make 72 | make install 73 | ``` 74 | 75 | ## 使用说明 76 | 77 | [c++接口](./docs/cpp-api.md) 78 | 79 | [c接口](./docs/c-api.md) 80 | -------------------------------------------------------------------------------- /test/simple.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "gtest/gtest.h" 3 | #include "flora-agent.h" 4 | #include "svc.h" 5 | 6 | using namespace std; 7 | using namespace flora; 8 | 9 | TEST(SimpleTest, postInstant) { 10 | Agent rcv; 11 | Agent snd1; 12 | Agent snd2; 13 | string uri = Service::floraUri; 14 | rcv.config(FLORA_AGENT_CONFIG_URI, uri.c_str()); 15 | snd1.config(FLORA_AGENT_CONFIG_URI, (uri + "#test1").c_str()); 16 | snd2.config(FLORA_AGENT_CONFIG_URI, (uri + "#test2").c_str()); 17 | string msgName = "foo"; 18 | struct { 19 | uint32_t num[2]{0, 0}; 20 | string tags[2]; 21 | uint32_t postCompleted{0}; 22 | 23 | int32_t tag2Idx(const string& tag) { 24 | int32_t i; 25 | for (i = 0; i < 2; ++i) { 26 | if (tags[i].empty()) { 27 | tags[i] = tag; 28 | return i; 29 | } 30 | if (tags[i] == tag) 31 | return i; 32 | } 33 | return -1; 34 | } 35 | } inst; 36 | rcv.subscribe(msgName.c_str(), 37 | [&inst](const char* name, shared_ptr& msg, uint32_t type) { 38 | string tag; 39 | MsgSender::to_string(tag); 40 | auto idx = inst.tag2Idx(tag); 41 | ASSERT_GE(idx, 0); 42 | ASSERT_LT(idx, 2); 43 | int32_t v{-1}; 44 | EXPECT_EQ(msg->read(v), CAPS_SUCCESS); 45 | EXPECT_EQ(v, inst.num[idx]); 46 | ++inst.num[idx]; 47 | }); 48 | rcv.start(); 49 | snd1.start(); 50 | snd2.start(); 51 | 52 | Service::thrPool.push([&snd1, &inst, &msgName]() { 53 | uint32_t i; 54 | for (i = 0; i < 78; ++i) { 55 | auto msg = Caps::new_instance(); 56 | msg->write(i); 57 | snd1.post(msgName.c_str(), msg); 58 | } 59 | ++inst.postCompleted; 60 | }); 61 | 62 | Service::thrPool.push([&snd2, &inst, &msgName]() { 63 | uint32_t i; 64 | for (i = 0; i < 99; ++i) { 65 | auto msg = Caps::new_instance(); 66 | msg->write(i); 67 | snd2.post(msgName.c_str(), msg); 68 | } 69 | ++inst.postCompleted; 70 | }); 71 | 72 | sleep(5); 73 | EXPECT_EQ(inst.postCompleted, 2); 74 | EXPECT_EQ(inst.num[0] + inst.num[1], 78 + 99); 75 | EXPECT_EQ(inst.num[0] == 78 || inst.num[0] == 99, true); 76 | } 77 | -------------------------------------------------------------------------------- /include/flora-svc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "caps.h" 4 | 5 | #define FLORA_POLL_SUCCESS 0 6 | #define FLORA_POLL_ALREADY_START -1 7 | #define FLORA_POLL_SYSERR -2 8 | #define FLORA_POLL_INVAL -3 9 | #define FLORA_POLL_UNSUPP -4 10 | 11 | // options of 'config' 12 | #define FLORA_POLL_OPT_KEEPALIVE_TIMEOUT 1 13 | 14 | #define FLORA_DISP_FLAG_MONITOR 1 15 | 16 | #ifdef __cplusplus 17 | #include 18 | 19 | namespace flora { 20 | 21 | class Dispatcher { 22 | public: 23 | virtual ~Dispatcher() = default; 24 | 25 | virtual void run(bool blocking = false) = 0; 26 | 27 | virtual void close() = 0; 28 | 29 | static std::shared_ptr new_instance(uint32_t flags = 0, 30 | uint32_t msg_buf_size = 0); 31 | }; 32 | 33 | class Poll { 34 | public: 35 | virtual ~Poll() = default; 36 | 37 | virtual int32_t start(std::shared_ptr &dispathcer) = 0; 38 | 39 | virtual void stop() = 0; 40 | 41 | virtual void config(uint32_t opt, ...) = 0; 42 | 43 | static std::shared_ptr new_instance(const char *uri); 44 | }; 45 | 46 | } // namespace flora 47 | 48 | extern "C" { 49 | #endif 50 | 51 | typedef intptr_t flora_poll_t; 52 | typedef intptr_t flora_dispatcher_t; 53 | typedef void (*flora_received_func_t)(flora_dispatcher_t handle, 54 | const char *name, caps_t msg, void *arg); 55 | 56 | // --flora dispatcher functions-- 57 | // 消息转发 58 | 59 | flora_dispatcher_t flora_dispatcher_new(uint32_t flags, uint32_t msg_buf_size); 60 | 61 | void flora_dispatcher_delete(flora_dispatcher_t handle); 62 | 63 | void flora_dispatcher_run(flora_dispatcher_t handle, int32_t block); 64 | 65 | void flora_dispatcher_close(flora_dispatcher_t handle); 66 | 67 | // void flora_dispatcher_forward_msg(flora_dispatcher_t handle, const char 68 | // *name, 69 | // caps_t msg); 70 | 71 | // void flora_dispatcher_subscribe(flora_dispatcher_t handle, const char *name, 72 | // flora_received_func_t callback, void *arg); 73 | 74 | // void flora_dispatcher_unsubscribe(flora_dispatcher_t handle, const char 75 | // *name); 76 | 77 | // --flora poll functions-- 78 | // 侦听连接 79 | // 封装网络通信协议 80 | // 轮询客户端连接消息 81 | 82 | // uri: 支持的schema: 83 | // tcp://$(host):$(port) 84 | // unix:$(domain_name) (unix domain socket) 85 | // return: 86 | // SUCESS 87 | // INVAL: uri参数不合法 88 | // UNSUPP: uri指定的协议不支持 89 | int32_t flora_poll_new(const char *uri, flora_poll_t *result); 90 | 91 | void flora_poll_delete(flora_poll_t handle); 92 | 93 | // msg_buf_size: 大于等于单个msg二进制数据最大长度 94 | // return: 95 | // SUCCESS 96 | // INVAL: handle或dispatcher参数为0 97 | // SYSERR: socket初始化失败 98 | int32_t flora_poll_start(flora_poll_t handle, flora_dispatcher_t dispatcher); 99 | 100 | void flora_poll_stop(flora_poll_t handle); 101 | 102 | #ifdef __cplusplus 103 | } // extern "C" 104 | #endif 105 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function print_prompt 4 | { 5 | cat << CONFEOF 6 | Usage: $0 [OPTION]... [VAR=VALUE]... 7 | 8 | Configuration: 9 | --help display this help and exit 10 | --debug build for debug 11 | --test build unit-tests 12 | --build-dir=DIR build directory 13 | --prefix=PREFIX install prefix 14 | --cmake-modules=DIR directory of cmake modules file exist 15 | --find-root-path=DIR root dir for search dependencies libs 16 | --svc-log-level=* log level of flora service(verbose|debug|info|warning|error|none) 17 | --cli-log-level=* log level of flora client(verbose|debug|info|warning|error|none) 18 | --use-gcc force use gcc compiler (in macosx) 19 | 20 | Dependencies: 21 | --mutils=DIR specify mutils install dir 22 | --ncurses=DIR specify ncurses install dir 23 | --gtest=DIR specify gtest install dir 24 | 25 | Cross Compile: 26 | --toolchain=DIR toolchain install dir 27 | --cross-prefix=PREFIX compiler name prefix 28 | CONFEOF 29 | } 30 | 31 | builddir="build" 32 | cmake_modules_dir="cmake" 33 | 34 | CMAKE_ARGS= 35 | for conf_opt 36 | do 37 | case $conf_opt in 38 | *=?*) conf_optarg=`expr "X$conf_opt" : '[^=]*=\(.*\)'` ;; 39 | *) conf_optarg= ;; 40 | esac 41 | 42 | case $conf_opt in 43 | --help) 44 | print_prompt 45 | exit 0 46 | ;; 47 | --debug) 48 | CMAKE_ARGS=(${CMAKE_ARGS[@]} -DBUILD_DEBUG=ON) 49 | ;; 50 | --test) 51 | CMAKE_ARGS=(${CMAKE_ARGS[@]} -DBUILD_TEST=ON) 52 | ;; 53 | --build-dir=*) 54 | builddir=$conf_optarg 55 | ;; 56 | --prefix=*) 57 | CMAKE_ARGS=(${CMAKE_ARGS[@]} -DCMAKE_INSTALL_PREFIX=$conf_optarg) 58 | ;; 59 | --cmake-modules=*) 60 | cmake_modules_dir=$conf_optarg 61 | ;; 62 | --svc-log-level=*) 63 | CMAKE_ARGS=(${CMAKE_ARGS[@]} -DSVC_LOGLEVEL=$conf_optarg) 64 | ;; 65 | --cli-log-level=*) 66 | CMAKE_ARGS=(${CMAKE_ARGS[@]} -DCLI_LOGLEVEL=$conf_optarg) 67 | ;; 68 | --use-gcc) 69 | CMAKE_ARGS=(${CMAKE_ARGS[@]} -DUSE_GCC=ON) 70 | ;; 71 | --mutils=*) 72 | CMAKE_ARGS=(${CMAKE_ARGS[@]} -DmutilsPrefix=$conf_optarg) 73 | ;; 74 | --ncurses=*) 75 | CMAKE_ARGS=(${CMAKE_ARGS[@]} -DncursesPrefix=$conf_optarg) 76 | ;; 77 | --gtest=*) 78 | CMAKE_ARGS=(${CMAKE_ARGS[@]} -DgtestPrefix=$conf_optarg) 79 | ;; 80 | --toolchain=*) 81 | CMAKE_ARGS=(${CMAKE_ARGS[@]} -DTOOLCHAIN_HOME=$conf_optarg) 82 | CROSS_COMPILE=yes 83 | ;; 84 | --cross-prefix=*) 85 | CMAKE_ARGS=(${CMAKE_ARGS[@]} -DCROSS_PREFIX=$conf_optarg) 86 | ;; 87 | --find-root-path=*) 88 | CMAKE_ARGS=(${CMAKE_ARGS[@]} -DCMAKE_FIND_ROOT_PATH=$conf_optarg) 89 | ;; 90 | esac 91 | done 92 | 93 | CMAKE_ARGS=(${CMAKE_ARGS[@]} -DCUSTOM_CMAKE_MODULES=$cmake_modules_dir) 94 | CUR_DIR=`pwd` 95 | if [ x$CROSS_COMPILE = x"yes" ]; then 96 | CMAKE_ARGS=(${CMAKE_ARGS[@]} -DCMAKE_TOOLCHAIN_FILE=$cmake_modules_dir/toolchain.cmake) 97 | fi 98 | 99 | mkdir -p $builddir 100 | cd $builddir 101 | cmake $CUR_DIR \ 102 | ${CMAKE_ARGS[@]} 103 | -------------------------------------------------------------------------------- /src/sock-adap.cc: -------------------------------------------------------------------------------- 1 | #include "sock-adap.h" 2 | #include "caps.h" 3 | #include "defs.h" 4 | #include "rlog.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace std; 13 | 14 | static void set_write_timeout(int sock, uint32_t timeout) { 15 | if (sock < 0) 16 | return; 17 | struct timeval tv; 18 | tv.tv_sec = timeout / 1000; 19 | tv.tv_usec = (timeout % 1000) * 1000; 20 | setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); 21 | } 22 | 23 | SocketAdapter::SocketAdapter(int sock, uint32_t bufsize, uint32_t flags, 24 | uint32_t wtimeout) : Adapter(flags), socketfd(sock) { 25 | buffer = (int8_t *)mmap(NULL, bufsize, PROT_READ | PROT_WRITE, 26 | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 27 | buf_size = bufsize; 28 | set_write_timeout(sock, wtimeout); 29 | } 30 | 31 | SocketAdapter::~SocketAdapter() { close(); } 32 | 33 | int32_t SocketAdapter::read() { 34 | if (buffer == nullptr) 35 | return SOCK_ADAPTER_ECLOSED; 36 | ssize_t c = ::read(socketfd, buffer + cur_size, buf_size - cur_size); 37 | if (c <= 0) { 38 | if (c == 0) { 39 | KLOGD(TAG, "socket closed by remote"); 40 | } else { 41 | KLOGE(TAG, "read socket failed: %s", strerror(errno)); 42 | } 43 | return SOCK_ADAPTER_ECLOSED; 44 | } 45 | cur_size += c; 46 | #ifdef FLORA_DEBUG 47 | recv_bytes += c; 48 | #endif 49 | return SOCK_ADAPTER_SUCCESS; 50 | } 51 | 52 | int32_t SocketAdapter::next_frame(Frame &frame) { 53 | if (buffer == nullptr) 54 | return SOCK_ADAPTER_ECLOSED; 55 | uint32_t sz = cur_size - frame_begin; 56 | if (sz < HEADER_SIZE) 57 | goto nomore; 58 | uint32_t version; 59 | uint32_t length; 60 | if (Caps::binary_info(buffer + frame_begin, &version, &length) != 61 | CAPS_SUCCESS) 62 | return SOCK_ADAPTER_EPROTO; 63 | if (length > buf_size) 64 | return SOCK_ADAPTER_ENOBUF; 65 | if (length > sz) 66 | goto nomore; 67 | frame.data = buffer + frame_begin; 68 | frame.size = length; 69 | frame_begin += length; 70 | #ifdef FLORA_DEBUG 71 | ++recv_times; 72 | #endif 73 | return SOCK_ADAPTER_SUCCESS; 74 | 75 | nomore: 76 | if (frame_begin < cur_size) { 77 | memmove(buffer, buffer + frame_begin, sz); 78 | } 79 | frame_begin = 0; 80 | cur_size = sz; 81 | return SOCK_ADAPTER_ENOMORE; 82 | } 83 | 84 | void SocketAdapter::close() { 85 | lock_guard locker(write_mutex); 86 | close_nolock(); 87 | } 88 | 89 | void SocketAdapter::close_nolock() { 90 | if (buffer) { 91 | munmap(buffer, buf_size); 92 | buffer = nullptr; 93 | if (socketfd >= 0) 94 | ::shutdown(socketfd, SHUT_RDWR); 95 | #ifdef FLORA_DEBUG 96 | KLOGI(TAG, "socket adapter %s: recv times = %u, recv bytes = %u", 97 | info ? info->name.c_str() : "", recv_times, recv_bytes); 98 | #endif 99 | } 100 | } 101 | 102 | bool SocketAdapter::closed() { 103 | lock_guard locker(write_mutex); 104 | return buffer == nullptr; 105 | } 106 | 107 | int32_t SocketAdapter::write(const void *data, uint32_t size) { 108 | lock_guard locker(write_mutex); 109 | if (buffer == nullptr) 110 | return -1; 111 | auto r = ::write(socketfd, data, size); 112 | if (r < 0) { 113 | KLOGE(TAG, "write to socket failed: %s", strerror(errno)); 114 | close_nolock(); 115 | if (errno == EAGAIN) 116 | return -2; 117 | return -1; 118 | } 119 | return 0; 120 | } 121 | -------------------------------------------------------------------------------- /monitor/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "xmopt.h" 6 | #include "flora-agent.h" 7 | 8 | using namespace std; 9 | using namespace flora; 10 | 11 | static const uint32_t COLUMN_WIDTH[] = {8, 32}; 12 | #define COLUMN_PAD 1 13 | static char textBuffer[64]; 14 | static const char *COLUMN_HEADER_TEXT[] = {"PID", "NAME"}; 15 | 16 | typedef struct { 17 | string floraUri{"unix:/var/run/flora.sock"}; 18 | } CmdlineArgs; 19 | 20 | static bool parseCmdline(int argc, char **argv, CmdlineArgs &res) { 21 | XMOptions options; 22 | if (!options.parse(argc, argv)) { 23 | return false; 24 | } 25 | auto it = options.find(nullptr); 26 | it.next(); 27 | auto opt = it.next(); 28 | if (opt != nullptr) 29 | res.floraUri = opt->value(); 30 | return true; 31 | } 32 | 33 | class FloraClientInfo { 34 | public: 35 | uint32_t id = 0; 36 | int32_t pid = 0; 37 | string name; 38 | uint32_t flags = 0; 39 | }; 40 | 41 | class MonitorView : public flora::MonitorCallback { 42 | public: 43 | void list_all(std::vector &items) { 44 | auto it = items.begin(); 45 | while (it != items.end()) { 46 | auto fcit = floraClients.emplace(floraClients.end()); 47 | fcit->id = it->id; 48 | fcit->pid = it->pid; 49 | fcit->name = it->name; 50 | fcit->flags = it->flags; 51 | ++it; 52 | } 53 | updateMonitorScreen(); 54 | } 55 | 56 | void list_add(MonitorListItem &item) { 57 | auto fcit = floraClients.emplace(floraClients.end()); 58 | fcit->id = item.id; 59 | fcit->pid = item.pid; 60 | fcit->name = item.name; 61 | fcit->flags = item.flags; 62 | updateMonitorScreen(); 63 | } 64 | 65 | void list_remove(uint32_t id) { 66 | auto it = floraClients.begin(); 67 | while (it != floraClients.end()) { 68 | if (it->id == id) { 69 | floraClients.erase(it); 70 | updateMonitorScreen(); 71 | break; 72 | } 73 | ++it; 74 | } 75 | } 76 | 77 | private: 78 | void updateMonitorScreen() { 79 | clear(); 80 | printTabHeader(); 81 | auto it = floraClients.begin(); 82 | while (it != floraClients.end()) { 83 | printTabLine(*it); 84 | ++it; 85 | } 86 | refresh(); 87 | } 88 | 89 | void printTabHeader() { 90 | uint32_t numCol = sizeof(COLUMN_WIDTH) / sizeof(COLUMN_WIDTH[0]); 91 | uint32_t i; 92 | 93 | for (i = 0; i < numCol; ++i) { 94 | printTabCell(i, COLUMN_HEADER_TEXT[i]); 95 | } 96 | printw("\n"); 97 | } 98 | 99 | void printTabLine(FloraClientInfo &info) { 100 | char buf[16]; 101 | snprintf(buf, sizeof(buf), "%d", info.pid); 102 | printTabCell(0, buf); 103 | printTabCell(1, info.name.c_str()); 104 | printw("\n"); 105 | } 106 | 107 | void printTabCell(int32_t idx, const char *content) { 108 | uint32_t width = COLUMN_WIDTH[idx] + COLUMN_PAD; 109 | int32_t len; 110 | memset(textBuffer, ' ', width); 111 | textBuffer[width] = '\0'; 112 | len = strlen(content); 113 | if (len > COLUMN_WIDTH[idx]) 114 | len = COLUMN_WIDTH[idx]; 115 | memcpy(textBuffer, content, len); 116 | printw("%s", textBuffer); 117 | } 118 | 119 | private: 120 | list floraClients; 121 | }; 122 | 123 | static void doMonitor(CmdlineArgs &args) { 124 | initscr(); 125 | raw(); 126 | keypad(stdscr, TRUE); 127 | noecho(); 128 | 129 | Agent agent; 130 | MonitorView monitorView; 131 | string uri = args.floraUri + "#flora-monitor"; 132 | agent.config(FLORA_AGENT_CONFIG_URI, uri.c_str()); 133 | agent.config(FLORA_AGENT_CONFIG_MONITOR, FLORA_CLI_FLAG_MONITOR, 134 | &monitorView); 135 | agent.start(); 136 | 137 | int c; 138 | while (true) { 139 | c = getch(); 140 | if (c < 0) { 141 | break; 142 | } 143 | if (c == 'q' || c == 'Q') 144 | break; 145 | } 146 | 147 | agent.close(); 148 | endwin(); 149 | } 150 | 151 | int main(int argc, char **argv) { 152 | CmdlineArgs cmdlineArgs; 153 | 154 | if (!parseCmdline(argc, argv, cmdlineArgs)) { 155 | return 1; 156 | } 157 | 158 | doMonitor(cmdlineArgs); 159 | return 0; 160 | } 161 | -------------------------------------------------------------------------------- /android/java/com/rokid/flora/Client.java: -------------------------------------------------------------------------------- 1 | package com.rokid.flora; 2 | 3 | import java.util.List; 4 | import java.util.Iterator; 5 | 6 | public class Client { 7 | public Client() { 8 | } 9 | 10 | public void finalize() { 11 | destroy(); 12 | } 13 | 14 | public int connect(String uri, Callback cb, int msgBufSize) { 15 | if (uri == null) 16 | return ERROR_INVALID_PARAM; 17 | int r = native_connect(uri, msgBufSize); 18 | if (r == SUCCESS) 19 | callback = cb; 20 | return r; 21 | } 22 | 23 | public void destroy() { 24 | native_destroy(native_handle); 25 | native_handle = 0; 26 | } 27 | 28 | public int subscribe(String name, int type) { 29 | if (native_handle == 0) 30 | return ERROR_CONNECTION; 31 | if (name == null) 32 | return ERROR_INVALID_PARAM; 33 | return native_subscribe(native_handle, name, type); 34 | } 35 | 36 | public int unsubscribe(String name, int type) { 37 | if (native_handle == 0) 38 | return ERROR_CONNECTION; 39 | if (name == null) 40 | return ERROR_INVALID_PARAM; 41 | return native_unsubscribe(native_handle, name, type); 42 | } 43 | 44 | public int post(String name, Caps msg, int type) { 45 | if (native_handle == 0) 46 | return ERROR_CONNECTION; 47 | if (name == null) 48 | return ERROR_INVALID_PARAM; 49 | return native_post(native_handle, name, 50 | msg == null ? 0 : msg.native_handle, type); 51 | } 52 | 53 | public int get(String name, Caps msg, List replys, int timeout) { 54 | if (native_handle == 0) 55 | return ERROR_CONNECTION; 56 | if (name == null || replys == null || timeout < 0) 57 | return ERROR_INVALID_PARAM; 58 | return native_get(native_handle, name, 59 | msg == null ? 0 : msg.native_handle, replys, timeout); 60 | } 61 | 62 | public void destroyReplys(List replys) { 63 | if (replys == null) 64 | return; 65 | Iterator it = replys.iterator(); 66 | Reply reply; 67 | while (it.hasNext()) { 68 | reply = it.next(); 69 | if (reply != null) 70 | reply.msg.destroy(); 71 | } 72 | } 73 | 74 | public static interface Callback { 75 | public void receivedPost(String name, Caps msg, int type); 76 | public Reply receivedGet(String name, Caps msg); 77 | public void disconnected(); 78 | 79 | public static class Reply { 80 | public int retCode; 81 | public Caps msg; 82 | } 83 | } 84 | 85 | public static class Reply { 86 | public int retCode; 87 | public Caps msg; 88 | public String extra; 89 | } 90 | 91 | private void nativePostCallback(String name, long msg, int type) { 92 | Caps msgcaps = new Caps(msg); 93 | if (callback != null) 94 | callback.receivedPost(name, msgcaps, type); 95 | msgcaps.destroy(); 96 | } 97 | 98 | private int nativeGetCallback(String name, long msg, long reply) { 99 | Caps msgcaps = new Caps(msg); 100 | if (callback != null) { 101 | Callback.Reply rep = callback.receivedGet(name, msgcaps); 102 | msgcaps.destroy(); 103 | if (rep != null) { 104 | if (rep.msg != null) { 105 | native_set_pointer(rep.msg.native_handle, reply); 106 | rep.msg.destroy(); 107 | } 108 | return rep.retCode; 109 | } 110 | } 111 | msgcaps.destroy(); 112 | return SUCCESS; 113 | } 114 | 115 | private void nativeDisconnectCallback() { 116 | if (callback != null) 117 | callback.disconnected(); 118 | } 119 | 120 | private static native void native_init(); 121 | 122 | private native int native_connect(String uri, int bufsize); 123 | 124 | private native int native_subscribe(long h, String name, int type); 125 | 126 | private native int native_unsubscribe(long h, String name, int type); 127 | 128 | private native int native_post(long h, String name, long msg, int type); 129 | 130 | private native int native_get(long h, String name, long msg, List replys, int timeout); 131 | 132 | private native void native_set_pointer(long src, long dst); 133 | 134 | private native void native_destroy(long h); 135 | 136 | private Callback callback; 137 | 138 | private long native_handle = 0; 139 | 140 | public static final int SUCCESS = 0; 141 | public static final int ERROR_AUTH = -1; 142 | public static final int ERROR_INVALID_PARAM = -2; 143 | public static final int ERROR_CONNECTION = -3; 144 | public static final int ERROR_TIMEOUT = -4; 145 | // 'get'目标事件不存在 146 | public static final int ERROR_NO_TARGET = -5; 147 | 148 | static { 149 | System.loadLibrary("flora-cli.jni"); 150 | native_init(); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/sock-conn.cc: -------------------------------------------------------------------------------- 1 | #include "sock-conn.h" 2 | #include "defs.h" 3 | #include "rlog.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | SocketConn::SocketConn(uint32_t rtmo) : recv_timeout(rtmo) {} 18 | 19 | SocketConn::~SocketConn() { 20 | if (sock >= 0) { 21 | ::close(sock); 22 | } 23 | } 24 | 25 | bool SocketConn::connect(const std::string &name) { 26 | #ifdef __APPLE__ 27 | int fd = socket(AF_UNIX, SOCK_STREAM, 0); 28 | #else 29 | int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 30 | #endif 31 | if (fd < 0) { 32 | KLOGE(TAG, "socket create failed: %s", strerror(errno)); 33 | return false; 34 | } 35 | #ifdef __APPLE__ 36 | auto f = fcntl(fd, F_GETFD); 37 | f |= FD_CLOEXEC; 38 | fcntl(fd, F_SETFD, f); 39 | #endif 40 | // set socket recv timeout 41 | if (recv_timeout > 0) { 42 | struct timeval tv; 43 | tv.tv_sec = recv_timeout / 1000; 44 | tv.tv_usec = (recv_timeout % 1000) * 1000; 45 | setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); 46 | } 47 | struct sockaddr_un addr; 48 | memset(&addr, 0, sizeof(addr)); 49 | addr.sun_family = AF_UNIX; 50 | strcpy(addr.sun_path, name.c_str()); 51 | if (::connect(fd, (sockaddr *)&addr, sizeof(addr)) < 0) { 52 | KLOGE(TAG, "socket connect %s failed: %s", name.c_str(), strerror(errno)); 53 | ::close(fd); 54 | return false; 55 | } 56 | sock = fd; 57 | sock_ready = true; 58 | return true; 59 | } 60 | 61 | bool SocketConn::connect(const std::string &host, int32_t port) { 62 | #ifdef __APPLE__ 63 | int fd = socket(AF_INET, SOCK_STREAM, 0); 64 | #else 65 | int fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); 66 | #endif 67 | if (fd < 0) { 68 | KLOGE(TAG, "socket create failed: %s", strerror(errno)); 69 | return false; 70 | } 71 | #ifdef __APPLE__ 72 | auto f = fcntl(fd, F_GETFD); 73 | f |= FD_CLOEXEC; 74 | fcntl(fd, F_SETFD, f); 75 | #endif 76 | // set socket recv timeout 77 | if (recv_timeout > 0) { 78 | struct timeval tv; 79 | tv.tv_sec = recv_timeout / 1000; 80 | tv.tv_usec = (recv_timeout % 1000) * 1000; 81 | setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); 82 | } 83 | struct sockaddr_in addr; 84 | struct hostent *hp; 85 | hp = gethostbyname(host.c_str()); 86 | if (hp == nullptr) { 87 | KLOGE(TAG, "dns failed for host %s: %s", host.c_str(), strerror(errno)); 88 | ::close(fd); 89 | return false; 90 | } 91 | memset(&addr, 0, sizeof(addr)); 92 | addr.sin_family = AF_INET; 93 | memcpy(&addr.sin_addr, hp->h_addr_list[0], sizeof(addr.sin_addr)); 94 | addr.sin_port = htons(port); 95 | if (::connect(fd, (sockaddr *)&addr, sizeof(addr)) < 0) { 96 | KLOGE(TAG, "socket connect failed: %s", strerror(errno)); 97 | ::close(fd); 98 | return false; 99 | } 100 | sock = fd; 101 | sock_ready = true; 102 | return true; 103 | } 104 | 105 | bool SocketConn::send(const void *data, uint32_t size) { 106 | lock_guard locker(write_mutex); 107 | if (!sock_ready) { 108 | return false; 109 | } 110 | ssize_t c = ::write(sock, data, size); 111 | if (c < 0) { 112 | KLOGE(TAG, "write to socket failed: %s", strerror(errno)); 113 | return false; 114 | } 115 | if (c == 0) { 116 | KLOGE(TAG, "write to socket failed: remote closed"); 117 | return false; 118 | } 119 | return true; 120 | } 121 | 122 | int32_t SocketConn::recv(void *data, uint32_t size) { 123 | unique_lock locker(write_mutex); 124 | if (!sock_ready) 125 | return -1; 126 | locker.unlock(); 127 | 128 | ssize_t c; 129 | do { 130 | c = ::read(sock, data, size); 131 | if (c < 0) { 132 | if (errno == EAGAIN) { 133 | KLOGI(TAG, "read socket timeout"); 134 | return -2; 135 | } 136 | if (errno == EINTR) { 137 | KLOGE(TAG, "read socket failed: %s", strerror(errno)); 138 | continue; 139 | } 140 | KLOGE(TAG, "read socket failed: %s", strerror(errno)); 141 | } else if (c == 0) { 142 | KLOGD(TAG, "read socket failed: remote closed"); 143 | } 144 | break; 145 | } while (true); 146 | return c; 147 | } 148 | 149 | void SocketConn::close() { 150 | lock_guard locker(write_mutex); 151 | if (sock_ready) { 152 | sock_ready = false; 153 | ::shutdown(sock, SHUT_RDWR); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/beep-sock-poll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "defs.h" 4 | #include "rlog.h" 5 | #include "sock-poll.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace flora { 15 | namespace internal { 16 | 17 | class ActiveAdapter { 18 | public: 19 | std::chrono::steady_clock::time_point lastest_action_tp; 20 | std::shared_ptr adapter; 21 | }; 22 | typedef std::list ActiveAdapterList; 23 | 24 | class BeepSocketPoll : public SocketPoll { 25 | public: 26 | BeepSocketPoll(const std::string &host, int32_t port) 27 | : SocketPoll(host, port) {} 28 | 29 | void config(uint32_t opt, ...) { 30 | va_list ap; 31 | va_start(ap, opt); 32 | switch (opt) { 33 | case FLORA_POLL_OPT_KEEPALIVE_TIMEOUT: 34 | options.beep_timeout = va_arg(ap, uint32_t); 35 | break; 36 | } 37 | va_end(ap); 38 | } 39 | 40 | private: 41 | int32_t do_poll(fd_set *rfds, int max_fd) { 42 | int r; 43 | struct timeval tv; 44 | struct timeval *ptv; 45 | std::chrono::steady_clock::time_point nowtp; 46 | 47 | while (true) { 48 | if (active_adapters.empty()) { 49 | #ifdef SELECT_BLOCK_IF_FD_CLOSED 50 | tv.tv_sec = 5; 51 | tv.tv_usec = 0; 52 | ptv = &tv; 53 | #else 54 | ptv = nullptr; 55 | #endif 56 | } else { 57 | nowtp = std::chrono::steady_clock::now(); 58 | obtain_timeout(nowtp, &tv); 59 | ptv = &tv; 60 | } 61 | r = select(max_fd, rfds, nullptr, nullptr, ptv); 62 | if (r < 0) { 63 | if (errno == EAGAIN) { 64 | sleep(1); 65 | continue; 66 | } 67 | KLOGE(TAG, "select failed: %s", strerror(errno)); 68 | } 69 | nowtp = std::chrono::steady_clock::now(); 70 | shutdown_timeout_adapter(nowtp); 71 | break; 72 | } 73 | return r; 74 | } 75 | 76 | std::shared_ptr do_accept(int lfd) { 77 | auto adap = SocketPoll::do_accept(lfd); 78 | if (adap != nullptr) { 79 | auto it = active_adapters.emplace(active_adapters.end()); 80 | it->adapter = adap; 81 | it->lastest_action_tp = std::chrono::steady_clock::now(); 82 | return adap; 83 | } 84 | return adap; 85 | } 86 | 87 | bool do_read(std::shared_ptr &adap) { 88 | bool r = SocketPoll::do_read(adap); 89 | auto it = active_adapters.begin(); 90 | while (it != active_adapters.end()) { 91 | if (it->adapter == adap) 92 | break; 93 | ++it; 94 | } 95 | if (it != active_adapters.end()) { 96 | if (r) { 97 | // move adapter to end of list 98 | // adapter sorted by lastest_action_tp 99 | it->lastest_action_tp = std::chrono::steady_clock::now(); 100 | if (it != active_adapters.rbegin().base()) 101 | active_adapters.splice(active_adapters.end(), active_adapters, it); 102 | } else { 103 | active_adapters.erase(it); 104 | } 105 | } 106 | return r; 107 | } 108 | 109 | void obtain_timeout(std::chrono::steady_clock::time_point &nowtp, 110 | struct timeval *tv) { 111 | auto dur = std::chrono::duration_cast( 112 | std::chrono::milliseconds(options.beep_timeout) - 113 | (nowtp - active_adapters.front().lastest_action_tp)); 114 | tv->tv_sec = dur.count() / 1000; 115 | tv->tv_usec = (dur.count() % 1000) * 1000; 116 | } 117 | 118 | void shutdown_timeout_adapter(std::chrono::steady_clock::time_point &nowtp) { 119 | #ifdef FLORA_DEBUG 120 | std::chrono::steady_clock::time_point dnowtp = nowtp; 121 | KLOGD(TAG, "before shutdown timeout adapters, timeout = %u", 122 | options.beep_timeout); 123 | print_active_adapters(dnowtp); 124 | #endif 125 | nowtp -= std::chrono::milliseconds(options.beep_timeout); 126 | auto it = active_adapters.begin(); 127 | while (it != active_adapters.end()) { 128 | if (nowtp >= it->lastest_action_tp) { 129 | int fd = std::static_pointer_cast(it->adapter)->socket(); 130 | KLOGW(TAG, "tcp socket %d keepalive timeout, shutdown!", fd); 131 | ::shutdown(fd, SHUT_RDWR); 132 | it = active_adapters.erase(it); 133 | continue; 134 | } 135 | break; 136 | } 137 | #ifdef FLORA_DEBUG 138 | KLOGD(TAG, "after shutdown timeout adapters, timeout = %u", 139 | options.beep_timeout); 140 | print_active_adapters(dnowtp); 141 | #endif 142 | } 143 | 144 | #ifdef FLORA_DEBUG 145 | void print_active_adapters(std::chrono::steady_clock::time_point &nowtp) { 146 | auto it = active_adapters.begin(); 147 | while (it != active_adapters.end()) { 148 | KLOGD(TAG, "%p: %d", it->adapter.get(), 149 | std::chrono::duration_cast( 150 | nowtp - it->lastest_action_tp) 151 | .count()); 152 | ++it; 153 | } 154 | } 155 | #endif 156 | 157 | private: 158 | class Options { 159 | public: 160 | uint32_t beep_timeout = 60000; 161 | }; 162 | ActiveAdapterList active_adapters; 163 | Options options; 164 | }; 165 | 166 | } // namespace internal 167 | } // namespace flora 168 | -------------------------------------------------------------------------------- /src/cli.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "caps.h" 4 | #include "conn.h" 5 | #include "defs.h" 6 | #include "flora-cli.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace flora { 17 | namespace internal { 18 | 19 | typedef std::function RespCallback; 20 | typedef struct { 21 | int32_t id; 22 | int32_t rcode; 23 | Response *result; 24 | RespCallback callback; 25 | } PendingRequest; 26 | typedef std::list PendingRequestList; 27 | 28 | class Client : public flora::Client { 29 | public: 30 | explicit Client(flora::ClientOptions *opts); 31 | 32 | ~Client() noexcept; 33 | 34 | int32_t connect(const char *uri, ClientCallback *ccb, MonitorCallback *mcb); 35 | 36 | int32_t close(bool passive); 37 | 38 | void set_weak_ptr(std::shared_ptr &ptr) { this_weak_ptr = ptr; } 39 | 40 | void send_reply(int32_t callid, int32_t code, std::shared_ptr &data); 41 | 42 | // implementation of flora::Client 43 | int32_t subscribe(const char *name); 44 | 45 | int32_t unsubscribe(const char *name); 46 | 47 | int32_t declare_method(const char *name); 48 | 49 | int32_t remove_method(const char *name); 50 | 51 | int32_t post(const char *name, std::shared_ptr &msg, uint32_t msgtype); 52 | 53 | int32_t call(const char *name, std::shared_ptr &msg, const char *target, 54 | Response &reply, uint32_t timeout); 55 | 56 | int32_t call(const char *name, std::shared_ptr &msg, const char *target, 57 | std::function &&cb, uint32_t timeout); 58 | 59 | int32_t call(const char *name, std::shared_ptr &msg, const char *target, 60 | std::function &cb, uint32_t timeout); 61 | 62 | int get_socket() const; 63 | 64 | private: 65 | int32_t auth(const std::string &extra, uint32_t flags); 66 | 67 | void recv_loop(); 68 | 69 | void keepalive_loop(); 70 | 71 | bool handle_received(int32_t size); 72 | 73 | bool handle_cmd_before_auth(int32_t cmd, std::shared_ptr &resp); 74 | 75 | bool handle_cmd_after_auth(int32_t cmd, std::shared_ptr &resp); 76 | 77 | void iclose(bool passive, int32_t err); 78 | 79 | bool handle_monitor_list_all(std::shared_ptr &resp); 80 | bool handle_monitor_list_add(std::shared_ptr &resp); 81 | bool handle_monitor_list_remove(std::shared_ptr &resp); 82 | bool handle_monitor_sub_all(std::shared_ptr &resp); 83 | bool handle_monitor_sub_add(std::shared_ptr &resp); 84 | bool handle_monitor_sub_remove(std::shared_ptr &resp); 85 | bool handle_monitor_decl_all(std::shared_ptr &resp); 86 | bool handle_monitor_decl_add(std::shared_ptr &resp); 87 | bool handle_monitor_decl_remove(std::shared_ptr &resp); 88 | bool handle_monitor_post(std::shared_ptr &resp); 89 | bool handle_monitor_call(std::shared_ptr &resp); 90 | 91 | void ping(); 92 | 93 | private: 94 | int8_t *sbuffer = nullptr; 95 | int8_t *rbuffer = nullptr; 96 | uint32_t rbuf_off = 0; 97 | ClientOptions options; 98 | std::shared_ptr connection; 99 | std::thread recv_thread; 100 | std::mutex req_mutex; 101 | std::condition_variable req_reply_cond; 102 | PendingRequestList pending_requests; 103 | std::thread keepalive_thread; 104 | std::mutex ka_mutex; 105 | std::condition_variable ka_cond; 106 | int32_t reqseq = 0; 107 | uint32_t serialize_flags = 0; 108 | int32_t close_reason = 0; 109 | std::weak_ptr this_weak_ptr; 110 | std::thread::id callback_thr_id; 111 | MonitorCallback *mon_callback = nullptr; 112 | typedef bool (flora::internal::Client::*CmdHandler)( 113 | int32_t cmd, std::shared_ptr &resp); 114 | CmdHandler cmd_handler = &Client::handle_cmd_before_auth; 115 | class AuthResult { 116 | public: 117 | std::mutex amutex; 118 | std::condition_variable acond; 119 | int32_t result = FLORA_CLI_EAUTH; 120 | }; 121 | AuthResult *auth_result = nullptr; 122 | std::mutex send_mutex; 123 | 124 | typedef bool (flora::internal::Client::*MonitorHandler)( 125 | std::shared_ptr &); 126 | static MonitorHandler monitor_handlers[MONITOR_SUBTYPE_NUM]; 127 | 128 | public: 129 | std::string auth_extra; 130 | ClientCallback *cli_callback = nullptr; 131 | static thread_local uint64_t tag; 132 | static thread_local std::string sender_name; 133 | #ifdef FLORA_DEBUG 134 | uint32_t post_times = 0; 135 | uint32_t post_bytes = 0; 136 | uint32_t req_times = 0; 137 | uint32_t req_bytes = 0; 138 | uint32_t send_times = 0; 139 | uint32_t send_bytes = 0; 140 | #endif 141 | }; 142 | 143 | class ReplyImpl : public flora::Reply { 144 | public: 145 | ReplyImpl(std::shared_ptr &&c, int32_t id); 146 | 147 | ~ReplyImpl() noexcept; 148 | 149 | void write_code(int32_t code); 150 | 151 | void write_data(std::shared_ptr &data); 152 | 153 | void end(); 154 | 155 | void end(int32_t code); 156 | 157 | void end(int32_t code, std::shared_ptr &data); 158 | 159 | private: 160 | void send(); 161 | 162 | private: 163 | std::shared_ptr client; 164 | int32_t ret_code = FLORA_CALL_RETCODE_NORESP; 165 | std::shared_ptr data; 166 | int32_t callid = 0; 167 | }; 168 | 169 | } // namespace internal 170 | } // namespace flora 171 | 172 | typedef struct { 173 | std::shared_ptr cxxreply; 174 | } CReply; 175 | -------------------------------------------------------------------------------- /src/disp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "adap.h" 4 | #include "caps.h" 5 | #include "defs.h" 6 | #include "flora-svc.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace flora { 17 | namespace internal { 18 | 19 | typedef std::list> AdapterList; 20 | typedef std::map SubscriptionMap; 21 | typedef struct { 22 | std::shared_ptr data; 23 | } PersistMsg; 24 | typedef std::map PersistMsgMap; 25 | typedef std::map> NamedAdapterMap; 26 | typedef std::pair, std::shared_ptr> CmdPacket; 27 | typedef std::list CmdPacketList; 28 | typedef struct { 29 | int32_t svrid; 30 | int32_t cliid; 31 | std::shared_ptr sender; 32 | std::shared_ptr target; 33 | std::chrono::steady_clock::time_point discard_tp; 34 | } PendingCall; 35 | typedef std::list PendingCallList; 36 | typedef std::map AdapterInfoMap; 37 | 38 | class Dispatcher : public flora::Dispatcher { 39 | public: 40 | Dispatcher(uint32_t flags, uint32_t bufsize); 41 | 42 | ~Dispatcher() noexcept; 43 | 44 | bool put(const void *data, uint32_t size, std::shared_ptr &sender); 45 | 46 | void run(bool blocking); 47 | 48 | void close(); 49 | 50 | inline uint32_t max_msg_size() const { return buf_size; } 51 | 52 | void erase_adapter(std::shared_ptr &adapter); 53 | 54 | private: 55 | bool handle_auth_req(std::shared_ptr &msg_caps, 56 | std::shared_ptr &sender); 57 | 58 | bool handle_subscribe_req(std::shared_ptr &msg_caps, 59 | std::shared_ptr &sender); 60 | 61 | bool handle_unsubscribe_req(std::shared_ptr &msg_caps, 62 | std::shared_ptr &sender); 63 | 64 | bool handle_declare_method(std::shared_ptr &msg_caps, 65 | std::shared_ptr &sender); 66 | 67 | bool handle_remove_method(std::shared_ptr &msg_caps, 68 | std::shared_ptr &sender); 69 | 70 | bool handle_post_req(std::shared_ptr &msg_caps, 71 | std::shared_ptr &sender); 72 | 73 | bool handle_call_req(std::shared_ptr &msg_caps, 74 | std::shared_ptr &sender); 75 | 76 | bool handle_reply_req(std::shared_ptr &msg_caps, 77 | std::shared_ptr &sender); 78 | 79 | bool handle_ping_req(std::shared_ptr &msg_caps, 80 | std::shared_ptr &sender); 81 | 82 | bool add_adapter(const std::string &name, uint32_t flags, int32_t pid, 83 | std::shared_ptr &adapter); 84 | 85 | void add_monitor(const std::string &name, uint32_t flags, 86 | std::shared_ptr &adapter); 87 | 88 | void handle_cmds(); 89 | 90 | void handle_cmd(std::shared_ptr &caps, 91 | std::shared_ptr &sender); 92 | 93 | void add_pending_call(int32_t svrid, int32_t cliid, 94 | std::shared_ptr &sender, 95 | std::shared_ptr &target, uint32_t timeout); 96 | 97 | void pending_call_timeout(PendingCall &pc); 98 | 99 | void discard_pending_calls(); 100 | 101 | bool post_msg(const std::string &name, uint32_t type, 102 | std::shared_ptr &args, Adapter *sender); 103 | 104 | void write_post_msg_to_adapters( 105 | const std::string &name, uint32_t type, std::shared_ptr &args, 106 | uint64_t tag, uint32_t flags, AdapterList &adapters, 107 | const char *sender_name); 108 | 109 | void do_erase_adapter(std::shared_ptr &sender); 110 | 111 | void write_monitor_data(uint32_t flags, std::shared_ptr &adapter); 112 | 113 | void write_monitor_list(std::shared_ptr &adapter); 114 | 115 | void write_monitor_list_add(std::shared_ptr &newitem, 116 | Adapter *monitor); 117 | 118 | void write_monitor_list_remove(uint32_t id, Adapter *monitor); 119 | 120 | void write_monitor_list_add(std::shared_ptr &newitem); 121 | 122 | void write_monitor_list_remove(uint32_t id); 123 | 124 | void check_subscriptions(); 125 | 126 | void clear_sub_gabages(); 127 | 128 | private: 129 | SubscriptionMap subscriptions; 130 | PersistMsgMap persist_msgs; 131 | NamedAdapterMap named_adapters; 132 | int8_t *buffer; 133 | uint32_t buf_size; 134 | CmdPacketList cmd_packets; 135 | std::mutex cmd_mutex; 136 | std::condition_variable cmd_cond; 137 | std::thread run_thread; 138 | PendingCallList pending_calls; 139 | int32_t reqseq = 0; 140 | AdapterInfoMap adapter_infos; 141 | AdapterInfoMap monitors; 142 | uint32_t flags; 143 | // if now timepoint more than handle_cmd_tp 10 seconds when erase adapter 144 | // traversal map of subscriptions, clear gabages 145 | std::chrono::steady_clock::time_point handle_cmd_tp; 146 | // the variable +1 for each erased adapter 147 | // if the variable >= CLEAR_SUBSCRIPTION_THRESHOLD, 148 | // traversal map of subscriptions, clear gabages 149 | uint32_t clear_sub_prio{0}; 150 | bool working = false; 151 | 152 | static bool (Dispatcher::*msg_handlers[MSG_HANDLER_COUNT])( 153 | std::shared_ptr &, std::shared_ptr &); 154 | }; 155 | 156 | } // namespace internal 157 | } // namespace flora 158 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.2) 2 | project(flora) 3 | set(VERSION 0.1) 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | option(USE_GCC "force use gcc" OFF) 7 | if (USE_GCC) 8 | SET(CMAKE_C_COMPILER gcc) 9 | SET(CMAKE_CXX_COMPILER g++) 10 | endif() 11 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) 12 | set(COMPILER_IS_CLANG TRUE) 13 | else() 14 | set(COMPILER_IS_CLANG FALSE) 15 | endif() 16 | include (${CUSTOM_CMAKE_MODULES}/common.mk) 17 | 18 | option(BUILD_DEBUG "debug or release" OFF) 19 | option(BUILD_TEST "build tests" OFF) 20 | option(DEBUG_FOR_YODAV8 "debug for yoda v8" OFF) 21 | 22 | function(parseLogLevel varName) 23 | if (NOT ${varName}) 24 | set(${varName} 2 PARENT_SCOPE) 25 | else() 26 | unset(llv CACHE) 27 | if (${varName} STREQUAL "verbose") 28 | set(llv 0) 29 | elseif (${varName} STREQUAL "debug") 30 | set(llv 1) 31 | elseif (${varName} STREQUAL "info") 32 | set(llv 2) 33 | elseif (${varName} STREQUAL "warning") 34 | set(llv 3) 35 | elseif (${varName} STREQUAL "error") 36 | set(llv 4) 37 | elseif (${varName} STREQUAL "none") 38 | set(llv 5) 39 | else () 40 | set(llv 2) 41 | endif() 42 | set(${varName} ${llv} PARENT_SCOPE) 43 | endif() 44 | endfunction(parseLogLevel) 45 | 46 | parseLogLevel(SVC_LOGLEVEL) 47 | parseLogLevel(CLI_LOGLEVEL) 48 | 49 | findPackage(mutils REQUIRED 50 | HINTS ${mutilsPrefix} 51 | INC_PATH_SUFFIX log caps misc 52 | HEADERS rlog.h caps.h uri.h 53 | SHARED_LIBS rlog caps 54 | STATIC_LIBS misc 55 | ) 56 | 57 | findPackage(ncurses 58 | HINTS ${ncursesPrefix} 59 | HEADERS curses.h 60 | SHARED_LIBS ncurses 61 | ) 62 | 63 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") 64 | if (NOT COMPILER_IS_CLANG) 65 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") 66 | endif() 67 | if (BUILD_DEBUG) 68 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -DFLORA_DEBUG") 69 | else() 70 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os") 71 | endif() 72 | if (CROSS_COMPILE_CXXFLAGS) 73 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CROSS_COMPILE_CXXFLAGS}") 74 | endif() 75 | 76 | #file(GLOB_RECURSE UWS_SOURCES 77 | # src/*.h 78 | # src/*.cpp 79 | #) 80 | set(flora_svc_SOURCES 81 | include/flora-svc.h 82 | include/flora-cli.h 83 | src/defs.h 84 | src/adap.h 85 | src/sock-adap.h 86 | src/sock-adap.cc 87 | src/poll.cc 88 | src/sock-poll.h 89 | src/sock-poll.cc 90 | src/beep-sock-poll.h 91 | src/disp.h 92 | src/disp.cc 93 | src/ser-helper.h 94 | src/ser-helper.cc 95 | ) 96 | add_library(flora-svc-static STATIC 97 | ${flora_svc_SOURCES} 98 | ) 99 | set_target_properties(flora-svc-static PROPERTIES 100 | OUTPUT_NAME flora-svc 101 | POSITION_INDEPENDENT_CODE ON 102 | ) 103 | target_include_directories(flora-svc-static PRIVATE 104 | include 105 | ${mutils_INCLUDE_DIRS} 106 | ) 107 | target_compile_options(flora-svc-static PRIVATE -DROKID_LOG_ENABLED=${SVC_LOGLEVEL}) 108 | add_library(flora-svc SHARED 109 | ${flora_svc_SOURCES} 110 | ) 111 | target_include_directories(flora-svc PRIVATE 112 | include 113 | ${mutils_INCLUDE_DIRS} 114 | ) 115 | target_link_libraries(flora-svc 116 | ${mutils_LIBRARIES} 117 | ) 118 | if (DEBUG_FOR_YODAV8) 119 | set(YODA_V8_DEBUG_FLAGS -DDEBUG_FOR_YODAV8) 120 | endif() 121 | target_compile_options(flora-svc PRIVATE 122 | -DROKID_LOG_ENABLED=${SVC_LOGLEVEL} 123 | ${YODA_V8_DEBUG_FLAGS} 124 | ) 125 | 126 | set(flora_cli_SOURCES 127 | include/flora-cli.h 128 | include/flora-agent.h 129 | src/cli.h 130 | src/cli.cc 131 | src/conn.h 132 | src/sock-conn.h 133 | src/sock-conn.cc 134 | src/defs.h 135 | src/ser-helper.h 136 | src/ser-helper.cc 137 | src/flora-agent.cc 138 | ) 139 | add_library(flora-cli-static STATIC 140 | ${flora_cli_SOURCES} 141 | ) 142 | set_target_properties(flora-cli-static PROPERTIES 143 | OUTPUT_NAME flora-cli 144 | POSITION_INDEPENDENT_CODE ON 145 | ) 146 | target_include_directories(flora-cli-static PRIVATE 147 | include 148 | ${mutils_INCLUDE_DIRS} 149 | ) 150 | target_compile_options(flora-cli-static PRIVATE -DROKID_LOG_ENABLED=${CLI_LOGLEVEL}) 151 | add_library(flora-cli SHARED 152 | ${flora_cli_SOURCES} 153 | ) 154 | target_include_directories(flora-cli PRIVATE 155 | include 156 | ${mutils_INCLUDE_DIRS} 157 | ) 158 | target_link_libraries(flora-cli 159 | ${mutils_LIBRARIES} 160 | ) 161 | target_compile_options(flora-cli PRIVATE -DROKID_LOG_ENABLED=${CLI_LOGLEVEL}) 162 | 163 | if (ncurses_LIBRARIES) 164 | file(GLOB monitor_SOURCES 165 | monitor/*.cc 166 | monitor/*.h 167 | ) 168 | add_executable(flora-monitor ${monitor_SOURCES}) 169 | target_include_directories(flora-monitor PRIVATE 170 | include 171 | ${mutils_INCLUDE_DIRS} 172 | ${ncurses_INCLUDE_DIRS} 173 | ) 174 | target_link_libraries(flora-monitor 175 | flora-cli 176 | ${mutils_LIBRARIES} 177 | ${ncurses_LIBRARIES} 178 | ) 179 | install(TARGETS flora-monitor 180 | RUNTIME DESTINATION bin 181 | ) 182 | endif(ncurses_LIBRARIES) 183 | 184 | # install 185 | install(TARGETS flora-svc flora-cli flora-svc-static flora-cli-static 186 | LIBRARY DESTINATION lib 187 | ARCHIVE DESTINATION lib 188 | ) 189 | file(GLOB flora_headers include/*.h) 190 | install(FILES ${flora_headers} DESTINATION include) 191 | 192 | # unit-tests 193 | if (BUILD_TEST) 194 | findPackage(gtest REQUIRED 195 | HINTS ${gtestPrefix} 196 | HEADERS gtest/gtest.h 197 | STATIC_LIBS gtest 198 | ) 199 | add_executable(flora-test test/main.cc test/simple.cc test/svc.h) 200 | target_include_directories(flora-test PRIVATE 201 | include 202 | ${gtest_INCLUDE_DIRS} 203 | ${mutils_INCLUDE_DIRS} 204 | ) 205 | target_link_libraries(flora-test 206 | flora-cli 207 | flora-svc 208 | ${gtest_LIBRARIES} 209 | ) 210 | endif(BUILD_TEST) 211 | -------------------------------------------------------------------------------- /unit-tests/main.cc: -------------------------------------------------------------------------------- 1 | #include "clargs.h" 2 | #include "rlog.h" 3 | #include "test-cli.h" 4 | #include "test-svc.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define TAG "unit-test.main" 12 | 13 | #define TCP_SERVICE_URI "tcp://0.0.0.0:37777/" 14 | #define TCP_CONN_URI "tcp://localhost:37777/" 15 | #define UNIX_URI "unix:flora-unittest" 16 | 17 | #define COMM_TYPE_UNIX 0 18 | #define COMM_TYPE_TCP 1 19 | 20 | static void print_prompt(const char *progname) { 21 | static const char *format = 22 | "USAGE: %s [options]\n" 23 | "options:\n" 24 | "--client-num=$TEST_CLIENT_NUM (default value 2)\n" 25 | "--repeat=$TEST_REPEAT_TIMES (default value 1)\n" 26 | "--comm-type=$TYPE (unix|tcp) (default value unix)\n" 27 | "--use-c-api\n"; 28 | KLOGE(TAG, format, progname); 29 | } 30 | 31 | typedef struct { 32 | int32_t client_num; 33 | int32_t repeat; 34 | int32_t comm_type; 35 | int32_t use_c_api; 36 | } CmdlineArgs; 37 | 38 | static bool parse_cmdline(int argc, char **argv, CmdlineArgs *args) { 39 | shared_ptr h = CLArgs::parse(argc, argv); 40 | if (h == nullptr) { 41 | print_prompt(argv[0]); 42 | return false; 43 | } 44 | if (h->find("help", nullptr, nullptr)) { 45 | print_prompt(argv[0]); 46 | return false; 47 | } 48 | uint32_t begin, end; 49 | CLPair pair; 50 | if (h->find("client-num", &begin, &end)) { 51 | h->at(begin, pair); 52 | if (pair.value == nullptr) 53 | args->client_num = 2; 54 | else if (!pair.to_integer(args->client_num)) { 55 | KLOGE(TAG, "invalid --client-num=%s", pair.value); 56 | return false; 57 | } 58 | } 59 | if (h->find("repeat", &begin, &end)) { 60 | h->at(begin, pair); 61 | if (pair.value == nullptr) 62 | args->repeat = 1; 63 | else if (!pair.to_integer(args->repeat)) { 64 | KLOGE(TAG, "invalid --repeat=%s", pair.value); 65 | return false; 66 | } 67 | } 68 | if (h->find("comm-type", &begin, &end)) { 69 | h->at(begin, pair); 70 | if (pair.value == nullptr) 71 | args->comm_type = COMM_TYPE_UNIX; 72 | else { 73 | if (strcmp(pair.value, "tcp") == 0) 74 | args->comm_type = COMM_TYPE_TCP; 75 | else 76 | args->comm_type = COMM_TYPE_UNIX; 77 | } 78 | } 79 | if (h->find("use-c-api", nullptr, nullptr)) 80 | args->use_c_api = 1; 81 | else 82 | args->use_c_api = 0; 83 | return true; 84 | } 85 | 86 | static bool check_results(TestClient *clients, int32_t num) { 87 | int32_t total_post_counter[FLORA_MSG_COUNT]; 88 | int32_t total_call_counter[FLORA_MSG_COUNT]; 89 | int32_t total_recv_call_counter[FLORA_MSG_COUNT]; 90 | int32_t i, j; 91 | 92 | memset(total_post_counter, 0, sizeof(total_post_counter)); 93 | for (i = 0; i < num; ++i) { 94 | for (j = 0; j < FLORA_MSG_COUNT; ++j) { 95 | total_post_counter[j] += clients[i].post_counter[j]; 96 | } 97 | } 98 | memset(total_call_counter, 0, sizeof(total_call_counter)); 99 | memset(total_recv_call_counter, 0, sizeof(total_recv_call_counter)); 100 | 101 | for (i = 0; i < num; ++i) { 102 | for (j = 0; j < FLORA_MSG_COUNT; ++j) { 103 | if (clients[i].subscribe_flags[j]) { 104 | if (clients[i].recv_post_counter[j] != total_post_counter[j]) { 105 | KLOGE(TAG, "client %d, msg %d recv/post not equal %d/%d", i, j, 106 | clients[i].recv_post_counter[j], total_post_counter[j]); 107 | return false; 108 | } 109 | } 110 | if (clients[i].subscribe_flags[j] == 0) { 111 | if (clients[i].recv_post_counter[j] != 0) { 112 | KLOGE(TAG, 113 | "client %d, msg %d should not received, but recv %d " 114 | "times", 115 | i, j, clients[i].recv_post_counter[j]); 116 | return false; 117 | } 118 | } 119 | total_call_counter[j] = clients[i].call_counter[j]; 120 | total_recv_call_counter[j] = clients[i].recv_call_counter[j]; 121 | } 122 | } 123 | for (i = 0; i < FLORA_MSG_COUNT; ++i) { 124 | if (total_call_counter[i] != total_recv_call_counter[i]) { 125 | KLOGE(TAG, "msg %d recv/call not equal %d/%d", i, 126 | total_recv_call_counter[i], total_call_counter[i]); 127 | return false; 128 | } 129 | } 130 | KLOGI(TAG, "test success"); 131 | return true; 132 | } 133 | 134 | int main(int argc, char **argv) { 135 | CmdlineArgs args; 136 | if (!parse_cmdline(argc, argv, &args)) 137 | return 0; 138 | 139 | srand(time(nullptr)); 140 | TestClient::static_init(args.use_c_api); 141 | 142 | TestService tsvc; 143 | const char *uri; 144 | if (args.comm_type == COMM_TYPE_TCP) 145 | uri = TCP_SERVICE_URI; 146 | else 147 | uri = UNIX_URI; 148 | if (!tsvc.run(uri, args.use_c_api)) { 149 | KLOGE(TAG, "service startup failed"); 150 | return 1; 151 | } 152 | 153 | TestClient *clients = new TestClient[args.client_num]; 154 | int32_t i; 155 | 156 | char cli_uri[64]; 157 | if (args.comm_type == COMM_TYPE_TCP) 158 | uri = TCP_CONN_URI; 159 | else 160 | uri = UNIX_URI; 161 | for (i = 0; i < args.client_num; ++i) { 162 | snprintf(cli_uri, sizeof(cli_uri), "%s#%03d", uri, i); 163 | if (!clients[i].init(cli_uri, args.use_c_api)) { 164 | KLOGE(TAG, "client %d init failed", i); 165 | tsvc.close(); 166 | return 1; 167 | } 168 | } 169 | 170 | while (args.repeat--) { 171 | for (i = 0; i < args.client_num; ++i) { 172 | clients[i].do_subscribe(); 173 | } 174 | sleep(2); 175 | for (i = 0; i < args.client_num; ++i) { 176 | clients[i].do_post(); 177 | } 178 | sleep(4); 179 | if (!check_results(clients, args.client_num)) { 180 | break; 181 | } 182 | for (i = 0; i < args.client_num; ++i) { 183 | clients[i].reset(); 184 | } 185 | } 186 | 187 | for (i = 0; i < args.client_num; ++i) { 188 | clients[i].close(); 189 | } 190 | delete[] clients; 191 | tsvc.close(); 192 | return 0; 193 | } 194 | -------------------------------------------------------------------------------- /include/flora-agent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "caps.h" 4 | #include "flora-cli.h" 5 | #include 6 | 7 | /** 8 | * flora client代理工具 9 | * 10 | * 包装flora client api,更易于使用 11 | * 自动重连 12 | * 更易用的消息回调 13 | */ 14 | 15 | // config(KEY, string uri) 16 | #define FLORA_AGENT_CONFIG_URI 0 17 | // config(KEY, uint32_t bufsize) 18 | #define FLORA_AGENT_CONFIG_BUFSIZE 1 19 | // config(KEY, uint32_t interval) 20 | #define FLORA_AGENT_CONFIG_RECONN_INTERVAL 2 21 | // config(KEY, uint32_t flags, MonitorCallback *cb) 22 | // flags: FLORA_CLI_FLAG_MONITOR_* (see flora-cli.h) 23 | #define FLORA_AGENT_CONFIG_MONITOR 3 24 | // config(KEY, uint32_t interval, uint32_t timeout) 25 | // interval: interval of send beep packet 26 | // timeout: timeout of flora service no response 27 | #define FLORA_AGENT_CONFIG_KEEPALIVE 4 28 | 29 | #ifdef __cplusplus 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | namespace flora { 40 | 41 | typedef std::function &, uint32_t)> 42 | PostHandler; 43 | typedef std::function &, 44 | std::shared_ptr &)> 45 | CallHandler; 46 | typedef std::map PostHandlerMap; 47 | typedef std::map CallHandlerMap; 48 | 49 | class Agent : public ClientCallback { 50 | public: 51 | ~Agent(); 52 | 53 | void config(uint32_t key, ...); 54 | 55 | void config(uint32_t key, va_list ap); 56 | 57 | void subscribe(const char *name, PostHandler &&cb); 58 | 59 | void subscribe(const char *name, PostHandler &cb); 60 | 61 | void unsubscribe(const char *name); 62 | 63 | void declare_method(const char *name, CallHandler &&cb); 64 | 65 | void declare_method(const char *name, CallHandler &cb); 66 | 67 | void remove_method(const char *name); 68 | 69 | void start(bool block = false); 70 | 71 | void close(); 72 | 73 | int32_t post(const char *name, std::shared_ptr &msg, 74 | uint32_t msgtype = FLORA_MSGTYPE_INSTANT); 75 | 76 | int32_t call(const char *name, std::shared_ptr &msg, const char *target, 77 | Response &response, uint32_t timeout = 0); 78 | 79 | int32_t call(const char *name, std::shared_ptr &msg, const char *target, 80 | std::function &&cb, 81 | uint32_t timeout = 0); 82 | 83 | int32_t call(const char *name, std::shared_ptr &msg, const char *target, 84 | std::function &cb, 85 | uint32_t timeout = 0); 86 | 87 | // override ClientCallback 88 | void recv_post(const char *name, uint32_t msgtype, 89 | std::shared_ptr &msg); 90 | 91 | void recv_call(const char *name, std::shared_ptr &msg, 92 | std::shared_ptr &reply); 93 | 94 | void disconnected(); 95 | 96 | int get_socket() const; 97 | 98 | private: 99 | void run(); 100 | 101 | void init_cli(std::shared_ptr &cli); 102 | 103 | void destroy_client(); 104 | 105 | void clean_gabages(std::list >& gabages); 106 | 107 | private: 108 | class Options { 109 | public: 110 | std::string uri; 111 | uint32_t bufsize = 0; 112 | std::chrono::milliseconds reconn_interval = 113 | std::chrono::milliseconds(10000); 114 | uint32_t flags = 0; 115 | MonitorCallback *mon_callback = nullptr; 116 | uint32_t beep_interval = FLORA_CLI_DEFAULT_BEEP_INTERVAL; 117 | uint32_t noresp_timeout = FLORA_CLI_DEFAULT_NORESP_TIMEOUT; 118 | }; 119 | 120 | Options options; 121 | PostHandlerMap post_handlers; 122 | CallHandlerMap call_handlers; 123 | std::mutex conn_mutex; 124 | std::condition_variable conn_cond; 125 | // 'start' and try connect flora service once 126 | std::condition_variable start_cond; 127 | std::shared_ptr flora_cli; 128 | std::thread run_thread; 129 | std::mutex cg_mutex; 130 | bool working = false; 131 | }; 132 | 133 | } // namespace flora 134 | 135 | extern "C" { 136 | #endif // __cplusplus 137 | 138 | typedef intptr_t flora_agent_t; 139 | typedef void (*flora_agent_subscribe_callback_t)(const char *name, caps_t msg, 140 | uint32_t type, void *arg); 141 | typedef void (*flora_agent_declare_method_callback_t)(const char *name, 142 | caps_t msg, 143 | flora_call_reply_t reply, 144 | void *arg); 145 | 146 | flora_agent_t flora_agent_create(); 147 | 148 | void flora_agent_config(flora_agent_t agent, uint32_t key, ...); 149 | 150 | void flora_agent_subscribe(flora_agent_t agent, const char *name, 151 | flora_agent_subscribe_callback_t cb, void *arg); 152 | 153 | void flora_agent_unsubscribe(flora_agent_t agent, const char *name); 154 | 155 | void flora_agent_declare_method(flora_agent_t agent, const char *name, 156 | flora_agent_declare_method_callback_t cb, 157 | void *arg); 158 | 159 | void flora_agent_remove_method(flora_agent_t agent, const char *name); 160 | 161 | void flora_agent_start(flora_agent_t agent, int32_t block); 162 | 163 | void flora_agent_close(flora_agent_t agent); 164 | 165 | int32_t flora_agent_post(flora_agent_t agent, const char *name, caps_t msg, 166 | uint32_t msgtype); 167 | 168 | int32_t flora_agent_call(flora_agent_t agent, const char *name, caps_t msg, 169 | const char *target, flora_call_result *result, 170 | uint32_t timeout); 171 | 172 | int32_t flora_agent_call_nb(flora_agent_t agent, const char *name, caps_t msg, 173 | const char *target, flora_call_callback_t cb, 174 | void *arg, uint32_t timeout); 175 | 176 | void flora_agent_delete(flora_agent_t agent); 177 | 178 | #ifdef __cplusplus 179 | } // extern "C" 180 | #endif // __cplusplus 181 | -------------------------------------------------------------------------------- /docs/client.md: -------------------------------------------------------------------------------- 1 | # Client 2 | 3 | flora客户端低级接口,与Agent功能相同,建议使用Agent. 4 | 5 | ``` 6 | class FloraCallback : public ClientCallback { 7 | public: 8 | void recv_post(const char *name, uint32_t msgtype, shared_ptr &msg) { 9 | // name == "foo" 10 | // msgtype == FLORA_MSGTYPE_INSTANT 11 | int32_t iv; 12 | string str; 13 | msg->read(iv); // read integer 1 14 | msg->read_string(str); // read string "hello" 15 | } 16 | 17 | void recv_call(const char *name, shared_ptr &msg, Reply &reply) { 18 | // name == "foo" 19 | // fill 'reply' content here 20 | // caller will receive the reply content 21 | reply.ret_code = 0; 22 | reply.data = Caps::new_instance(); 23 | reply.data->write("world"); 24 | } 25 | 26 | void disconnected() { 27 | } 28 | }; 29 | 30 | shared_ptr floraClient; 31 | FloraCallback floraCallback; 32 | flora::Client::connect("unix:/var/run/flora.sock#exam-client", floraCallback, 80 * 1024, floraClient); 33 | 34 | floraClient->subscribe("foo"); 35 | floraClient->declare_method("foo"); 36 | 37 | 38 | // 发送广播消息 39 | shared_ptr msg = Caps::new_instance(); 40 | msg.write(1); 41 | msg.write("hello"); 42 | floraClient->post("foo", msg, FLORA_MSGTYPE_INSTANT); 43 | // 调用远程方法并等待结果返回 44 | Response resp; 45 | int32_t r = floraClient->call("foo", msg, "exam-client", resp, 0); 46 | if (r == FLORA_CLI_SUCCESS) { 47 | string str; 48 | // resp.ret_code == 0 49 | resp.data->read(str); // str == "world" 50 | } 51 | // 异步调用远程方法,在callback函数中得到返回结果 52 | floraClient->call("foo", msg, "exam-client", [](int32_t rescode, Response &resp) { 53 | if (rescode == FLORA_CLI_SUCCESS) { 54 | // resp.ret_code == 0 55 | string str; 56 | resp.data->read(str); // str == "world" 57 | } 58 | }, 0); 59 | ``` 60 | 61 | ## Warning!!! 62 | 63 | **Don't destruct flora::Client instance in callback function, may cause program crash** 64 | 65 | ## Methods 66 | 67 | ### (static) connect(uri, cb, bufsize, result) 68 | 69 | 连接flora服务,创建flora::Client对象 70 | 71 | #### Parameters 72 | 73 | name | type | default | description 74 | --- | --- | --- | --- 75 | uri | string | | flora服务uri 76 | cb | [flora::ClientCallback](#ClientCallback) | | 77 | bufsize | uint32_t | | 消息缓冲大小 78 | result | shared_ptr\ & | | 创建的flora::Client对象 79 | 80 | --- 81 | 82 | ### subscribe(name) 83 | 84 | 订阅消息 85 | 86 | #### Parameters 87 | 88 | name | type | default | description 89 | --- | --- | --- | --- 90 | name | const char* | | 消息名称 91 | 92 | --- 93 | 94 | ### unsubscribe(name) 95 | 96 | 取消订阅 97 | 98 | #### Parameters 99 | 100 | name | type | default | description 101 | --- | --- | --- | --- 102 | name | const char* | | 消息名称 103 | 104 | --- 105 | 106 | ### declare_method(name) 107 | 108 | 声明远程调用方法 109 | 110 | #### Parameters 111 | 112 | name | type | default | description 113 | --- | --- | --- | --- 114 | name | const char* | | 方法名称 115 | 116 | --- 117 | 118 | ### remove_method(name) 119 | 120 | 删除远程调用方法 121 | 122 | #### Parameters 123 | 124 | name | type | default | description 125 | --- | --- | --- | --- 126 | name | const char* | | 方法名称 127 | 128 | --- 129 | 130 | ### post(name, msg, type) 131 | 132 | 发送消息 133 | 134 | #### Parameters 135 | 136 | name | type | default | description 137 | --- | --- | --- | --- 138 | name | const char* | | 消息名称 139 | msg | shared_ptr\<[Caps](https://github.com/Rokid/aife-mutils/blob/master/caps.md)>& | | 消息内容 140 | type | uint32_t | FLORA_MSGTYPE_INSTANT | 消息类型
FLORA_MSGTYPE_INSTANT
FLORA_MSGTYPE_PERSIST 141 | 142 | #### returns 143 | 144 | Type: int32_t 145 | 146 | value | description 147 | --- | --- 148 | FLORA_CLI_SUCCESS | 成功 149 | FLORA_CLI_EINVAL | 参数非法 150 | FLORA_CLI_ECONN | flora service连接错误 151 | 152 | --- 153 | 154 | ### call(name, msg, target, response, timeout) 155 | 156 | 向另一客户端发起远程方法调用,等待返回结果。 157 | 158 | #### Parameters 159 | 160 | name | type | default | description 161 | --- | --- | --- | --- 162 | name | const char* | | 远程方法名称 163 | msg | shared_ptr\<[Caps](https://github.com/Rokid/aife-mutils/blob/master/caps.md)>& | | 方法参数 164 | target | const char* | | 声明远程方法的客户端id 165 | response | [Response](#Response) | | 远程方法的返回 166 | timeout | uint32_t | 0 | 等待回复的超时时间,0表示使用默认超时时间。 167 | 168 | #### returns 169 | 170 | Type: int32_t 171 | 172 | value | description 173 | --- | --- 174 | FLORA_CLI_SUCCESS | 成功 175 | FLORA_CLI_EINVAL | 参数非法 176 | FLORA_CLI_ECONN | flora service连接错误 177 | FLORA_CLI_ENEXISTS | 找不到此远程调用方法 178 | FLORA_CLI_ETIMEOUT | 超时无回复 179 | FLORA_CLI_EDEADLOCK | 在回调函数中调用此方法,将造成无限阻塞 180 | 181 | --- 182 | 183 | ### call(name, msg, target, cb, timeout) 184 | 185 | 发送消息并等待另一客户端回复消息 186 | 187 | #### Parameters 188 | 189 | name | type | default | description 190 | --- | --- | --- | --- 191 | name | const char* | | 远程方法名称 192 | msg | shared_ptr\<[Caps](https://github.com/Rokid/aife-mutils/blob/master/caps.md)>& | | 方法参数 193 | target | const char* | | 声明远程方法的客户端id 194 | cb | [CallCallback](#CallCallback) | | 回调函数 195 | timeout | uint32_t | 0 | 等待回复的超时时间,0表示使用默认超时时间。 196 | 197 | #### returns 198 | 199 | Type: int32_t 200 | 201 | value | description 202 | --- | --- 203 | FLORA_CLI_SUCCESS | 成功 204 | FLORA_CLI_EINVAL | 参数非法 205 | FLORA_CLI_ECONN | flora service连接错误 206 | 207 | --- 208 | ## Definitions 209 | 210 | ### CallCallback(int32_t, response) 211 | 212 | 回调函数:收到回复的消息 213 | 214 | #### Parameters 215 | 216 | name | type | description 217 | --- | --- | --- 218 | rescode | int32_t | 远程方法调用错误码 219 | response | [Response](#Response)& | 远程调用返回结果 220 | 221 | ### Reply 222 | 223 | #### Members 224 | 225 | name | type | description 226 | --- | --- | --- 227 | ret_code | int32_t | 返回码,0为成功。 228 | data | shared_ptr\<[Caps](https://github.com/Rokid/aife-mutils/blob/master/caps.md)>& | 消息内容 229 | 230 | ### Response 231 | 232 | #### Members 233 | 234 | name | type | description 235 | --- | --- | --- 236 | ret_code | int32_t | 返回码,远程方法返回,0为成功。 237 | data | shared_ptr\<[Caps](https://github.com/Rokid/aife-mutils/blob/master/caps.md)>& | 消息内容 238 | sender | string | 远程方法声明方的身份标识 239 | 240 | --- 241 | 242 | # ClientCallback 243 | 244 | ## Methods 245 | 246 | ### recv_post(name, type, msg) 247 | 248 | 回调函数:收到订阅的消息 249 | 250 | #### Parameters 251 | 252 | name | type | description 253 | --- | --- | --- 254 | name | string | 消息名称 255 | type | uint32_t | 消息类型
FLORA_MSGTYPE_INSTANT
FLORA_MSGTYPE_PERSIST 256 | msg | shared_ptr\<[Caps](https://github.com/Rokid/aife-mutils/blob/master/caps.md)>& | 消息内容 257 | 258 | ### recv_call(name, msg, reply) 259 | 260 | 回调函数:收到远程方法调用 261 | 262 | #### Parameters 263 | 264 | name | type | description 265 | --- | --- | --- 266 | name | string | 消息名称 267 | msg | shared_ptr\<[Caps](https://github.com/Rokid/aife-mutils/blob/master/caps.md)>& | 消息内容 268 | reply | [Reply](#Reply)& | 填充reply结构体,给远程方法调用者返回数据 269 | 270 | ### disconnected 271 | 272 | 回调函数:连接断开 273 | -------------------------------------------------------------------------------- /docs/agent.md: -------------------------------------------------------------------------------- 1 | # Agent 2 | 3 | flora client代理类,自动保持与flora service连接及订阅状态。 4 | 5 | ``` 6 | Agent agent; 7 | // #exam-agent为可选项,标识此agent身份 8 | agent.config(FLORA_AGENT_CONFIG_URI, "unix:/var/run/flora.sock#exam-agent"); 9 | agent.config(FLORA_AGENT_CONFIG_BUFSIZE, 80 * 1024); 10 | agent.config(FLORA_AGENT_CONFIG_RECONN_INTERVAL, 5000); 11 | // 订阅消息 12 | agent.subscribe("foo", [](const char* name, shared_ptr& msg, uint32_t type) { 13 | int32_t iv; 14 | string str; 15 | msg->read(iv); // read integer 1 16 | msg->read_string(str); // read string "hello" 17 | }); 18 | // 声明远程方法 19 | agent.declare_method("foo", [](const char* name, shared_ptr& msg, shared_ptr& reply) { 20 | // use 'reply->end()' to send return values to caller 21 | reply->write_code(FLORA_CLI_SUCCESS); 22 | shared_ptr data = Caps::new_instance(); 23 | data->write("world"); 24 | reply->write_data(data); 25 | reply->end(); 26 | }); 27 | 28 | agent.start(); 29 | 30 | // 发送广播消息 31 | shared_ptr msg = Caps::new_instance(); 32 | msg.write(1); 33 | msg.write("hello"); 34 | agent.post("foo", msg, FLORA_MSGTYPE_INSTANT); 35 | 36 | // 远程方法调用 37 | Response resp; 38 | // blocking call(msgname, msg, target, response, timeout) 39 | // NOTE: timeout set to 0 will use default timeout 40 | int32_t r = agent.call("foo", msg, "exam-agent", resp, 0); 41 | if (r == FLORA_CLI_SUCCESS) { 42 | string str; 43 | resp.data->read(str); 44 | printf("recv call foo reply: %d, %s\n", res.ret_code, str.c_str()); 45 | } 46 | // non-blocking call(msgname, msg, target, callback, timeout) 47 | agent.call("foo", msg, "exam-agent", [](int32_t rescode, Response& resp) { 48 | if (rescode == FLORA_CLI_SUCCESS) { 49 | // resp.ret_code; // ret_code will be 0 50 | // resp.sender; // sender will be "exam-agent" 51 | string str; 52 | resp.data->read(str); // str will be "world" 53 | } 54 | }, 0); 55 | ``` 56 | 57 | ## Methods 58 | 59 | ### config(key, ...) 60 | 61 | 为Agent对象配置参数 62 | 63 | #### Parameters 64 | 65 | name | type | default | description 66 | --- | --- | --- | --- 67 | key | uint32_t | | FLORA_AGENT_CONFIG_URI
FLORA_AGENT_CONFIG_BUFSIZE
FLORA_AGENT_CONFIG_RECONN_INTERVAL 68 | 69 | --- 70 | 71 | ### subscribe(name, cb) 72 | 73 | 订阅消息并指定收到消息的回调函数 74 | 75 | #### Parameters 76 | 77 | name | type | default | description 78 | --- | --- | --- | --- 79 | name | const char* | | 消息名称 80 | cb | [SubscribeCallback](#SubscribeCallback) | | 回调函数 81 | 82 | --- 83 | 84 | ### unsubscribe(name) 85 | 86 | 取消订阅 87 | 88 | #### Parameters 89 | 90 | name | type | default | description 91 | --- | --- | --- | --- 92 | name | const char* | | 消息名称 93 | 94 | --- 95 | 96 | ### declare_method(name, cb) 97 | 98 | 声明远程调用方法,可接受远程方法调用并返回数据 99 | 100 | #### Parameters 101 | 102 | name | type | default | description 103 | --- | --- | --- | --- 104 | name | const char* | | 方法名称 105 | cb | [DeclareMethodCallback](#DeclareMethodCallback) | | 回调函数 106 | 107 | --- 108 | 109 | ### remove_method(name) 110 | 111 | 删除远程调用方法 112 | 113 | #### Parameters 114 | 115 | name | type | default | description 116 | --- | --- | --- | --- 117 | name | const char* | | 方法名称 118 | 119 | --- 120 | 121 | ### start(block) 122 | 123 | 启动Agent。需要在config指定uri后调用。可以在subscribe之后或之前调用。 124 | 125 | #### Parameters 126 | 127 | name | type | default | description 128 | --- | --- | --- | --- 129 | block | bool | false | 阻塞模式开关。如果开启阻塞模式,start不会返回,直至调用close方法。 130 | 131 | --- 132 | 133 | ### close() 134 | 135 | 关闭Agent。 136 | 137 | --- 138 | 139 | ### post(name, msg, type) 140 | 141 | 发送消息 142 | 143 | #### Parameters 144 | 145 | name | type | default | description 146 | --- | --- | --- | --- 147 | name | const char* | | 消息名称 148 | msg | shared_ptr\<[Caps](https://github.com/Rokid/aife-mutils/blob/master/caps.md)>& | | 消息内容 149 | type | uint32_t | FLORA_MSGTYPE_INSTANT | 消息类型
FLORA_MSGTYPE_INSTANT
FLORA_MSGTYPE_PERSIST 150 | 151 | #### returns 152 | 153 | Type: int32_t 154 | 155 | value | description 156 | --- | --- 157 | FLORA_CLI_SUCCESS | 成功 158 | FLORA_CLI_EINVAL | 参数非法 159 | FLORA_CLI_ECONN | flora service连接错误 160 | 161 | --- 162 | 163 | ### call(name, msg, target, response, timeout) 164 | 165 | 向另一客户端发起远程方法调用,等待返回结果。 166 | 167 | #### Parameters 168 | 169 | name | type | default | description 170 | --- | --- | --- | --- 171 | name | const char* | | 远程方法名称 172 | msg | shared_ptr\<[Caps](https://github.com/Rokid/aife-mutils/blob/master/caps.md)>& | | 方法参数 173 | target | const char* | | 声明远程方法的客户端id 174 | response | [Response](#Response) | | 远程方法的返回 175 | timeout | uint32_t | 0 | 等待回复的超时时间,0表示使用默认超时时间。 176 | 177 | #### returns 178 | 179 | Type: int32_t 180 | 181 | value | description 182 | --- | --- 183 | FLORA_CLI_SUCCESS | 成功 184 | FLORA_CLI_EINVAL | 参数非法 185 | FLORA_CLI_ECONN | flora service连接错误 186 | FLORA_CLI_ENEXISTS | 找不到此远程调用方法 187 | FLORA_CLI_ETIMEOUT | 超时无回复 188 | FLORA_CLI_EDEADLOCK | 在回调函数中调用此方法,将造成无限阻塞 189 | 190 | --- 191 | 192 | ### call(name, msg, target, cb, timeout) 193 | 194 | 发送消息并等待另一客户端回复消息 195 | 196 | #### Parameters 197 | 198 | name | type | default | description 199 | --- | --- | --- | --- 200 | name | const char* | | 远程方法名称 201 | msg | shared_ptr\<[Caps](https://github.com/Rokid/aife-mutils/blob/master/caps.md)>& | | 方法参数 202 | target | const char* | | 声明远程方法的客户端id 203 | cb | [CallCallback](#CallCallback) | | 回调函数 204 | timeout | uint32_t | 0 | 等待回复的超时时间,0表示使用默认超时时间。 205 | 206 | #### returns 207 | 208 | Type: int32_t 209 | 210 | value | description 211 | --- | --- 212 | FLORA_CLI_SUCCESS | 成功 213 | FLORA_CLI_EINVAL | 参数非法 214 | FLORA_CLI_ECONN | flora service连接错误 215 | 216 | --- 217 | 218 | ## Definition 219 | 220 | ### SubscribeCallback(name, msg, type) 221 | 222 | 回调函数:收到订阅的消息 223 | 224 | #### Parameters 225 | 226 | name | type | description 227 | --- | --- | --- 228 | name | string | 消息名称 229 | msg | shared_ptr\<[Caps](https://github.com/Rokid/aife-mutils/blob/master/caps.md)>& | 消息内容 230 | type | uint32_t | 消息类型
FLORA_MSGTYPE_INSTANT
FLORA_MSGTYPE_PERSIST 231 | 232 | ### DeclareMethodCallback(name, msg, reply) 233 | 234 | 回调函数:收到远程方法调用 235 | 236 | #### Parameters 237 | 238 | name | type | description 239 | --- | --- | --- 240 | name | string | 消息名称 241 | msg | shared_ptr\<[Caps](https://github.com/Rokid/aife-mutils/blob/master/caps.md)>& | 消息内容 242 | reply | shared_ptr\<[Reply](reply.md)\>& | reply对象,给远程方法调用者返回数据 243 | 244 | ### CallCallback(int32_t, response) 245 | 246 | 回调函数:收到回复的消息 247 | 248 | #### Parameters 249 | 250 | name | type | description 251 | --- | --- | --- 252 | rescode | int32_t | 远程方法调用错误码 253 | response | [Response](#Response)& | 远程调用返回结果 254 | 255 | ### Response 256 | 257 | #### Members 258 | 259 | name | type | description 260 | --- | --- | --- 261 | ret_code | int32_t | 返回码,远程方法返回,0为成功。 262 | data | shared_ptr\<[Caps](https://github.com/Rokid/aife-mutils/blob/master/caps.md)>& | 消息内容 263 | sender | string | 远程方法声明方的身份标识 264 | -------------------------------------------------------------------------------- /android/java/com/rokid/flora/Caps.java: -------------------------------------------------------------------------------- 1 | package com.rokid.flora; 2 | 3 | public class Caps { 4 | public Caps() { 5 | } 6 | 7 | Caps(long h) { 8 | native_handle = h; 9 | } 10 | 11 | public void finalize() { 12 | destroy(); 13 | } 14 | 15 | public void destroy() { 16 | native_destroy(native_handle); 17 | native_handle = 0; 18 | } 19 | 20 | public int parse(byte[] data, int offset, int length) { 21 | throw new UnsupportedOperationException("Caps.parse"); 22 | // return native_parse(data, offset, length); 23 | } 24 | 25 | public int parse(byte[] data) { 26 | if (data == null) 27 | return ERROR_INVALID_PARAM; 28 | return parse(data, 0, data.length); 29 | } 30 | 31 | public int serialize(byte[] data, int offset, int length) { 32 | throw new UnsupportedOperationException("Caps.serialize"); 33 | // return native_serialize(data, offset, length); 34 | } 35 | 36 | public int writeInteger(int v) { 37 | if (native_handle == 0) 38 | native_handle = native_create(); 39 | return native_write_integer(native_handle, v); 40 | } 41 | 42 | public int writeFloat(float v) { 43 | if (native_handle == 0) 44 | native_handle = native_create(); 45 | return native_write_float(native_handle, v); 46 | } 47 | 48 | public int writeLong(long v) { 49 | if (native_handle == 0) 50 | native_handle = native_create(); 51 | return native_write_long(native_handle, v); 52 | } 53 | 54 | public int writeDouble(double v) { 55 | if (native_handle == 0) 56 | native_handle = native_create(); 57 | return native_write_double(native_handle, v); 58 | } 59 | 60 | public int writeString(String v) { 61 | if (native_handle == 0) 62 | native_handle = native_create(); 63 | return native_write_string(native_handle, v); 64 | } 65 | 66 | public int writeBinary(byte[] v, int offset, int length) { 67 | if (native_handle == 0) 68 | native_handle = native_create(); 69 | if (v != null && (offset < 0 || length < 0 || offset + length > v.length)) 70 | return ERROR_INVALID_PARAM; 71 | return native_write_binary(native_handle, v, offset, length); 72 | } 73 | 74 | public int writeBinary(byte[] v) { 75 | if (v == null) 76 | return writeBinary(null, 0, 0); 77 | return writeBinary(v, 0, v.length); 78 | } 79 | 80 | public int writeCaps(Caps v) { 81 | if (native_handle == 0) 82 | native_handle = native_create(); 83 | return native_write_caps(native_handle, v == null ? 0 : v.native_handle); 84 | } 85 | 86 | public int readInteger() throws CapsException { 87 | if (native_handle == 0) 88 | throw new CapsException(ERROR_INVALID_PARAM); 89 | IntegerValue v = new IntegerValue(); 90 | int r = native_read(native_handle, v); 91 | if (r != SUCCESS) 92 | throw new CapsException(r); 93 | return v.value; 94 | } 95 | 96 | public float readFloat() throws CapsException { 97 | if (native_handle == 0) 98 | throw new CapsException(ERROR_INVALID_PARAM); 99 | FloatValue v = new FloatValue(); 100 | int r = native_read(native_handle, v); 101 | if (r != SUCCESS) 102 | throw new CapsException(r); 103 | return v.value; 104 | } 105 | 106 | public long readLong() throws CapsException { 107 | if (native_handle == 0) 108 | throw new CapsException(ERROR_INVALID_PARAM); 109 | LongValue v = new LongValue(); 110 | int r = native_read(native_handle, v); 111 | if (r != SUCCESS) 112 | throw new CapsException(r); 113 | return v.value; 114 | } 115 | 116 | public double readDouble() throws CapsException { 117 | if (native_handle == 0) 118 | throw new CapsException(ERROR_INVALID_PARAM); 119 | DoubleValue v = new DoubleValue(); 120 | int r = native_read(native_handle, v); 121 | if (r != SUCCESS) 122 | throw new CapsException(r); 123 | return v.value; 124 | } 125 | 126 | public String readString() throws CapsException { 127 | if (native_handle == 0) 128 | throw new CapsException(ERROR_INVALID_PARAM); 129 | StringValue v = new StringValue(); 130 | int r = native_read(native_handle, v); 131 | if (r != SUCCESS) 132 | throw new CapsException(r); 133 | return v.value; 134 | } 135 | 136 | public byte[] readBinary() throws CapsException { 137 | if (native_handle == 0) 138 | throw new CapsException(ERROR_INVALID_PARAM); 139 | BinaryValue v = new BinaryValue(); 140 | int r = native_read(native_handle, v); 141 | if (r != SUCCESS) 142 | throw new CapsException(r); 143 | return v.value; 144 | } 145 | 146 | public Caps readCaps() throws CapsException { 147 | if (native_handle == 0) 148 | throw new CapsException(ERROR_INVALID_PARAM); 149 | CapsValue v = new CapsValue(); 150 | int r = native_read(native_handle, v); 151 | if (r != SUCCESS) 152 | throw new CapsException(r); 153 | return new Caps(v.value); 154 | } 155 | 156 | private static class IntegerValue { 157 | public int value; 158 | } 159 | private static class FloatValue { 160 | public float value; 161 | } 162 | private static class LongValue { 163 | public long value; 164 | } 165 | private static class DoubleValue { 166 | public double value; 167 | } 168 | private static class StringValue { 169 | public String value; 170 | } 171 | private static class BinaryValue { 172 | public byte[] value; 173 | } 174 | private static class CapsValue { 175 | public long value; 176 | } 177 | 178 | private static native void native_init(Class[] classes); 179 | private static final Class nativeValueClasses[] = { 180 | IntegerValue.class, 181 | FloatValue.class, 182 | LongValue.class, 183 | DoubleValue.class, 184 | StringValue.class, 185 | BinaryValue.class, 186 | CapsValue.class 187 | }; 188 | static { 189 | System.loadLibrary("flora-cli.jni"); 190 | native_init(nativeValueClasses); 191 | } 192 | 193 | private native long native_create(); 194 | private native void native_destroy(long h); 195 | private native int native_write_integer(long h, int v); 196 | private native int native_write_float(long h, float v); 197 | private native int native_write_long(long h, long v); 198 | private native int native_write_double(long h, double v); 199 | private native int native_write_string(long h, String v); 200 | private native int native_write_binary(long h, byte[] v, int offset, int length); 201 | private native int native_write_caps(long h, long v); 202 | private native int native_read(long h, IntegerValue v); 203 | private native int native_read(long h, FloatValue v); 204 | private native int native_read(long h, LongValue v); 205 | private native int native_read(long h, DoubleValue v); 206 | private native int native_read(long h, StringValue v); 207 | private native int native_read(long h, BinaryValue v); 208 | private native int native_read(long h, CapsValue v); 209 | 210 | public static final int SUCCESS = 0; 211 | public static final int ERROR_INVALID_PARAM = -1; 212 | public static final int ERROR_WRONLY = -4; 213 | public static final int ERROR_RDONLY = -5; 214 | public static final int ERROR_INCORRECT_TYPE = -6; 215 | public static final int ERROR_END_OF_OBJECT = -7; 216 | 217 | long native_handle = 0; 218 | } 219 | -------------------------------------------------------------------------------- /unit-tests/test-cli.cc: -------------------------------------------------------------------------------- 1 | #include "test-cli.h" 2 | #include "rlog.h" 3 | #include 4 | #include 5 | #include 6 | 7 | #define MAX_POST_COUNT 256 8 | #define MAX_CALL_COUNT 256 9 | #define TAG "unit-test.TestClient" 10 | 11 | FloraMsg TestClient::flora_msgs[FLORA_MSG_COUNT]; 12 | flora_cli_callback_t TestClient::flora_callback; 13 | 14 | void TestClient::recv_post_s(const char *name, uint32_t msgtype, caps_t msg, 15 | void *arg) { 16 | TestClient *tc = reinterpret_cast(arg); 17 | tc->c_recv_post(name, msgtype, msg); 18 | } 19 | 20 | void TestClient::recv_call_s(const char *name, caps_t msg, void *arg, 21 | flora_call_reply_t reply) { 22 | TestClient *tc = reinterpret_cast(arg); 23 | tc->c_recv_call(name, msg, reply); 24 | } 25 | 26 | static void conn_disconnected(void *arg) {} 27 | 28 | void TestClient::static_init(bool capi) { 29 | int32_t i; 30 | for (i = 0; i < FLORA_MSG_COUNT; ++i) { 31 | snprintf(flora_msgs[i].name, sizeof(flora_msgs[i].name), "foo%02d", i); 32 | if (i % 2 == 0) { 33 | if (capi) { 34 | flora_msgs[i].c_args = caps_create(); 35 | caps_write_integer(flora_msgs[i].c_args, i); 36 | } else { 37 | flora_msgs[i].args = Caps::new_instance(); 38 | flora_msgs[i].args->write(i); 39 | } 40 | } else { 41 | flora_msgs[i].args = 0; 42 | } 43 | } 44 | 45 | if (capi) { 46 | flora_callback.recv_post = TestClient::recv_post_s; 47 | flora_callback.disconnected = conn_disconnected; 48 | flora_callback.recv_call = TestClient::recv_call_s; 49 | } 50 | } 51 | 52 | bool TestClient::init(const char *uri, bool capi) { 53 | use_c_api = capi; 54 | if (capi) 55 | return flora_cli_connect(uri, &flora_callback, this, 0, &c_flora_cli) == 56 | FLORA_CLI_SUCCESS; 57 | return Client::connect(uri, this, 0, flora_cli) == FLORA_CLI_SUCCESS; 58 | } 59 | 60 | void TestClient::do_subscribe() { 61 | int32_t i; 62 | int32_t r; 63 | 64 | memset(recv_post_counter, 0, sizeof(recv_post_counter)); 65 | memset(recv_call_counter, 0, sizeof(recv_call_counter)); 66 | memset(subscribe_flags, 0, sizeof(subscribe_flags)); 67 | memset(declare_method_flags, 0, sizeof(declare_method_flags)); 68 | for (i = 0; i < FLORA_MSG_COUNT; ++i) { 69 | bool dosub = (rand() % 5) <= 2; 70 | if (dosub) { 71 | if (use_c_api) { 72 | r = flora_cli_subscribe(c_flora_cli, flora_msgs[i].name); 73 | } else { 74 | r = flora_cli->subscribe(flora_msgs[i].name); 75 | } 76 | 77 | if (r != FLORA_CLI_SUCCESS) 78 | KLOGE(TAG, "client subscribe failed"); 79 | else 80 | subscribe_flags[i] = 1; 81 | } 82 | 83 | bool dodecl = (rand() % 5) <= 2; 84 | if (dodecl) { 85 | if (use_c_api) { 86 | r = flora_cli_declare_method(c_flora_cli, flora_msgs[i].name); 87 | } else { 88 | r = flora_cli->declare_method(flora_msgs[i].name); 89 | } 90 | 91 | if (r != FLORA_CLI_SUCCESS) 92 | KLOGE(TAG, "client declare method failed"); 93 | else 94 | declare_method_flags[i] = 1; 95 | } 96 | } 97 | } 98 | 99 | void TestClient::do_post() { 100 | int32_t c = rand() % MAX_POST_COUNT; 101 | int32_t i; 102 | int32_t r; 103 | 104 | memset(post_counter, 0, sizeof(post_counter)); 105 | for (i = 0; i < c; ++i) { 106 | int32_t idx = rand() % FLORA_MSG_COUNT; 107 | if (use_c_api) { 108 | r = flora_cli_post(c_flora_cli, flora_msgs[idx].name, 109 | flora_msgs[idx].c_args, FLORA_MSGTYPE_INSTANT); 110 | } else { 111 | r = flora_cli->post(flora_msgs[idx].name, flora_msgs[idx].args, 112 | FLORA_MSGTYPE_INSTANT); 113 | } 114 | if (r != FLORA_CLI_SUCCESS) 115 | KLOGE(TAG, "client post failed: %d", r); 116 | else 117 | ++post_counter[idx]; 118 | } 119 | } 120 | 121 | void TestClient::do_call(int32_t clinum) { 122 | int32_t i; 123 | int32_t r; 124 | int32_t c = rand() % MAX_CALL_COUNT; 125 | char clid[8]; 126 | 127 | memset(call_counter, 0, sizeof(call_counter)); 128 | for (i = 0; i < c; ++i) { 129 | int32_t idx = rand() % FLORA_MSG_COUNT; 130 | int32_t idn = rand() % clinum; 131 | snprintf(clid, sizeof(clid), "%03d", idn); 132 | if (use_c_api) { 133 | flora_call_result result; 134 | r = flora_cli_call(c_flora_cli, flora_msgs[idx].name, 135 | flora_msgs[idx].c_args, clid, &result, 0); 136 | } else { 137 | Response resp; 138 | r = flora_cli->call(flora_msgs[idx].name, flora_msgs[idx].args, clid, 139 | resp, 0); 140 | } 141 | if (r == FLORA_CLI_SUCCESS) 142 | ++call_counter[idx]; 143 | else if (r != FLORA_CLI_ENEXISTS) 144 | KLOGE(TAG, "client call failed: %d", r); 145 | } 146 | } 147 | 148 | void TestClient::reset() { 149 | int32_t i; 150 | int32_t r; 151 | 152 | for (i = 0; i < FLORA_MSG_COUNT; ++i) { 153 | if (use_c_api) { 154 | r = flora_cli_unsubscribe(c_flora_cli, flora_msgs[i].name); 155 | } else { 156 | r = flora_cli->unsubscribe(flora_msgs[i].name); 157 | } 158 | if (r != FLORA_CLI_SUCCESS) 159 | KLOGE(TAG, "client unsubscribe failed"); 160 | } 161 | memset(subscribe_flags, 0, sizeof(subscribe_flags)); 162 | memset(post_counter, 0, sizeof(post_counter)); 163 | } 164 | 165 | void TestClient::close() { 166 | if (use_c_api) { 167 | flora_cli_delete(c_flora_cli); 168 | c_flora_cli = 0; 169 | } else 170 | flora_cli.reset(); 171 | } 172 | 173 | void TestClient::recv_post(const char *name, uint32_t msgtype, 174 | shared_ptr &args) { 175 | int32_t i; 176 | int32_t v; 177 | 178 | for (i = 0; i < FLORA_MSG_COUNT; ++i) { 179 | if (strcmp(name, flora_msgs[i].name) == 0) { 180 | ++recv_post_counter[i]; 181 | if (flora_msgs[i].args.get() == nullptr && args.get() == nullptr) 182 | break; 183 | if ((flora_msgs[i].args.get() == nullptr && args.get()) || 184 | (flora_msgs[i].args.get() && args.get() == nullptr) || 185 | args->read(v) != CAPS_SUCCESS || v != i) { 186 | KLOGE(TAG, "received incorrect msg args, msgtype = %u", msgtype); 187 | break; 188 | } 189 | break; 190 | } 191 | } 192 | } 193 | 194 | void TestClient::recv_call(const char *name, shared_ptr &args, 195 | shared_ptr &reply) { 196 | int32_t i; 197 | int32_t v; 198 | 199 | for (i = 0; i < FLORA_MSG_COUNT; ++i) { 200 | if (strcmp(name, flora_msgs[i].name) == 0) { 201 | ++recv_call_counter[i]; 202 | if (flora_msgs[i].args.get() == nullptr && args.get() == nullptr) 203 | break; 204 | if ((flora_msgs[i].args.get() == nullptr && args.get()) || 205 | (flora_msgs[i].args.get() && args.get() == nullptr) || 206 | args->read(v) != CAPS_SUCCESS || v != i) { 207 | KLOGE(TAG, "received incorrect method args"); 208 | break; 209 | } 210 | break; 211 | } 212 | } 213 | reply->write_data(args); 214 | reply->end(); 215 | } 216 | 217 | void TestClient::c_recv_post(const char *name, uint32_t msgtype, caps_t args) { 218 | int32_t i; 219 | int32_t v; 220 | 221 | for (i = 0; i < FLORA_MSG_COUNT; ++i) { 222 | if (strcmp(name, flora_msgs[i].name) == 0) { 223 | ++recv_post_counter[i]; 224 | if (flora_msgs[i].c_args == 0 && args == 0) 225 | break; 226 | if ((flora_msgs[i].c_args == 0 && args != 0) || 227 | (flora_msgs[i].c_args != 0 && args == 0) || 228 | caps_read_integer(args, &v) != CAPS_SUCCESS || v != i) { 229 | KLOGE(TAG, "received incorrect msg args, msgtype = %u", msgtype); 230 | break; 231 | } 232 | break; 233 | } 234 | } 235 | } 236 | 237 | void TestClient::c_recv_call(const char *name, caps_t args, 238 | flora_call_reply_t reply) { 239 | int32_t i; 240 | int32_t v; 241 | 242 | for (i = 0; i < FLORA_MSG_COUNT; ++i) { 243 | if (strcmp(name, flora_msgs[i].name) == 0) { 244 | ++recv_call_counter[i]; 245 | if (flora_msgs[i].c_args == 0 && args == 0) 246 | break; 247 | if ((flora_msgs[i].c_args == 0 && args != 0) || 248 | (flora_msgs[i].c_args != 0 && args == 0) || 249 | caps_read_integer(args, &v) != CAPS_SUCCESS || v != i) { 250 | KLOGE(TAG, "received incorrect method args"); 251 | break; 252 | } 253 | break; 254 | } 255 | } 256 | caps_t data = caps_create(); 257 | caps_write_integer(data, v); 258 | flora_call_reply_write_data(reply, data); 259 | flora_call_reply_end(reply); 260 | } 261 | -------------------------------------------------------------------------------- /src/sock-poll.cc: -------------------------------------------------------------------------------- 1 | #include "sock-poll.h" 2 | #include "ser-helper.h" 3 | #include "flora-svc.h" 4 | #include "rlog.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | using namespace std; 17 | 18 | #define POLL_TYPE_UNIX 0 19 | #define POLL_TYPE_TCP 1 20 | #define TCP_SOCK_WRITE_TIMEOUT 4000 21 | #define UNIX_SOCK_WRITE_TIMEOUT 800 22 | 23 | namespace flora { 24 | namespace internal { 25 | 26 | SocketPoll::SocketPoll(const std::string &name) { 27 | this->name = name; 28 | type = POLL_TYPE_UNIX; 29 | this->port = 0; 30 | } 31 | 32 | SocketPoll::SocketPoll(const std::string &host, int32_t port) { 33 | this->host = host; 34 | this->port = port; 35 | type = POLL_TYPE_TCP; 36 | } 37 | 38 | SocketPoll::~SocketPoll() { stop(); } 39 | 40 | int32_t SocketPoll::start(shared_ptr &disp) { 41 | unique_lock locker(start_mutex); 42 | if (dispatcher.get()) 43 | return FLORA_POLL_ALREADY_START; 44 | bool r; 45 | if (type == POLL_TYPE_TCP) 46 | r = init_tcp_socket(); 47 | else 48 | r = init_unix_socket(); 49 | if (!r) 50 | return FLORA_POLL_SYSERR; 51 | run_thread = thread([this]() { this->run(); }); 52 | dispatcher = static_pointer_cast(disp); 53 | max_msg_size = dispatcher->max_msg_size(); 54 | // wait until thread running 55 | start_cond.wait(locker); 56 | return FLORA_POLL_SUCCESS; 57 | } 58 | 59 | void SocketPoll::stop() { 60 | unique_lock locker(start_mutex); 61 | if (dispatcher.get() == nullptr) 62 | return; 63 | int fd = listen_fd; 64 | listen_fd = -1; 65 | ::shutdown(fd, SHUT_RDWR); 66 | locker.unlock(); 67 | run_thread.join(); 68 | ::close(fd); 69 | dispatcher.reset(); 70 | } 71 | 72 | int32_t SocketPoll::do_poll(fd_set *rfds, int max_fd) { 73 | int r; 74 | #ifdef SELECT_BLOCK_IF_FD_CLOSED 75 | struct timeval tv; 76 | tv.tv_sec = 5; 77 | tv.tv_usec = 0; 78 | #endif 79 | while (true) { 80 | #ifdef SELECT_BLOCK_IF_FD_CLOSED 81 | r = select(max_fd, rfds, nullptr, nullptr, &tv); 82 | #else 83 | r = select(max_fd, rfds, nullptr, nullptr, nullptr); 84 | #endif 85 | if (r < 0) { 86 | if (errno == EAGAIN) { 87 | sleep(1); 88 | continue; 89 | } 90 | KLOGE(TAG, "select failed: %s", strerror(errno)); 91 | } 92 | break; 93 | } 94 | return r; 95 | } 96 | 97 | static int unix_accept(int lfd) { 98 | sockaddr_un addr; 99 | socklen_t addr_len = sizeof(addr); 100 | #ifdef __APPLE__ 101 | auto fd = accept(lfd, (sockaddr *)&addr, &addr_len); 102 | auto f = fcntl(fd, F_GETFD); 103 | f |= FD_CLOEXEC; 104 | fcntl(fd, F_SETFD, f); 105 | return fd; 106 | #else 107 | return accept4(lfd, (sockaddr *)&addr, &addr_len, SOCK_CLOEXEC); 108 | #endif 109 | } 110 | 111 | static int tcp_accept(int lfd, uint64_t& tag) { 112 | sockaddr_in addr; 113 | socklen_t addr_len = sizeof(addr); 114 | #ifdef __APPLE__ 115 | auto fd = accept(lfd, (sockaddr *)&addr, &addr_len); 116 | auto f = fcntl(fd, F_GETFD); 117 | f |= FD_CLOEXEC; 118 | fcntl(fd, F_SETFD, f); 119 | #else 120 | auto fd = accept4(lfd, (sockaddr *)&addr, &addr_len, SOCK_CLOEXEC); 121 | #endif 122 | if (fd < 0) 123 | return fd; 124 | tag = TagHelper::create(addr); 125 | return fd; 126 | } 127 | 128 | void SocketPoll::run() { 129 | fd_set rfds; 130 | int ifd; 131 | 132 | start_mutex.lock(); 133 | FD_ZERO(&all_fds); 134 | FD_SET(listen_fd, &all_fds); 135 | max_fd = listen_fd + 1; 136 | start_cond.notify_one(); 137 | start_mutex.unlock(); 138 | while (true) { 139 | rfds = all_fds; 140 | int32_t r = do_poll(&rfds, max_fd); 141 | // system call error 142 | if (r < 0) 143 | break; 144 | // closed 145 | int lfd = get_listen_fd(); 146 | if (lfd < 0) { 147 | KLOGI(TAG, "unix poll closed, quit"); 148 | break; 149 | } 150 | // select timeout, this Poll not closed, continue 151 | if (r == 0) 152 | continue; 153 | for (ifd = 0; ifd < max_fd; ++ifd) { 154 | if (FD_ISSET(ifd, &rfds)) { 155 | if (ifd == lfd) { 156 | auto new_adap = do_accept(lfd); 157 | if (new_adap == nullptr) { 158 | KLOGE(TAG, "accept failed: %s", strerror(errno)); 159 | continue; 160 | } 161 | KLOGI(TAG, "accept new connection %d", 162 | static_pointer_cast(new_adap)->socket()); 163 | } else { 164 | auto it = adapters.find(ifd); 165 | if (it != adapters.end()) { 166 | KLOGD(TAG, "read from fd %d", ifd); 167 | if (!do_read(it->second)) { 168 | KLOGD(TAG, "delete adapter %s", 169 | it->second->info ? it->second->info->name.c_str() : ""); 170 | delete_adapter(it->second); 171 | adapters.erase(it); 172 | } 173 | } 174 | } 175 | } 176 | } 177 | } 178 | 179 | // release resources 180 | while (adapters.size() > 0) { 181 | auto it = adapters.begin(); 182 | delete_adapter(it->second); 183 | adapters.erase(it); 184 | } 185 | KLOGI(TAG, "unix poll: run thread quit"); 186 | } 187 | 188 | shared_ptr SocketPoll::do_accept(int lfd) { 189 | int new_fd = -1; 190 | uint64_t tag = 0; 191 | 192 | if (type == POLL_TYPE_TCP) 193 | new_fd = tcp_accept(lfd, tag); 194 | else 195 | new_fd = unix_accept(lfd); 196 | if (new_fd < 0) 197 | return nullptr; 198 | auto adap = new_adapter(new_fd); 199 | adap->tag = tag; 200 | adapters.insert(make_pair(new_fd, adap)); 201 | return adap; 202 | } 203 | 204 | bool SocketPoll::init_unix_socket() { 205 | #ifdef __APPLE__ 206 | int fd = socket(AF_UNIX, SOCK_STREAM, 0); 207 | #else 208 | int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 209 | #endif 210 | if (fd < 0) 211 | return false; 212 | #ifdef __APPLE__ 213 | auto f = fcntl(fd, F_GETFD); 214 | f |= FD_CLOEXEC; 215 | fcntl(fd, F_SETFD, f); 216 | #endif 217 | struct sockaddr_un addr; 218 | memset(&addr, 0, sizeof(addr)); 219 | addr.sun_family = AF_UNIX; 220 | strcpy(addr.sun_path, name.c_str()); 221 | unlink(name.c_str()); 222 | if (::bind(fd, (sockaddr *)&addr, sizeof(addr)) < 0) { 223 | ::close(fd); 224 | KLOGE(TAG, "socket bind failed: %s", strerror(errno)); 225 | return false; 226 | } 227 | listen(fd, 10); 228 | listen_fd = fd; 229 | return true; 230 | } 231 | 232 | bool SocketPoll::init_tcp_socket() { 233 | #ifdef __APPLE__ 234 | int fd = socket(AF_INET, SOCK_STREAM, 0); 235 | #else 236 | int fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); 237 | #endif 238 | if (fd < 0) 239 | return false; 240 | #ifdef __APPLE__ 241 | auto f = fcntl(fd, F_GETFD); 242 | f |= FD_CLOEXEC; 243 | fcntl(fd, F_SETFD, f); 244 | #endif 245 | int v = 1; 246 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)); 247 | struct sockaddr_in addr; 248 | struct hostent *hp; 249 | hp = gethostbyname(host.c_str()); 250 | if (hp == nullptr) { 251 | KLOGE(TAG, "gethostbyname failed for host %s: %s", host.c_str(), 252 | strerror(errno)); 253 | ::close(fd); 254 | return false; 255 | } 256 | memset(&addr, 0, sizeof(addr)); 257 | addr.sin_family = AF_INET; 258 | memcpy(&addr.sin_addr, hp->h_addr_list[0], sizeof(addr.sin_addr)); 259 | addr.sin_port = htons(port); 260 | if (::bind(fd, (sockaddr *)&addr, sizeof(addr)) < 0) { 261 | ::close(fd); 262 | KLOGE(TAG, "socket bind failed: %s", strerror(errno)); 263 | return false; 264 | } 265 | listen(fd, 10); 266 | listen_fd = fd; 267 | return true; 268 | } 269 | 270 | shared_ptr SocketPoll::new_adapter(int fd) { 271 | shared_ptr adap = make_shared( 272 | fd, max_msg_size, type == POLL_TYPE_TCP ? CAPS_FLAG_NET_BYTEORDER : 0, 273 | type == POLL_TYPE_TCP ? TCP_SOCK_WRITE_TIMEOUT : UNIX_SOCK_WRITE_TIMEOUT); 274 | if (fd >= max_fd) 275 | max_fd = fd + 1; 276 | FD_SET(fd, &all_fds); 277 | return static_pointer_cast(adap); 278 | } 279 | 280 | void SocketPoll::delete_adapter(shared_ptr &adap) { 281 | int fd = static_pointer_cast(adap)->socket(); 282 | adap->close(); 283 | FD_CLR(fd, &all_fds); 284 | ::close(fd); 285 | dispatcher->erase_adapter(adap); 286 | } 287 | 288 | bool SocketPoll::do_read(shared_ptr &adap) { 289 | int32_t r = adap->read(); 290 | if (r != SOCK_ADAPTER_SUCCESS) 291 | return false; 292 | 293 | Frame frame; 294 | while (true) { 295 | r = adap->next_frame(frame); 296 | if (r == SOCK_ADAPTER_SUCCESS) { 297 | if (!dispatcher->put(frame.data, frame.size, adap)) { 298 | KLOGE(TAG, "dispatcher put failed"); 299 | return false; 300 | } 301 | } else if (r == SOCK_ADAPTER_ENOMORE) { 302 | break; 303 | } else { 304 | KLOGE(TAG, "adapter next frame return %d", r); 305 | return false; 306 | } 307 | } 308 | return true; 309 | } 310 | 311 | int SocketPoll::get_listen_fd() { 312 | lock_guard locker(start_mutex); 313 | return listen_fd; 314 | } 315 | 316 | } // namespace internal 317 | } // namespace flora 318 | -------------------------------------------------------------------------------- /src/ser-helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "caps.h" 4 | #include "defs.h" 5 | #include "disp.h" 6 | #include "flora-cli.h" 7 | #include 8 | #include 9 | #include 10 | 11 | namespace flora { 12 | namespace internal { 13 | 14 | class RequestSerializer { 15 | public: 16 | static int32_t serialize_auth(uint32_t version, const char *extra, 17 | int32_t pid, uint32_t flags, void *data, 18 | uint32_t size, uint32_t ser_flags); 19 | 20 | static int32_t serialize_subscribe(const char *name, void *data, 21 | uint32_t size, uint32_t flags); 22 | 23 | static int32_t serialize_unsubscribe(const char *name, void *data, 24 | uint32_t size, uint32_t flags); 25 | 26 | static int32_t serialize_declare_method(const char *name, void *data, 27 | uint32_t size, uint32_t flags); 28 | 29 | static int32_t serialize_remove_method(const char *name, void *data, 30 | uint32_t size, uint32_t flags); 31 | 32 | static int32_t serialize_post(const char *name, uint32_t msgtype, 33 | std::shared_ptr &args, void *data, 34 | uint32_t size, uint32_t flags); 35 | 36 | static int32_t serialize_call(const char *name, std::shared_ptr &args, 37 | const char *target, int32_t id, 38 | uint32_t timeout, void *data, uint32_t size, 39 | uint32_t flags); 40 | 41 | static int32_t serialize_reply(int32_t id, int32_t code, 42 | std::shared_ptr &values, void *data, 43 | uint32_t size, uint32_t flags); 44 | 45 | static int32_t serialize_ping(void *data, uint32_t size, uint32_t flags); 46 | }; 47 | 48 | class ResponseSerializer { 49 | public: 50 | static int32_t serialize_auth(int32_t result, uint32_t version, void *data, 51 | uint32_t size, uint32_t flags); 52 | 53 | static int32_t serialize_post(const char *name, uint32_t msgtype, 54 | std::shared_ptr &args, uint64_t tag, 55 | const char *cliname, void *data, uint32_t size, 56 | uint32_t flags); 57 | 58 | static int32_t serialize_call(const char *name, std::shared_ptr &args, 59 | int32_t id, uint64_t tag, const char *cliname, 60 | void *data, uint32_t size, uint32_t flags); 61 | 62 | static int32_t serialize_reply(int32_t id, int32_t rescode, Response *reply, 63 | uint64_t tag, void *data, uint32_t size, 64 | uint32_t flags); 65 | 66 | static int32_t serialize_monitor_list_all(AdapterInfoMap &infos, void *data, 67 | uint32_t size, uint32_t flags); 68 | 69 | static int32_t serialize_monitor_list_add(AdapterInfo &info, void *data, 70 | uint32_t size, uint32_t flags); 71 | 72 | static int32_t serialize_monitor_list_remove(uint32_t id, void *data, 73 | uint32_t size, uint32_t flags); 74 | 75 | static int32_t serialize_monitor_sub_all(AdapterInfoMap &infos, void *data, 76 | uint32_t size, uint32_t flags); 77 | 78 | static int32_t serialize_monitor_sub_add(uint32_t id, const std::string &name, 79 | void *data, uint32_t size, 80 | uint32_t flags); 81 | 82 | static int32_t serialize_monitor_sub_remove(uint32_t id, 83 | const std::string &name, 84 | void *data, uint32_t size, 85 | uint32_t flags); 86 | 87 | static int32_t serialize_monitor_decl_all(AdapterInfoMap &infos, void *data, 88 | uint32_t size, uint32_t flags); 89 | 90 | static int32_t serialize_monitor_decl_add(uint32_t id, 91 | const std::string &name, void *data, 92 | uint32_t size, uint32_t flags); 93 | 94 | static int32_t serialize_monitor_decl_remove(uint32_t id, 95 | const std::string &name, 96 | void *data, uint32_t size, 97 | uint32_t flags); 98 | 99 | static int32_t serialize_monitor_post(uint32_t from, const std::string &name, 100 | void *data, uint32_t size, 101 | uint32_t flags); 102 | 103 | static int32_t serialize_monitor_call(uint32_t from, const std::string &name, 104 | const std::string &target, int32_t err, 105 | void *data, uint32_t size, 106 | uint32_t flags); 107 | 108 | static int32_t serialize_pong(void *data, uint32_t size, uint32_t flags); 109 | }; 110 | 111 | class RequestParser { 112 | public: 113 | static int32_t parse_auth(std::shared_ptr &caps, uint32_t &version, 114 | std::string &extra, int32_t &pid, uint32_t &flags); 115 | 116 | static int32_t parse_subscribe(std::shared_ptr &caps, 117 | std::string &name); 118 | 119 | static int32_t parse_unsubscribe(std::shared_ptr &caps, 120 | std::string &name); 121 | 122 | static int32_t parse_declare_method(std::shared_ptr &caps, 123 | std::string &name); 124 | 125 | static int32_t parse_remove_method(std::shared_ptr &caps, 126 | std::string &name); 127 | 128 | static int32_t parse_post(std::shared_ptr &caps, std::string &name, 129 | uint32_t &msgtype, std::shared_ptr &args); 130 | 131 | static int32_t parse_call(std::shared_ptr &caps, std::string &name, 132 | std::shared_ptr &args, std::string &target, 133 | int32_t &id, uint32_t &timeout); 134 | 135 | static int32_t parse_reply(std::shared_ptr &caps, int32_t &id, 136 | int32_t &code, std::shared_ptr &values); 137 | }; 138 | 139 | class ResponseParser { 140 | public: 141 | static int32_t parse_auth(std::shared_ptr &caps, int32_t &result, 142 | uint32_t &version); 143 | 144 | static int32_t parse_post(std::shared_ptr &caps, std::string &name, 145 | uint32_t &msgtype, std::shared_ptr &args, 146 | uint64_t &tag, std::string &cliname); 147 | 148 | static int32_t parse_call(std::shared_ptr &caps, std::string &name, 149 | std::shared_ptr &args, int32_t &id, 150 | uint64_t &tag, std::string &cliname); 151 | 152 | static int32_t parse_reply(std::shared_ptr &caps, int32_t &id, 153 | int32_t &rescode, Response &reply, uint64_t &tag); 154 | 155 | static int32_t parse_monitor_list_all(std::shared_ptr &caps, 156 | std::vector &infos); 157 | 158 | static int32_t parse_monitor_list_add(std::shared_ptr &caps, 159 | MonitorListItem &info); 160 | 161 | static int32_t parse_monitor_list_remove(std::shared_ptr &caps, 162 | uint32_t &id); 163 | 164 | static int32_t 165 | parse_monitor_sub_all(std::shared_ptr &caps, 166 | std::vector &infos); 167 | 168 | static int32_t parse_monitor_sub_add(std::shared_ptr &caps, 169 | MonitorSubscriptionItem &info); 170 | 171 | static int32_t parse_monitor_sub_remove(std::shared_ptr &caps, 172 | MonitorSubscriptionItem &info); 173 | 174 | static int32_t 175 | parse_monitor_decl_all(std::shared_ptr &caps, 176 | std::vector &infos); 177 | 178 | static int32_t parse_monitor_decl_add(std::shared_ptr &caps, 179 | MonitorDeclarationItem &info); 180 | 181 | static int32_t parse_monitor_decl_remove(std::shared_ptr &caps, 182 | MonitorDeclarationItem &info); 183 | 184 | static int32_t parse_monitor_post(std::shared_ptr &caps, 185 | MonitorPostInfo &info); 186 | 187 | static int32_t parse_monitor_call(std::shared_ptr &caps, 188 | MonitorCallInfo &info); 189 | }; 190 | 191 | bool is_valid_msgtype(uint32_t msgtype); 192 | 193 | class TagHelper { 194 | public: 195 | static uint64_t create(struct sockaddr_in& addr) { 196 | return (((uint64_t)(0x80000000 | addr.sin_port)) << 32) | addr.sin_addr.s_addr; 197 | } 198 | 199 | static uint64_t create(uint32_t pid) { return pid; } 200 | 201 | // return: 0 unix 202 | // 1 tcp 203 | static uint32_t type(uint64_t tag) { 204 | return ((tag >> 32) & 0x80000000) ? 1 : 0; 205 | } 206 | 207 | static pid_t pid(uint64_t tag) { 208 | return tag & 0xffffffff; 209 | } 210 | 211 | static const char* ipaddr(uint64_t tag) { 212 | struct in_addr iaddr; 213 | iaddr.s_addr = (tag & 0xffffffff); 214 | return inet_ntoa(iaddr); 215 | } 216 | 217 | static uint16_t port(uint64_t tag) { 218 | return (tag >> 32) & 0xffff; 219 | } 220 | 221 | static void to_string(uint64_t tag, std::string& str) { 222 | char buf[16]; 223 | if (type(tag) == 0) { 224 | snprintf(buf, sizeof(buf), "%d", pid(tag)); 225 | str = buf; 226 | } else { 227 | str = ipaddr(tag); 228 | str += ':'; 229 | snprintf(buf, sizeof(buf), "%d", port(tag)); 230 | str += buf; 231 | } 232 | } 233 | }; 234 | 235 | } // namespace internal 236 | } // namespace flora 237 | -------------------------------------------------------------------------------- /android/jni/com_rokid_flora_Caps.cpp: -------------------------------------------------------------------------------- 1 | #include "defs.h" 2 | #include "rlog.h" 3 | 4 | using namespace std; 5 | 6 | #define JAVA_FIELD_INTEGER_VALUE 0 7 | #define JAVA_FIELD_FLOAT_VALUE 1 8 | #define JAVA_FIELD_LONG_VALUE 2 9 | #define JAVA_FIELD_DOUBLE_VALUE 3 10 | #define JAVA_FIELD_STRING_VALUE 4 11 | #define JAVA_FIELD_BINARY_VALUE 5 12 | #define JAVA_FIELD_CAPS_VALUE 6 13 | #define JAVA_FIELD_NUMBER 7 14 | 15 | typedef struct { 16 | jfieldID java_fields[JAVA_FIELD_NUMBER]; 17 | } GlobalConstants; 18 | static GlobalConstants g_constants; 19 | 20 | static void com_rokid_flora_Caps_native_init(JNIEnv *env, jclass thiz, 21 | jobjectArray classes) { 22 | jsize len = env->GetArrayLength(classes); 23 | if (len != JAVA_FIELD_NUMBER) { 24 | KLOGE(TAG, "native_init: classes array length incorrect %d/%d", len, 25 | JAVA_FIELD_NUMBER); 26 | return; 27 | } 28 | jclass cls; 29 | cls = (jclass)env->GetObjectArrayElement(classes, 0); 30 | g_constants.java_fields[JAVA_FIELD_INTEGER_VALUE] = 31 | env->GetFieldID(cls, "value", "I"); 32 | cls = (jclass)env->GetObjectArrayElement(classes, 1); 33 | g_constants.java_fields[JAVA_FIELD_FLOAT_VALUE] = 34 | env->GetFieldID(cls, "value", "F"); 35 | cls = (jclass)env->GetObjectArrayElement(classes, 2); 36 | g_constants.java_fields[JAVA_FIELD_LONG_VALUE] = 37 | env->GetFieldID(cls, "value", "J"); 38 | cls = (jclass)env->GetObjectArrayElement(classes, 3); 39 | g_constants.java_fields[JAVA_FIELD_DOUBLE_VALUE] = 40 | env->GetFieldID(cls, "value", "D"); 41 | cls = (jclass)env->GetObjectArrayElement(classes, 4); 42 | g_constants.java_fields[JAVA_FIELD_STRING_VALUE] = 43 | env->GetFieldID(cls, "value", "Ljava/lang/String;"); 44 | cls = (jclass)env->GetObjectArrayElement(classes, 5); 45 | g_constants.java_fields[JAVA_FIELD_BINARY_VALUE] = 46 | env->GetFieldID(cls, "value", "[B"); 47 | cls = (jclass)env->GetObjectArrayElement(classes, 6); 48 | g_constants.java_fields[JAVA_FIELD_CAPS_VALUE] = 49 | env->GetFieldID(cls, "value", "J"); 50 | } 51 | 52 | static jlong com_rokid_flora_Caps_native_create(JNIEnv *env, jobject thiz) { 53 | CapsNativeHandle *handle = new CapsNativeHandle(); 54 | handle->caps = Caps::new_instance(); 55 | return (jlong)handle; 56 | } 57 | 58 | static void com_rokid_flora_Caps_native_destroy(JNIEnv *env, jobject thiz, 59 | jlong h) { 60 | if (h == 0) 61 | return; 62 | CapsNativeHandle *handle = (CapsNativeHandle *)h; 63 | delete handle; 64 | } 65 | 66 | static jint com_rokid_flora_Caps_native_write_integer(JNIEnv *env, jobject thiz, 67 | jlong h, jint v) { 68 | CapsNativeHandle *handle = (CapsNativeHandle *)h; 69 | return handle->caps->write((int32_t)v); 70 | } 71 | 72 | static jint com_rokid_flora_Caps_native_write_float(JNIEnv *env, jobject thiz, 73 | jlong h, jfloat v) { 74 | CapsNativeHandle *handle = (CapsNativeHandle *)h; 75 | return handle->caps->write(v); 76 | } 77 | 78 | static jint com_rokid_flora_Caps_native_write_long(JNIEnv *env, jobject thiz, 79 | jlong h, jlong v) { 80 | CapsNativeHandle *handle = (CapsNativeHandle *)h; 81 | return handle->caps->write((int64_t)v); 82 | } 83 | 84 | static jint com_rokid_flora_Caps_native_write_double(JNIEnv *env, jobject thiz, 85 | jlong h, jdouble v) { 86 | CapsNativeHandle *handle = (CapsNativeHandle *)h; 87 | return handle->caps->write(v); 88 | } 89 | 90 | static jint com_rokid_flora_Caps_native_write_string(JNIEnv *env, jobject thiz, 91 | jlong h, jstring v) { 92 | CapsNativeHandle *handle = (CapsNativeHandle *)h; 93 | char *str = nullptr; 94 | if (v != nullptr) { 95 | jsize len = env->GetStringUTFLength(v); 96 | str = new char[len + 1]; 97 | env->GetStringUTFRegion(v, 0, len, str); 98 | str[len] = '\0'; 99 | } 100 | int32_t r = handle->caps->write(str); 101 | if (str) 102 | delete[] str; 103 | return r; 104 | } 105 | 106 | static jint com_rokid_flora_Caps_native_write_binary(JNIEnv *env, jobject thiz, 107 | jlong h, jbyteArray v, 108 | jint offset, jint length) { 109 | CapsNativeHandle *handle = (CapsNativeHandle *)h; 110 | jbyte *data = nullptr; 111 | if (v != nullptr) { 112 | data = new jbyte[length]; 113 | env->GetByteArrayRegion(v, offset, length, data); 114 | } else { 115 | offset = 0; 116 | length = 0; 117 | } 118 | int32_t r = handle->caps->write(data, length); 119 | if (data) 120 | delete[] data; 121 | return r; 122 | } 123 | 124 | static jint com_rokid_flora_Caps_native_write_caps(JNIEnv *env, jobject thiz, 125 | jlong h, jlong v) { 126 | CapsNativeHandle *handle = (CapsNativeHandle *)h; 127 | int32_t r; 128 | if (v == 0) { 129 | shared_ptr t; 130 | r = handle->caps->write(t); 131 | } else { 132 | CapsNativeHandle *sub = (CapsNativeHandle *)v; 133 | r = handle->caps->write(sub->caps); 134 | } 135 | return r; 136 | } 137 | 138 | static jint com_rokid_flora_Caps_native_read_integer(JNIEnv *env, jobject thiz, 139 | jlong h, jobject res) { 140 | CapsNativeHandle *handle = (CapsNativeHandle *)h; 141 | int32_t v; 142 | int32_t r = handle->caps->read(v); 143 | if (r == CAPS_SUCCESS) { 144 | env->SetIntField(res, g_constants.java_fields[JAVA_FIELD_INTEGER_VALUE], v); 145 | } 146 | return r; 147 | } 148 | 149 | static jint com_rokid_flora_Caps_native_read_float(JNIEnv *env, jobject thiz, 150 | jlong h, jobject res) { 151 | CapsNativeHandle *handle = (CapsNativeHandle *)h; 152 | float v; 153 | int32_t r = handle->caps->read(v); 154 | if (r == CAPS_SUCCESS) { 155 | env->SetFloatField(res, g_constants.java_fields[JAVA_FIELD_FLOAT_VALUE], v); 156 | } 157 | return r; 158 | } 159 | 160 | static jint com_rokid_flora_Caps_native_read_long(JNIEnv *env, jobject thiz, 161 | jlong h, jobject res) { 162 | CapsNativeHandle *handle = (CapsNativeHandle *)h; 163 | int64_t v; 164 | int32_t r = handle->caps->read(v); 165 | if (r == CAPS_SUCCESS) { 166 | env->SetLongField(res, g_constants.java_fields[JAVA_FIELD_LONG_VALUE], 167 | (jlong)v); 168 | } 169 | return r; 170 | } 171 | 172 | static jint com_rokid_flora_Caps_native_read_double(JNIEnv *env, jobject thiz, 173 | jlong h, jobject res) { 174 | CapsNativeHandle *handle = (CapsNativeHandle *)h; 175 | double v; 176 | int32_t r = handle->caps->read(v); 177 | if (r == CAPS_SUCCESS) { 178 | env->SetDoubleField(res, g_constants.java_fields[JAVA_FIELD_DOUBLE_VALUE], 179 | v); 180 | } 181 | return r; 182 | } 183 | 184 | static jint com_rokid_flora_Caps_native_read_string(JNIEnv *env, jobject thiz, 185 | jlong h, jobject res) { 186 | CapsNativeHandle *handle = (CapsNativeHandle *)h; 187 | string s; 188 | int32_t r = handle->caps->read_string(s); 189 | if (r == CAPS_SUCCESS) { 190 | jstring v = env->NewStringUTF(s.c_str()); 191 | env->SetObjectField(res, g_constants.java_fields[JAVA_FIELD_STRING_VALUE], 192 | v); 193 | } 194 | return r; 195 | } 196 | 197 | static jint com_rokid_flora_Caps_native_read_binary(JNIEnv *env, jobject thiz, 198 | jlong h, jobject res) { 199 | CapsNativeHandle *handle = (CapsNativeHandle *)h; 200 | string b; 201 | int32_t r = handle->caps->read_binary(b); 202 | if (r == CAPS_SUCCESS) { 203 | jbyteArray v = env->NewByteArray(b.length()); 204 | env->SetByteArrayRegion(v, 0, b.length(), (const jbyte *)b.data()); 205 | env->SetObjectField(res, g_constants.java_fields[JAVA_FIELD_BINARY_VALUE], 206 | v); 207 | } 208 | return r; 209 | } 210 | 211 | static jint com_rokid_flora_Caps_native_read_caps(JNIEnv *env, jobject thiz, 212 | jlong h, jobject res) { 213 | CapsNativeHandle *handle = (CapsNativeHandle *)h; 214 | shared_ptr v; 215 | int32_t r = handle->caps->read(v); 216 | if (r == CAPS_SUCCESS) { 217 | CapsNativeHandle *rh = new CapsNativeHandle(); 218 | rh->caps = v; 219 | env->SetLongField(res, g_constants.java_fields[JAVA_FIELD_LONG_VALUE], 220 | (jlong)rh); 221 | } 222 | return r; 223 | } 224 | 225 | static JNINativeMethod _caps_nmethods[] = { 226 | {"native_init", "([Ljava/lang/Class;)V", 227 | (void *)com_rokid_flora_Caps_native_init}, 228 | {"native_create", "()J", (void *)com_rokid_flora_Caps_native_create}, 229 | {"native_destroy", "(J)V", (void *)com_rokid_flora_Caps_native_destroy}, 230 | {"native_write_integer", "(JI)I", 231 | (void *)com_rokid_flora_Caps_native_write_integer}, 232 | {"native_write_float", "(JF)I", 233 | (void *)com_rokid_flora_Caps_native_write_float}, 234 | {"native_write_long", "(JJ)I", 235 | (void *)com_rokid_flora_Caps_native_write_long}, 236 | {"native_write_double", "(JD)I", 237 | (void *)com_rokid_flora_Caps_native_write_double}, 238 | {"native_write_string", "(JLjava/lang/String;)I", 239 | (void *)com_rokid_flora_Caps_native_write_string}, 240 | {"native_write_binary", "(J[BII)I", 241 | (void *)com_rokid_flora_Caps_native_write_binary}, 242 | {"native_write_caps", "(JJ)I", 243 | (void *)com_rokid_flora_Caps_native_write_caps}, 244 | {"native_read", "(JLcom/rokid/flora/Caps$IntegerValue;)I", 245 | (void *)com_rokid_flora_Caps_native_read_integer}, 246 | {"native_read", "(JLcom/rokid/flora/Caps$FloatValue;)I", 247 | (void *)com_rokid_flora_Caps_native_read_float}, 248 | {"native_read", "(JLcom/rokid/flora/Caps$LongValue;)I", 249 | (void *)com_rokid_flora_Caps_native_read_long}, 250 | {"native_read", "(JLcom/rokid/flora/Caps$DoubleValue;)I", 251 | (void *)com_rokid_flora_Caps_native_read_double}, 252 | {"native_read", "(JLcom/rokid/flora/Caps$StringValue;)I", 253 | (void *)com_rokid_flora_Caps_native_read_string}, 254 | {"native_read", "(JLcom/rokid/flora/Caps$BinaryValue;)I", 255 | (void *)com_rokid_flora_Caps_native_read_binary}, 256 | {"native_read", "(JLcom/rokid/flora/Caps$CapsValue;)I", 257 | (void *)com_rokid_flora_Caps_native_read_caps}, 258 | }; 259 | 260 | int register_com_rokid_flora_Caps(JNIEnv *env) { 261 | const char *kclass = "com/rokid/flora/Caps"; 262 | jclass target = env->FindClass(kclass); 263 | if (target == NULL) { 264 | KLOGE("find class for %s failed", kclass); 265 | return -1; 266 | } 267 | return env->RegisterNatives(target, _caps_nmethods, 268 | sizeof(_caps_nmethods) / sizeof(JNINativeMethod)); 269 | } 270 | -------------------------------------------------------------------------------- /android/jni/com_rokid_flora_Client.cpp: -------------------------------------------------------------------------------- 1 | #include "defs.h" 2 | #include "flora-cli.h" 3 | #include "rlog.h" 4 | 5 | using namespace std; 6 | 7 | #define JAVA_METHOD_POST_CALLBACK 0 8 | #define JAVA_METHOD_GET_CALLBACK 1 9 | #define JAVA_METHOD_DISCONNECTED_CALLBACK 2 10 | #define JAVA_METHOD_LIST_ADD 3 11 | #define JAVA_METHOD_CAPS_CONSTRUCTOR 4 12 | #define JAVA_METHOD_NUMBER 5 13 | 14 | #define JAVA_FIELD_NATIVE_HANDLE 0 15 | #define JAVA_FIELD_REPLY_RETCODE 1 16 | #define JAVA_FIELD_REPLY_MSG 2 17 | #define JAVA_FIELD_REPLY_EXTRA 3 18 | #define JAVA_FIELD_NUMBER 4 19 | 20 | typedef struct { 21 | JavaVM *jvm; 22 | jmethodID java_methods[JAVA_METHOD_NUMBER]; 23 | jfieldID java_fields[JAVA_FIELD_NUMBER]; 24 | jclass java_reply_cls; 25 | jclass java_caps_cls; 26 | } GlobalConstants; 27 | static GlobalConstants g_constants; 28 | 29 | class FloraCallbackNative : public flora::ClientCallback { 30 | public: 31 | void recv_post(const char *name, uint32_t msgtype, shared_ptr &msg) { 32 | attachJavaThread(); 33 | env->PushLocalFrame(16); 34 | jstring nameobj = env->NewStringUTF(name); 35 | CapsNativeHandle *capsh = new CapsNativeHandle(); 36 | capsh->caps = msg; 37 | env->CallVoidMethod(javaThis, 38 | g_constants.java_methods[JAVA_METHOD_POST_CALLBACK], 39 | nameobj, (jlong)capsh, msgtype); 40 | env->PopLocalFrame(0); 41 | detachJavaThread(); 42 | } 43 | 44 | int32_t recv_get(const char *name, shared_ptr &msg, 45 | shared_ptr &reply) { 46 | attachJavaThread(); 47 | env->PushLocalFrame(16); 48 | jstring nameobj = env->NewStringUTF(name); 49 | CapsNativeHandle *capsh = new CapsNativeHandle(); 50 | CapsNativeHandle *replyh = new CapsNativeHandle(); 51 | capsh->caps = msg; 52 | int32_t r = env->CallIntMethod( 53 | javaThis, g_constants.java_methods[JAVA_METHOD_GET_CALLBACK], nameobj, 54 | (jlong)capsh, (jlong)replyh); 55 | reply = replyh->caps; 56 | delete replyh; 57 | env->PopLocalFrame(0); 58 | detachJavaThread(); 59 | return r; 60 | } 61 | 62 | void disconnected() { 63 | attachJavaThread(); 64 | env->PushLocalFrame(16); 65 | env->CallVoidMethod( 66 | javaThis, g_constants.java_methods[JAVA_METHOD_DISCONNECTED_CALLBACK]); 67 | env->PopLocalFrame(0); 68 | detachJavaThread(); 69 | } 70 | 71 | private: 72 | void attachJavaThread() { 73 | if (env == nullptr) 74 | g_constants.jvm->AttachCurrentThreadAsDaemon(&env, nullptr); 75 | } 76 | 77 | void detachJavaThread() { 78 | if (env) { 79 | g_constants.jvm->DetachCurrentThread(); 80 | env = nullptr; 81 | } 82 | } 83 | 84 | public: 85 | jobject javaThis = nullptr; 86 | 87 | private: 88 | JNIEnv *env = nullptr; 89 | }; 90 | 91 | typedef struct { 92 | shared_ptr cli; 93 | FloraCallbackNative callback; 94 | } NativeHandle; 95 | 96 | static void com_rokid_flora_Client_native_init(JNIEnv *env, jclass thiz) { 97 | env->GetJavaVM(&g_constants.jvm); 98 | g_constants.java_methods[JAVA_METHOD_POST_CALLBACK] = 99 | env->GetMethodID(thiz, "nativePostCallback", "(Ljava/lang/String;JI)V"); 100 | g_constants.java_methods[JAVA_METHOD_GET_CALLBACK] = 101 | env->GetMethodID(thiz, "nativeGetCallback", "(Ljava/lang/String;JJ)I"); 102 | g_constants.java_methods[JAVA_METHOD_DISCONNECTED_CALLBACK] = 103 | env->GetMethodID(thiz, "nativeDisconnectCallback", "()V"); 104 | g_constants.java_fields[JAVA_FIELD_NATIVE_HANDLE] = 105 | env->GetFieldID(thiz, "native_handle", "J"); 106 | 107 | jclass cls; 108 | cls = env->FindClass("com/rokid/flora/Client$Reply"); 109 | g_constants.java_reply_cls = (jclass)env->NewGlobalRef(cls); 110 | KLOGI(TAG, "find java class Reply %p", g_constants.java_reply_cls); 111 | g_constants.java_fields[JAVA_FIELD_REPLY_RETCODE] = 112 | env->GetFieldID(g_constants.java_reply_cls, "retCode", "I"); 113 | g_constants.java_fields[JAVA_FIELD_REPLY_MSG] = env->GetFieldID( 114 | g_constants.java_reply_cls, "msg", "Lcom/rokid/flora/Caps;"); 115 | g_constants.java_fields[JAVA_FIELD_REPLY_EXTRA] = env->GetFieldID( 116 | g_constants.java_reply_cls, "extra", "Ljava/lang/String;"); 117 | 118 | cls = env->FindClass("com/rokid/flora/Caps"); 119 | g_constants.java_caps_cls = (jclass)env->NewGlobalRef(cls); 120 | g_constants.java_methods[JAVA_METHOD_CAPS_CONSTRUCTOR] = 121 | env->GetMethodID(g_constants.java_caps_cls, "", "(J)V"); 122 | 123 | cls = env->FindClass("java/util/List"); 124 | g_constants.java_methods[JAVA_METHOD_LIST_ADD] = 125 | env->GetMethodID(cls, "add", "(Ljava/lang/Object;)Z"); 126 | } 127 | 128 | static jint com_rokid_flora_Client_native_connect(JNIEnv *env, jobject thiz, 129 | jstring uri, jint bufsize) { 130 | if (thiz == nullptr || uri == nullptr) 131 | return FLORA_CLI_EINVAL; 132 | NativeHandle *handle = new NativeHandle(); 133 | char *uristr; 134 | jsize slen; 135 | handle->callback.javaThis = env->NewGlobalRef(thiz); 136 | slen = env->GetStringUTFLength(uri); 137 | uristr = new char[slen + 1]; 138 | env->GetStringUTFRegion(uri, 0, slen, uristr); 139 | uristr[slen] = '\0'; 140 | int32_t r = 141 | flora::Client::connect(uristr, &handle->callback, bufsize, handle->cli); 142 | delete[] uristr; 143 | if (r == FLORA_CLI_SUCCESS) { 144 | env->SetLongField(thiz, g_constants.java_fields[JAVA_FIELD_NATIVE_HANDLE], 145 | (jlong)handle); 146 | } else { 147 | env->DeleteGlobalRef(handle->callback.javaThis); 148 | delete handle; 149 | } 150 | return r; 151 | } 152 | 153 | static void com_rokid_flora_Client_native_destroy(JNIEnv *env, jobject thiz, 154 | jlong handle) { 155 | if (handle == 0) 156 | return; 157 | NativeHandle *nh = (NativeHandle *)handle; 158 | env->DeleteGlobalRef(nh->callback.javaThis); 159 | delete nh; 160 | } 161 | 162 | static jint com_rokid_flora_Client_native_subscribe(JNIEnv *env, jobject thiz, 163 | jlong handle, jstring name, 164 | jint type) { 165 | NativeHandle *nh = (NativeHandle *)handle; 166 | char *namestr; 167 | jsize slen; 168 | slen = env->GetStringUTFLength(name); 169 | namestr = new char[slen + 1]; 170 | env->GetStringUTFRegion(name, 0, slen, namestr); 171 | namestr[slen] = '\0'; 172 | int32_t r = nh->cli->subscribe(namestr, type); 173 | delete[] namestr; 174 | return r; 175 | } 176 | 177 | static jint com_rokid_flora_Client_native_unsubscribe(JNIEnv *env, jobject thiz, 178 | jlong handle, 179 | jstring name, jint type) { 180 | NativeHandle *nh = (NativeHandle *)handle; 181 | char *namestr; 182 | jsize slen; 183 | slen = env->GetStringUTFLength(name); 184 | namestr = new char[slen + 1]; 185 | env->GetStringUTFRegion(name, 0, slen, namestr); 186 | namestr[slen] = '\0'; 187 | int32_t r = nh->cli->unsubscribe(namestr, type); 188 | delete[] namestr; 189 | return r; 190 | } 191 | 192 | static jint com_rokid_flora_Client_native_post(JNIEnv *env, jobject thiz, 193 | jlong handle, jstring name, 194 | jlong msg, jint type) { 195 | NativeHandle *nh = (NativeHandle *)handle; 196 | char *namestr; 197 | jsize slen; 198 | slen = env->GetStringUTFLength(name); 199 | namestr = new char[slen + 1]; 200 | env->GetStringUTFRegion(name, 0, slen, namestr); 201 | namestr[slen] = '\0'; 202 | CapsNativeHandle *cmsg = (CapsNativeHandle *)msg; 203 | shared_ptr t; 204 | int32_t r = nh->cli->post(namestr, cmsg ? cmsg->caps : t, type); 205 | delete[] namestr; 206 | return r; 207 | } 208 | 209 | static void gen_java_reply_list(JNIEnv *env, jobject replys, 210 | vector &nreps) { 211 | jobject jr; 212 | jobject jm; 213 | jobject je; 214 | vector::iterator it; 215 | 216 | for (it = nreps.begin(); it != nreps.end(); ++it) { 217 | jr = env->AllocObject(g_constants.java_reply_cls); 218 | env->SetIntField(jr, g_constants.java_fields[JAVA_FIELD_REPLY_RETCODE], 219 | (*it).ret_code); 220 | CapsNativeHandle *cnh = new CapsNativeHandle(); 221 | cnh->caps = (*it).data; 222 | jm = env->NewObject(g_constants.java_caps_cls, 223 | g_constants.java_methods[JAVA_METHOD_CAPS_CONSTRUCTOR], 224 | (jlong)cnh); 225 | env->SetObjectField(jr, g_constants.java_fields[JAVA_FIELD_REPLY_MSG], jm); 226 | je = env->NewStringUTF((*it).extra.c_str()); 227 | env->SetObjectField(jr, g_constants.java_fields[JAVA_FIELD_REPLY_EXTRA], 228 | je); 229 | env->CallVoidMethod(replys, g_constants.java_methods[JAVA_METHOD_LIST_ADD], 230 | jr); 231 | } 232 | } 233 | 234 | static jint com_rokid_flora_Client_native_get(JNIEnv *env, jobject thiz, 235 | jlong handle, jstring name, 236 | jlong msg, jobject replys, 237 | jint timeout) { 238 | NativeHandle *nh = (NativeHandle *)handle; 239 | char *namestr; 240 | jsize slen; 241 | slen = env->GetStringUTFLength(name); 242 | namestr = new char[slen + 1]; 243 | env->GetStringUTFRegion(name, 0, slen, namestr); 244 | namestr[slen] = '\0'; 245 | CapsNativeHandle *cmsg = (CapsNativeHandle *)msg; 246 | vector nreps; 247 | shared_ptr t; 248 | int32_t r = nh->cli->get(namestr, cmsg ? cmsg->caps : t, nreps, timeout); 249 | delete[] namestr; 250 | if (r == FLORA_CLI_SUCCESS) { 251 | gen_java_reply_list(env, replys, nreps); 252 | } 253 | return r; 254 | } 255 | 256 | static void com_rokid_flora_Client_native_set_pointer(JNIEnv *env, jobject thiz, 257 | jlong src, jlong dst) { 258 | CapsNativeHandle *srch = (CapsNativeHandle *)src; 259 | CapsNativeHandle *dsth = (CapsNativeHandle *)dst; 260 | dsth->caps = srch->caps; 261 | } 262 | 263 | static JNINativeMethod _cli_nmethods[] = { 264 | {"native_init", "()V", (void *)com_rokid_flora_Client_native_init}, 265 | {"native_connect", "(Ljava/lang/String;I)I", 266 | (void *)com_rokid_flora_Client_native_connect}, 267 | {"native_destroy", "(J)V", (void *)com_rokid_flora_Client_native_destroy}, 268 | {"native_subscribe", "(JLjava/lang/String;I)I", 269 | (void *)com_rokid_flora_Client_native_subscribe}, 270 | {"native_unsubscribe", "(JLjava/lang/String;I)I", 271 | (void *)com_rokid_flora_Client_native_unsubscribe}, 272 | {"native_post", "(JLjava/lang/String;JI)I", 273 | (void *)com_rokid_flora_Client_native_post}, 274 | {"native_get", "(JLjava/lang/String;JLjava/util/List;I)I", 275 | (void *)com_rokid_flora_Client_native_get}, 276 | {"native_set_pointer", "(JJ)V", 277 | (void *)com_rokid_flora_Client_native_set_pointer}, 278 | }; 279 | 280 | int register_com_rokid_flora_Client(JNIEnv *env) { 281 | const char *kclass = "com/rokid/flora/Client"; 282 | jclass target = env->FindClass(kclass); 283 | if (target == NULL) { 284 | KLOGE("find class for %s failed", kclass); 285 | return -1; 286 | } 287 | return env->RegisterNatives(target, _cli_nmethods, 288 | sizeof(_cli_nmethods) / sizeof(JNINativeMethod)); 289 | } 290 | -------------------------------------------------------------------------------- /docs/c-api.md: -------------------------------------------------------------------------------- 1 | # 1. Agent 2 | 3 | ``` 4 | flora_agent_t agent = flora_agent_create(); 5 | // #exam-agent为可选项,标识此agent身份 6 | flora_agent_config(agent, FLORA_AGENT_CONFIG_URI, "unix:/var/run/flora.sock#exam-agent"); 7 | flora_agent_config(agent, FLORA_AGENT_CONFIG_BUFSIZE, 80 * 1024); 8 | flora_agent_config(agent, FLORA_AGENT_CONFIG_RECONN_INTERVAL, 5000); 9 | 10 | static void foo_sub_callback(const char* name, caps_t msg, uint32_t type, void* arg) { 11 | int32_t iv; 12 | char* str; 13 | caps_read_integer(msg, &iv); // read integer 1 14 | caps_read_string(msg, &str); // read string "hello" 15 | } 16 | static void foo_method_callback(const char* name, caps_t msg, flora_call_reply_t reply, void* arg) { 17 | // use 'flora_call_reply_end(reply)' to send return values to caller 18 | caps_t data = caps_create(); 19 | caps_write_string(data, "world"); 20 | flora_call_reply_write_code(reply, FLORA_CLI_SUCCESS); 21 | flora_call_reply_write_data(reply, data); 22 | flora_call_reply_end(reply); 23 | caps_destroy(data); 24 | } 25 | 26 | // 最后参数(void*)1 为可选项,将传入foo_sub_callback 27 | flora_agent_subscribe(agent, "foo", foo_sub_callback, (void*)1); 28 | flora_agent_declare_method(agent, "foo", foo_method_callback, (void*)2); 29 | 30 | // flora_agent_start(agent, bufsize), bufsize minimal value 32768. 31 | // if 'bufsize' < minimal value, 'bufsize' will set to minimal value. 32 | flora_agent_start(agent, 0); 33 | 34 | // post message 35 | caps_t msg = caps_create(); 36 | caps_write_integer(msg, 1); 37 | caps_write_string(msg, "hello"); 38 | flora_agent_post(agent, "foo", msg, FLORA_MSGTYPE_INSTANT); 39 | 40 | // rpc call 41 | flora_call_result result; 42 | // flora_agent_call(agent, methodName, methodParams, targetName, result, timeout) 43 | // if timeout == 0, use default timeout 44 | if (flora_agent_call(agent, "foo", msg, "exam-agent", &result, 0) == FLORA_CLI_SUCCESS) { 45 | result->ret_code; 46 | result->data; 47 | result->sender; 48 | flora_result_delete(&result); 49 | } 50 | 51 | static void foo_return_callback(int32_t rescode, flora_result_t* result, void* arg) { 52 | if (rescode == FLORA_CLI_SUCCESS) { 53 | // result->ret_code; // ret_code will be 0 54 | // result->sender; // sender will be "exam-agent" 55 | char* str; 56 | caps_read_string(result->data, &str); // str will be "world" 57 | } 58 | } 59 | // flora_agent_call_nb(agent, methodName, methodParams, targetName, callback, arg, timeout) 60 | // 如果超时,foo_return_callback第一个参数rescode值为FLORA_CLI_ETIMEOUT, result参数为空指针 61 | // 最后参数(void*)2 为可选项,将传入foo_return_callback 62 | flora_agent_call_nb(agent, "foo", msg, "exam-agent", foo_return_callback, (void*)2, 0); 63 | caps_destroy(msg); 64 | flora_agent_close(agent); 65 | flora_agent_delete(agent); 66 | ``` 67 | 68 | ## Methods 69 | 70 | ### flora\_agent\_config(agent, key, ...) 71 | 72 | 为Agent对象配置参数 73 | 74 | #### Parameters 75 | 76 | name | type | default | description 77 | --- | --- | --- | --- 78 | agent | flora\_agent\_t | | agent对象 79 | key | uint32_t | | FLORA\_AGENT\_CONFIG\_URI
FLORA\_AGENT\_CONFIG\_BUFSIZE
FLORA\_AGENT\_CONFIG\_RECONN\_INTERVAL 80 | 81 | --- 82 | 83 | ### flora\_agent\_subscribe(agent, name, cb, arg) 84 | 85 | 订阅消息并指定收到消息的回调函数 86 | 87 | #### Parameters 88 | 89 | name | type | default | description 90 | --- | --- | --- | --- 91 | agent | flora\_agent\_t | | 92 | name | const char* | | 消息名称 93 | cb | [flora\_agent\_subscribe\_callback\_t](#SubscribeCallback) | | 回调函数 94 | arg | void* | | 95 | 96 | --- 97 | 98 | ### flora\_agent\_unsubscribe(agent, name) 99 | 100 | 取消订阅 101 | 102 | #### Parameters 103 | 104 | name | type | default | description 105 | --- | --- | --- | --- 106 | agent | flora\_agent\_t | | 107 | name | const char* | | 消息名称 108 | 109 | --- 110 | 111 | ### flora\_agent\_declare\_method(agent, name, cb, arg) 112 | 113 | 声明远程方法 114 | 115 | #### Parameters 116 | 117 | name | type | default | description 118 | --- | --- | --- | --- 119 | agent | flora\_agent\_t | | 120 | name | const char* | | 远程方法名称 121 | cb | [flora\_agent\_declare\_method\_callback\_t](#DeclareMethodCallback) | | 回调函数 122 | arg | void* | | 123 | 124 | --- 125 | 126 | ### flora\_agent\_start(agent, block) 127 | 128 | 启动Agent。需要在config指定uri后调用。可以在subscribe之后或之前调用。 129 | 130 | #### Parameters 131 | 132 | name | type | default | description 133 | --- | --- | --- | --- 134 | agent | flora\_agent\_t | | 135 | block | int32_t | | 阻塞模式开关。如果开启阻塞模式,start不会返回,直至调用close方法。 136 | 137 | --- 138 | 139 | ### flora\_agent\_close(agent) 140 | 141 | 关闭Agent。 142 | 143 | #### Parameters 144 | 145 | name | type | default | description 146 | --- | --- | --- | --- 147 | agent | flora\_agent\_t | | 148 | 149 | --- 150 | 151 | ### flora\_agent\_post(agent, name, msg, type) 152 | 153 | 发送消息 154 | 155 | #### Parameters 156 | 157 | name | type | default | description 158 | --- | --- | --- | --- 159 | agent | flora\_agent\_t | | 160 | name | const char* | | 消息名称 161 | msg | [caps_t](https://github.com/Rokid/aife-mutils/blob/master/caps.md) | | 消息内容 162 | type | uint32_t | FLORA\_MSGTYPE\_INSTANT | 消息类型
FLORA\_MSGTYPE\_INSTANT
FLORA\_MSGTYPE\_PERSIST 163 | 164 | #### returns 165 | 166 | Type: int32_t 167 | 168 | value | description 169 | --- | --- 170 | FLORA\_CLI\_SUCCESS | 成功 171 | FLORA\_CLI\_EINVAL | 参数非法 172 | FLORA\_CLI\_ECONN | flora service连接错误 173 | 174 | --- 175 | 176 | ### flora\_agent\_call(agent, name, msg, target, result, timeout) 177 | 178 | 远程方法调用(同步) 179 | 180 | #### Parameters 181 | 182 | name | type | default | description 183 | --- | --- | --- | --- 184 | agent | flora\_agent\_t | | 185 | name | const char* | | 远程方法名称 186 | msg | [caps_t](https://github.com/Rokid/aife-mutils/blob/master/caps.md) | | 方法参数 187 | target | const char* | | 远程方法声明客户端id 188 | result | [flora\_result\_t](#Result)* | | 远程方法返回信息 189 | timeout | uint32_t | 0 | 等待回复的超时时间,0表示使用默认超时。 190 | 191 | #### returns 192 | 193 | Type: int32_t 194 | 195 | value | description 196 | --- | --- 197 | FLORA\_CLI\_SUCCESS | 成功 198 | FLORA\_CLI\_EINVAL | 参数非法 199 | FLORA\_CLI\_ECONN | flora service连接错误 200 | FLORA\_CLI\_ETIMEOUT | 超时无回复 201 | FLORA\_CLI\_ENEXISTS | 远程方法未找到 202 | FLORA_CLI_EDEADLOCK | 在回调函数中调用此方法,将造成无限阻塞 203 | 204 | --- 205 | 206 | ### flora\_agent\_call\_nb(agent, name, msg, target, cb, arg, timeout) 207 | 208 | 远程方法调用(异步回调) 209 | 210 | #### Parameters 211 | 212 | name | type | default | description 213 | --- | --- | --- | --- 214 | agent | flora\_agent\_t | | 215 | name | const char* | | 远程方法名称 216 | msg | [caps_t](https://github.com/Rokid/aife-mutils/blob/master/caps.md) | | 方法参数 217 | target | const char* | | 远程方法声明客户端id 218 | cb | [flora\_call\_callback\_t](#CallCallback) | | 回调函数 219 | arg | void* | | 220 | timeout | uint32_t | 0 | 等待回复的超时时间,0表示使用默认超时。 221 | 222 | #### returns 223 | 224 | Type: int32_t 225 | 226 | value | description 227 | --- | --- 228 | FLORA_CLI_SUCCESS | 成功 229 | FLORA_CLI_EINVAL | 参数非法 230 | FLORA_CLI_ECONN | flora service连接错误 231 | 232 | --- 233 | 234 | ### flora_call_reply_write_code 235 | 236 | 设置远程函数返回码 237 | 238 | #### Parameters 239 | 240 | name | type | default | description 241 | --- | --- | --- | --- 242 | reply | flora_call_reply_t | | 详见[flora\_agent\_declare\_method\_callback\_t](#DeclareMethodCallback) 243 | code | int32_t | 0 | 244 | 245 | --- 246 | 247 | ### flora_call_reply_write_data 248 | 249 | 设置远程函数返回值 250 | 251 | #### Parameters 252 | 253 | name | type | default | description 254 | --- | --- | --- | --- 255 | reply | flora_call_reply_t | | 详见[flora\_agent\_declare\_method\_callback\_t](#DeclareMethodCallback) 256 | data | [caps_t](https://github.com/Rokid/aife-mutils/blob/master/caps.md) | | 消息内容 257 | 258 | --- 259 | 260 | ### flora_call_reply_end 261 | 262 | 销毁flora_call_reply_t对象并将返回码与返回值发送至flora服务,flora服务将发送给远程函数调用者 263 | 264 | #### Parameters 265 | 266 | name | type | default | description 267 | --- | --- | --- | --- 268 | reply | flora_call_reply_t | | 详见[flora\_agent\_declare\_method\_callback\_t](#DeclareMethodCallback) 269 | 270 | --- 271 | 272 | ## Definition 273 | 274 | ### flora\_agent\_subscribe\_callback\_t(name, msg, type, arg) 275 | 276 | 回调函数:收到订阅的消息 277 | 278 | #### Parameters 279 | 280 | name | type | description 281 | --- | --- | --- 282 | name | string | 消息名称 283 | msg | [caps_t](https://github.com/Rokid/aife-mutils/blob/master/caps.md) | | 消息内容 284 | type | uint32_t | 消息类型
FLORA_MSGTYPE_INSTANT
FLORA_MSGTYPE_PERSIST 285 | arg | void* | subscribe传入的参数arg 286 | 287 | ### flora\_agent\_declare\_method\_callback\_t(name, msg, reply, arg) 288 | 289 | 回调函数:远程方法被调用 290 | 291 | #### Parameters 292 | 293 | name | type | description 294 | --- | --- | --- 295 | name | string | 远程方法名称 296 | msg | [caps_t](https://github.com/Rokid/aife-mutils/blob/master/caps.md) | | 方法参数 297 | reply | [flora\_call_reply\_t](#Reply) | reply对象,通过flora_call_reply_\*系列函数给远程方法调用者返回数据 298 | arg | void* | declare_method传入的参数arg 299 | 300 | ### flora_call\_callback\_t(rescode, result, arg) 301 | 302 | 回调函数:远程方法调用返回值 303 | 304 | #### Parameters 305 | 306 | name | type | description 307 | --- | --- | --- 308 | rescode | int32_t | 远程方法调用错误码 309 | result | [flora\_result\_t](#Result)* | 远程方法调用返回值 310 | arg | void* | call_nb传入的参数arg 311 | 312 | ### flora\_result\_t 313 | 314 | #### Members 315 | 316 | name | type | description 317 | --- | --- | --- 318 | ret_code | int32_t | 返回码,由消息订阅者设置,0为成功。 319 | data | [caps_t](https://github.com/Rokid/aife-mutils/blob/master/caps.md) | 远程方法返回数据 320 | sender | string | 远程方法定义者身份标识 321 | 322 | # 2. Dispatcher 323 | 324 | flora service消息分发模块,需与[Poll](#poll)配合使用 325 | 326 | ## Methods 327 | 328 | ### flora_dispatcher_new(bufsize) 329 | 330 | 创建Dispatcher对象 331 | 332 | #### Parameters 333 | 334 | name | type | default | description 335 | --- | --- | --- | --- 336 | bufsize | uint32_t | 0 | 消息缓冲区大小(决定了一个消息最大大小),最小值32K,小于32K的值会被改为32K。 337 | 338 | #### Returns 339 | 340 | Type: flora_dispatcher_t 341 | 342 | --- 343 | 344 | ### flora_dispatcher_delete(dispatcher) 345 | 346 | 销毁Dispatcher对象 347 | 348 | #### Parameters 349 | 350 | name | type | default | description 351 | --- | --- | --- | --- 352 | dispatcher | flora_dispatcher_t | | 353 | 354 | --- 355 | 356 | ### flora_dispatcher_run(dispatcher, block) 357 | 358 | 开始运行 359 | 360 | #### Parameters 361 | 362 | name | type | default | descriptions 363 | --- | --- | --- | --- 364 | dispatcher | flora_dispatcher_t | | 365 | block | int32_t | | 0: 异步运行模式,此函数立即返回
1: 同步运行模式,此函数阻塞直至flora_dispatcher_close被调用 366 | 367 | --- 368 | 369 | ### flora_dispatcher_close(dispatcher) 370 | 371 | 停止运行 372 | 373 | #### Parameters 374 | 375 | name | type | default | descriptions 376 | --- | --- | --- | --- 377 | dispatcher | flora_dispatcher_t | | 378 | 379 | --- 380 | 381 | # 3. Poll 382 | 383 | flora service连接管理,需与[Dispatcher](#dispatcher)配合使用 384 | 385 | ## Methods 386 | 387 | ### flora_poll_new(uri, result) 388 | 389 | 创建Poll对象 390 | 391 | #### Parameters 392 | 393 | name | type | default | description 394 | --- | --- | --- | --- 395 | uri | const char* | | uri - 服务侦听地址
支持unix domain socket及tcp socket 396 | result | flora_poll_t* | | 创建的Poll对象 397 | 398 | #### Returns 399 | 400 | Type: int32_t 401 | 402 | value | description 403 | --- | --- 404 | FLORA_POLL_SUCCESS | 成功 405 | FLORA_POLL_INVAL | 参数不合法 406 | FLORA_POLL_UNSUPP | uri scheme不支持(目前仅支持unix:及tcp:) 407 | 408 | ### flora_poll_delete(poll) 409 | 410 | #### Parameters 411 | 412 | name | type | default | description 413 | --- | --- | --- | --- 414 | poll | flora_poll_t | | 415 | 416 | ### flora_poll_start(poll, dispatcher) 417 | 418 | 启动Poll,侦听服务地址 419 | 420 | #### Parameters 421 | 422 | name | type | default | description 423 | --- | --- | --- | --- 424 | poll | flora_poll_t | | 425 | dispatcher | flora_dispatcher_t | | 426 | 427 | #### Returns 428 | 429 | Type: int32_t 430 | 431 | value | description 432 | --- | --- 433 | FLORA_POLL_SUCCESS | 成功 434 | FLORA_POLL_INVAL | 参数不合法,poll或dispatcher对象句柄为0 435 | FLORA_POLL_SYSERR | socket或其它系统调用错误 436 | 437 | ### flora_poll_stop(poll) 438 | 439 | 停止侦听并关闭所有连接 440 | 441 | #### Parameters 442 | 443 | name | type | default | description 444 | --- | --- | --- | --- 445 | poll | flora_poll_t | | 446 | -------------------------------------------------------------------------------- /include/flora-cli.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "caps.h" 4 | 5 | #define FLORA_CLI_SUCCESS 0 6 | // 认证过程失败(版本号不支持) 7 | #define FLORA_CLI_EAUTH -1 8 | // 参数不合法 9 | #define FLORA_CLI_EINVAL -2 10 | // 连接错误 11 | #define FLORA_CLI_ECONN -3 12 | // 'get'请求超时 13 | #define FLORA_CLI_ETIMEOUT -4 14 | // 'get'请求目标不存在 15 | #define FLORA_CLI_ENEXISTS -5 16 | // 客户端id已被占用 17 | // uri '#' 字符后面的字符串是客户端id 18 | #define FLORA_CLI_EDUPID -6 19 | // 缓冲区大小不足 20 | #define FLORA_CLI_EINSUFF_BUF -7 21 | // 客户端主动关闭 22 | #define FLORA_CLI_ECLOSED -8 23 | // 在回调线程中调用call(阻塞模式) 24 | #define FLORA_CLI_EDEADLOCK -9 25 | // monitor模式下,不可调用subscribe/declare_method/post/call 26 | #define FLORA_CLI_EMONITOR -10 27 | 28 | #define FLORA_CALL_RETCODE_NORESP -2000000000 29 | 30 | #define FLORA_MSGTYPE_INSTANT 0 31 | #define FLORA_MSGTYPE_PERSIST 1 32 | #define FLORA_NUMBER_OF_MSGTYPE 2 33 | 34 | #ifdef __cplusplus 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #define FLORA_CLI_FLAG_MONITOR 0x1 41 | #define FLORA_CLI_FLAG_MONITOR_DETAIL_SUB 0x2 42 | #define FLORA_CLI_FLAG_MONITOR_DETAIL_DECL 0x4 43 | #define FLORA_CLI_FLAG_MONITOR_DETAIL_POST 0x8 44 | #define FLORA_CLI_FLAG_MONITOR_DETAIL_CALL 0x10 45 | #define FLORA_CLI_FLAG_KEEPALIVE 0x20 46 | 47 | #define FLORA_CLI_DEFAULT_BEEP_INTERVAL 50000 48 | #define FLORA_CLI_DEFAULT_NORESP_TIMEOUT 100000 49 | 50 | namespace flora { 51 | 52 | typedef struct { 53 | int32_t ret_code; 54 | std::shared_ptr data; 55 | std::string extra; 56 | } Response; 57 | 58 | // NOTE: the Reply class methods not threadsafe 59 | class Reply { 60 | public: 61 | virtual ~Reply() = default; 62 | 63 | virtual void write_code(int32_t code) = 0; 64 | 65 | virtual void write_data(std::shared_ptr &data) = 0; 66 | 67 | virtual void end() = 0; 68 | 69 | virtual void end(int32_t code) = 0; 70 | 71 | virtual void end(int32_t code, std::shared_ptr &data) = 0; 72 | }; 73 | 74 | class ClientCallback; 75 | class MonitorCallback; 76 | 77 | class ClientOptions { 78 | public: 79 | uint32_t bufsize = 0; 80 | uint32_t flags = 0; 81 | // effective when FLORA_CLI_FLAG_KEEPALIVE is set 82 | uint32_t beep_interval = FLORA_CLI_DEFAULT_BEEP_INTERVAL; 83 | uint32_t noresp_timeout = FLORA_CLI_DEFAULT_NORESP_TIMEOUT; 84 | }; 85 | 86 | class Client { 87 | public: 88 | virtual ~Client() = default; 89 | 90 | virtual int32_t subscribe(const char *name) = 0; 91 | 92 | virtual int32_t unsubscribe(const char *name) = 0; 93 | 94 | virtual int32_t declare_method(const char *name) = 0; 95 | 96 | virtual int32_t remove_method(const char *name) = 0; 97 | 98 | virtual int32_t post(const char *name, std::shared_ptr &msg, 99 | uint32_t msgtype) = 0; 100 | 101 | virtual int32_t call(const char *name, std::shared_ptr &msg, 102 | const char *target, Response &reply, 103 | uint32_t timeout = 0) = 0; 104 | 105 | virtual int32_t call(const char *name, std::shared_ptr &msg, 106 | const char *target, 107 | std::function &&cb, 108 | uint32_t timeout = 0) = 0; 109 | 110 | virtual int32_t call(const char *name, std::shared_ptr &msg, 111 | const char *target, 112 | std::function &cb, 113 | uint32_t timeout = 0) = 0; 114 | 115 | virtual int get_socket() const = 0; 116 | 117 | static int32_t connect(const char *uri, ClientCallback *cb, 118 | uint32_t msg_buf_size, 119 | std::shared_ptr &result); 120 | 121 | static int32_t connect(const char *uri, ClientCallback *ccb, 122 | MonitorCallback *mcb, ClientOptions *opts, 123 | std::shared_ptr &result); 124 | }; 125 | 126 | class ClientCallback { 127 | public: 128 | virtual ~ClientCallback() = default; 129 | 130 | virtual void recv_post(const char *name, uint32_t msgtype, 131 | std::shared_ptr &msg) {} 132 | 133 | virtual void recv_call(const char *name, std::shared_ptr &msg, 134 | std::shared_ptr &reply) {} 135 | 136 | virtual void disconnected() {} 137 | }; 138 | 139 | class MonitorListItem { 140 | public: 141 | uint32_t id; 142 | int32_t pid; 143 | std::string name; 144 | uint32_t flags; 145 | uint64_t tag; 146 | }; 147 | 148 | class MonitorSubscriptionItem { 149 | public: 150 | std::string name; 151 | // id of MonitorListItem 152 | uint32_t id; 153 | }; 154 | 155 | class MonitorPostInfo { 156 | public: 157 | std::string name; 158 | // id of MonitorListItem, sender 159 | uint32_t from; 160 | }; 161 | 162 | class MonitorCallInfo { 163 | public: 164 | std::string name; 165 | // id of MonitorListItem, sender 166 | uint32_t from; 167 | std::string target; 168 | int32_t err = FLORA_CLI_SUCCESS; 169 | }; 170 | 171 | typedef MonitorSubscriptionItem MonitorDeclarationItem; 172 | 173 | class MonitorCallback { 174 | public: 175 | virtual ~MonitorCallback() = default; 176 | 177 | virtual void list_all(std::vector &items) {} 178 | 179 | virtual void list_add(MonitorListItem &item) {} 180 | 181 | virtual void list_remove(uint32_t id) {} 182 | 183 | virtual void sub_all(std::vector &items) {} 184 | 185 | virtual void sub_add(MonitorSubscriptionItem &item) {} 186 | 187 | virtual void sub_remove(MonitorSubscriptionItem &item) {} 188 | 189 | virtual void decl_all(std::vector &items) {} 190 | 191 | virtual void decl_add(MonitorDeclarationItem &item) {} 192 | 193 | virtual void decl_remove(MonitorDeclarationItem &item) {} 194 | 195 | virtual void post(MonitorPostInfo &info) {} 196 | 197 | virtual void call(MonitorCallInfo &info) {} 198 | 199 | virtual void disconnected() {} 200 | }; 201 | 202 | class MsgSender { 203 | public: 204 | // return: 0 unix domain socket connection 205 | // 1 tcp socket connection 206 | static uint32_t connection_type(); 207 | 208 | // pid of msg sender, if connection type is unix domain socket connection 209 | static pid_t pid(); 210 | 211 | // identify of msg sender 212 | static uint64_t tag(); 213 | 214 | // ipv4 addr string, if connection type is tcp socket connection 215 | static const char* ipaddr(); 216 | 217 | // ipv4 port, if connection type is tcp socket connection 218 | static uint16_t port(); 219 | 220 | static const char* name(); 221 | 222 | // pid string, if connection type is unix domain socket connection 223 | // ipaddr:port, if connection type is tcp socket connection 224 | static void to_string(std::string& str); 225 | }; 226 | 227 | } // namespace flora 228 | 229 | extern "C" { 230 | #endif 231 | 232 | typedef intptr_t flora_cli_t; 233 | typedef void (*flora_cli_disconnected_func_t)(void *arg); 234 | // msgtype: INSTANT | PERSIST 235 | // 'msg': 注意,必须在函数未返回时读取msg,函数返回后读取msg非法 236 | typedef void (*flora_cli_recv_post_func_t)(const char *name, uint32_t msgtype, 237 | caps_t msg, void *arg); 238 | typedef intptr_t flora_call_reply_t; 239 | 240 | // 'call'请求的接收端 241 | // 在此函数中填充'reply'变量,客户端将把'reply'数据发回给'call'请求发送端 242 | typedef void (*flora_cli_recv_call_func_t)(const char *name, caps_t msg, 243 | void *arg, flora_call_reply_t reply); 244 | typedef struct { 245 | flora_cli_recv_post_func_t recv_post; 246 | flora_cli_recv_call_func_t recv_call; 247 | flora_cli_disconnected_func_t disconnected; 248 | } flora_cli_callback_t; 249 | typedef struct { 250 | // FLORA_CLI_SUCCESS: 成功 251 | // 其它: 此请求服务端自定义错误码 252 | int32_t ret_code; 253 | caps_t data; 254 | // 回应请求的服务端自定义标识 255 | // 可能为空字串 256 | char *extra; 257 | } flora_call_result; 258 | typedef void (*flora_call_callback_t)(int32_t rescode, 259 | flora_call_result *result, void *arg); 260 | 261 | typedef struct { 262 | uint64_t tag; 263 | const char *name; 264 | uint32_t id; 265 | } flora_cli_monitor_list_item; 266 | typedef struct { 267 | const char *name; 268 | // id of flora_cli_monitor_list_item 269 | uint32_t id; 270 | } flora_cli_monitor_sub_item; 271 | typedef flora_cli_monitor_sub_item flora_cli_monitor_decl_item; 272 | typedef struct { 273 | const char *name; 274 | // id of flora_cli_monitor_list_item, sender 275 | uint32_t from; 276 | } flora_cli_monitor_post_info; 277 | typedef struct { 278 | const char *name; 279 | // id of flora_cli_monitor_list_item, sender 280 | uint32_t from; 281 | const char *target; 282 | } flora_cli_monitor_call_info; 283 | typedef void (*flora_cli_monitor_list_all_func_t)( 284 | flora_cli_monitor_list_item *items, uint32_t size); 285 | typedef void (*flora_cli_monitor_list_add_func_t)( 286 | flora_cli_monitor_list_item *item); 287 | typedef void (*flora_cli_monitor_list_remove_func_t)(uint32_t id); 288 | typedef void (*flora_cli_monitor_sub_all_func_t)( 289 | flora_cli_monitor_sub_item *items, uint32_t size); 290 | typedef void (*flora_cli_monitor_sub_add_func_t)( 291 | flora_cli_monitor_sub_item *item); 292 | typedef void (*flora_cli_monitor_sub_remove_func_t)( 293 | flora_cli_monitor_sub_item *item); 294 | typedef void (*flora_cli_monitor_decl_all_func_t)( 295 | flora_cli_monitor_decl_item *items, uint32_t size); 296 | typedef void (*flora_cli_monitor_decl_add_func_t)( 297 | flora_cli_monitor_decl_item *item); 298 | typedef void (*flora_cli_monitor_decl_remove_func_t)( 299 | flora_cli_monitor_decl_item *item); 300 | typedef void (*flora_cli_monitor_post_func_t)( 301 | flora_cli_monitor_post_info *info); 302 | typedef void (*flora_cli_monitor_call_func_t)( 303 | flora_cli_monitor_call_info *info); 304 | typedef struct { 305 | flora_cli_monitor_list_all_func_t list_all; 306 | flora_cli_monitor_list_add_func_t list_add; 307 | flora_cli_monitor_list_remove_func_t list_remove; 308 | flora_cli_monitor_sub_all_func_t sub_all; 309 | flora_cli_monitor_sub_add_func_t sub_add; 310 | flora_cli_monitor_sub_remove_func_t sub_remove; 311 | flora_cli_monitor_decl_all_func_t decl_all; 312 | flora_cli_monitor_decl_add_func_t decl_add; 313 | flora_cli_monitor_decl_remove_func_t decl_remove; 314 | flora_cli_monitor_post_func_t post; 315 | flora_cli_monitor_call_func_t call; 316 | flora_cli_disconnected_func_t disconnected; 317 | } flora_cli_monitor_callback_t; 318 | 319 | // uri: 支持的schema: 320 | // tcp://$(host):$(port)/<#extra> 321 | // unix:$(domain_name)<#extra> (unix domain socket) 322 | // #extra为客户端自定义字符串,用于粗略标识自身身份,不保证全局唯一性 323 | // async: 异步模式开关 324 | // result: 返回flora_cli_t对象 325 | int32_t flora_cli_connect(const char *uri, flora_cli_callback_t *callback, 326 | void *arg, uint32_t msg_buf_size, 327 | flora_cli_t *result); 328 | 329 | int32_t flora_cli_connect2(const char *uri, 330 | flora_cli_monitor_callback_t *callback, void *arg, 331 | uint32_t msg_buf_size, flora_cli_t *result); 332 | 333 | typedef struct { 334 | uint32_t bufsize; 335 | uint32_t flags; 336 | uint32_t beep_interval; 337 | uint32_t noresp_timeout; 338 | } flora_cli_options; 339 | int32_t flora_cli_connect3(const char *uri, flora_cli_callback_t *ccb, 340 | flora_cli_monitor_callback_t *mcb, void *arg, 341 | flora_cli_options *opts, flora_cli_t *result); 342 | 343 | void flora_cli_delete(flora_cli_t handle); 344 | 345 | int32_t flora_cli_subscribe(flora_cli_t handle, const char *name); 346 | 347 | int32_t flora_cli_unsubscribe(flora_cli_t handle, const char *name); 348 | 349 | int32_t flora_cli_declare_method(flora_cli_t handle, const char *name); 350 | 351 | int32_t flora_cli_remove_method(flora_cli_t handle, const char *name); 352 | 353 | // msgtype: INSTANT | PERSIST 354 | int32_t flora_cli_post(flora_cli_t handle, const char *name, caps_t msg, 355 | uint32_t msgtype); 356 | 357 | int32_t flora_cli_call(flora_cli_t handle, const char *name, caps_t msg, 358 | const char *target, flora_call_result *result, 359 | uint32_t timeout); 360 | 361 | int32_t flora_cli_call_nb(flora_cli_t handle, const char *name, caps_t msg, 362 | const char *target, flora_call_callback_t cb, 363 | void *arg, uint32_t timeout); 364 | 365 | void flora_result_delete(flora_call_result *result); 366 | 367 | void flora_call_reply_write_code(flora_call_reply_t reply, int32_t code); 368 | 369 | void flora_call_reply_write_data(flora_call_reply_t reply, caps_t data); 370 | 371 | void flora_call_reply_end(flora_call_reply_t reply); 372 | 373 | #ifdef __cplusplus 374 | } // namespace flora 375 | #endif 376 | -------------------------------------------------------------------------------- /src/flora-agent.cc: -------------------------------------------------------------------------------- 1 | #include "flora-agent.h" 2 | #include "cli.h" 3 | #include "rlog.h" 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | using namespace std::chrono; 9 | 10 | namespace flora { 11 | 12 | Agent::~Agent() { close(); } 13 | 14 | void Agent::config(uint32_t key, ...) { 15 | va_list ap; 16 | va_start(ap, key); 17 | config(key, ap); 18 | va_end(ap); 19 | } 20 | 21 | void Agent::config(uint32_t key, va_list ap) { 22 | switch (key) { 23 | case FLORA_AGENT_CONFIG_URI: 24 | options.uri = va_arg(ap, const char *); 25 | break; 26 | case FLORA_AGENT_CONFIG_BUFSIZE: 27 | options.bufsize = va_arg(ap, uint32_t); 28 | break; 29 | case FLORA_AGENT_CONFIG_RECONN_INTERVAL: 30 | options.reconn_interval = milliseconds(va_arg(ap, uint32_t)); 31 | break; 32 | case FLORA_AGENT_CONFIG_MONITOR: 33 | options.flags = va_arg(ap, uint32_t); 34 | if (options.flags & FLORA_CLI_FLAG_MONITOR) { 35 | options.mon_callback = va_arg(ap, MonitorCallback *); 36 | } 37 | break; 38 | case FLORA_AGENT_CONFIG_KEEPALIVE: 39 | options.beep_interval = va_arg(ap, uint32_t); 40 | options.noresp_timeout = va_arg(ap, uint32_t); 41 | break; 42 | } 43 | } 44 | 45 | void Agent::subscribe(const char *name, PostHandler &&cb) { 46 | subscribe(name, cb); 47 | } 48 | 49 | void Agent::subscribe(const char *name, PostHandler &cb) { 50 | shared_ptr cli; 51 | conn_mutex.lock(); 52 | auto r = post_handlers.insert(make_pair(name, cb)); 53 | cli = flora_cli; 54 | conn_mutex.unlock(); 55 | if (r.second && cli.get()) 56 | cli->subscribe(name); 57 | } 58 | 59 | void Agent::unsubscribe(const char *name) { 60 | shared_ptr cli; 61 | PostHandlerMap::iterator it; 62 | 63 | conn_mutex.lock(); 64 | it = post_handlers.find(name); 65 | if (it != post_handlers.end()) { 66 | post_handlers.erase(it); 67 | cli = flora_cli; 68 | } 69 | conn_mutex.unlock(); 70 | if (cli.get()) 71 | cli->unsubscribe(name); 72 | } 73 | 74 | void Agent::declare_method(const char *name, CallHandler &&cb) { 75 | declare_method(name, cb); 76 | } 77 | 78 | void Agent::declare_method(const char *name, CallHandler &cb) { 79 | shared_ptr cli; 80 | conn_mutex.lock(); 81 | auto r = call_handlers.insert(make_pair(name, cb)); 82 | cli = flora_cli; 83 | conn_mutex.unlock(); 84 | if (r.second && cli.get()) 85 | cli->declare_method(name); 86 | } 87 | 88 | void Agent::remove_method(const char *name) { 89 | shared_ptr cli; 90 | CallHandlerMap::iterator it; 91 | 92 | conn_mutex.lock(); 93 | it = call_handlers.find(name); 94 | if (it != call_handlers.end()) { 95 | call_handlers.erase(it); 96 | cli = flora_cli; 97 | } 98 | conn_mutex.unlock(); 99 | if (cli.get()) 100 | cli->remove_method(name); 101 | } 102 | 103 | void Agent::start(bool block) { 104 | if (block) { 105 | working = true; 106 | run(); 107 | } else { 108 | unique_lock locker(conn_mutex); 109 | run_thread = thread([this]() { 110 | working = true; 111 | this->run(); 112 | }); 113 | // wait run thread execute and connect service 114 | start_cond.wait(locker); 115 | } 116 | } 117 | 118 | void Agent::clean_gabages(list >& gabages) { 119 | lock_guard locker(cg_mutex); 120 | auto it = gabages.begin(); 121 | while (it != gabages.end()) { 122 | auto cli = *it; 123 | if (cli) { 124 | static_pointer_cast(cli)->close(false); 125 | if (cli.use_count() == 1) { 126 | cli.reset(); 127 | it = gabages.erase(it); 128 | } else 129 | ++it; 130 | } else { 131 | ++it; 132 | } 133 | } 134 | } 135 | 136 | void Agent::run() { 137 | unique_lock locker(conn_mutex, defer_lock); 138 | shared_ptr cli; 139 | list > gabages; 140 | flora::ClientOptions cliopts; 141 | 142 | cliopts.bufsize = options.bufsize; 143 | cliopts.flags = options.flags; 144 | cliopts.beep_interval = options.beep_interval; 145 | cliopts.noresp_timeout = options.noresp_timeout; 146 | while (working) { 147 | int32_t r = Client::connect(options.uri.c_str(), this, options.mon_callback, 148 | &cliopts, cli); 149 | if (r != FLORA_CLI_SUCCESS) { 150 | KLOGI(TAG, 151 | "connect to flora service %s failed, retry after %u milliseconds", 152 | options.uri.c_str(), options.reconn_interval.count()); 153 | locker.lock(); 154 | start_cond.notify_one(); 155 | conn_cond.wait_for(locker, options.reconn_interval); 156 | } else { 157 | KLOGI(TAG, "flora service %s connected", options.uri.c_str()); 158 | init_cli(cli); 159 | locker.lock(); 160 | gabages.push_back(cli); 161 | flora_cli.swap(cli); 162 | start_cond.notify_one(); 163 | conn_cond.wait(locker); 164 | flora_cli.reset(); 165 | } 166 | locker.unlock(); 167 | clean_gabages(gabages); 168 | } 169 | } 170 | 171 | void Agent::init_cli(shared_ptr &cli) { 172 | PostHandlerMap::iterator pit; 173 | CallHandlerMap::iterator cit; 174 | 175 | for (pit = post_handlers.begin(); pit != post_handlers.end(); ++pit) { 176 | cli->subscribe((*pit).first.c_str()); 177 | } 178 | for (cit = call_handlers.begin(); cit != call_handlers.end(); ++cit) { 179 | cli->declare_method((*cit).first.c_str()); 180 | } 181 | } 182 | 183 | void Agent::close() { 184 | unique_lock locker(conn_mutex); 185 | if (working) { 186 | working = false; 187 | conn_cond.notify_one(); 188 | shared_ptr cli = 189 | static_pointer_cast(flora_cli); 190 | flora_cli.reset(); 191 | post_handlers.clear(); 192 | call_handlers.clear(); 193 | locker.unlock(); 194 | cg_mutex.lock(); 195 | if (cli != nullptr && cli->close(false) == FLORA_CLI_EDEADLOCK) { 196 | thread tmp([cli]() { cli->close(false); }); 197 | tmp.detach(); 198 | } 199 | cg_mutex.unlock(); 200 | if (run_thread.joinable()) 201 | run_thread.join(); 202 | } 203 | } 204 | 205 | int32_t Agent::post(const char *name, shared_ptr &msg, uint32_t msgtype) { 206 | shared_ptr cli; 207 | 208 | conn_mutex.lock(); 209 | cli = flora_cli; 210 | conn_mutex.unlock(); 211 | 212 | if (cli.get() == nullptr) { 213 | return FLORA_CLI_ECONN; 214 | } 215 | int32_t r = cli->post(name, msg, msgtype); 216 | if (r == FLORA_CLI_ECONN) { 217 | destroy_client(); 218 | } 219 | return r; 220 | } 221 | 222 | int32_t Agent::call(const char *name, shared_ptr &msg, const char *target, 223 | Response &response, uint32_t timeout) { 224 | shared_ptr cli; 225 | 226 | conn_mutex.lock(); 227 | cli = flora_cli; 228 | conn_mutex.unlock(); 229 | 230 | if (cli.get() == nullptr) { 231 | return FLORA_CLI_ECONN; 232 | } 233 | int32_t r = cli->call(name, msg, target, response, timeout); 234 | if (r == FLORA_CLI_ECONN) { 235 | destroy_client(); 236 | } 237 | return r; 238 | } 239 | 240 | int32_t Agent::call(const char *name, shared_ptr &msg, const char *target, 241 | function &&cb, 242 | uint32_t timeout) { 243 | return call(name, msg, target, cb, timeout); 244 | } 245 | 246 | int32_t Agent::call(const char *name, shared_ptr &msg, const char *target, 247 | function &cb, uint32_t timeout) { 248 | shared_ptr cli; 249 | 250 | conn_mutex.lock(); 251 | cli = flora_cli; 252 | conn_mutex.unlock(); 253 | 254 | if (cli.get() == nullptr) { 255 | return FLORA_CLI_ECONN; 256 | } 257 | int32_t r = cli->call(name, msg, target, cb, timeout); 258 | if (r == FLORA_CLI_ECONN) { 259 | destroy_client(); 260 | } 261 | return r; 262 | } 263 | 264 | void Agent::destroy_client() { 265 | conn_mutex.lock(); 266 | conn_cond.notify_one(); 267 | conn_mutex.unlock(); 268 | } 269 | 270 | void Agent::recv_post(const char *name, uint32_t msgtype, 271 | shared_ptr &msg) { 272 | unique_lock locker(conn_mutex); 273 | PostHandlerMap::iterator it = post_handlers.find(name); 274 | if (it != post_handlers.end()) { 275 | auto cb = it->second; 276 | locker.unlock(); 277 | cb(name, msg, msgtype); 278 | } 279 | } 280 | 281 | void Agent::recv_call(const char *name, shared_ptr &msg, 282 | shared_ptr &reply) { 283 | unique_lock locker(conn_mutex); 284 | CallHandlerMap::iterator it = call_handlers.find(name); 285 | if (it != call_handlers.end()) { 286 | auto cb = it->second; 287 | locker.unlock(); 288 | cb(name, msg, reply); 289 | } 290 | } 291 | 292 | void Agent::disconnected() { destroy_client(); } 293 | 294 | int Agent::get_socket() const { 295 | auto cli = flora_cli; 296 | if (cli) 297 | return cli->get_socket(); 298 | return -1; 299 | } 300 | 301 | } // namespace flora 302 | 303 | using namespace flora; 304 | flora_agent_t flora_agent_create() { 305 | Agent *agent = new Agent(); 306 | return reinterpret_cast(agent); 307 | } 308 | 309 | void flora_agent_config(flora_agent_t agent, uint32_t key, ...) { 310 | Agent *cxxagent = reinterpret_cast(agent); 311 | va_list ap; 312 | va_start(ap, key); 313 | cxxagent->config(key, ap); 314 | va_end(ap); 315 | } 316 | 317 | void flora_agent_subscribe(flora_agent_t agent, const char *name, 318 | flora_agent_subscribe_callback_t cb, void *arg) { 319 | Agent *cxxagent = reinterpret_cast(agent); 320 | cxxagent->subscribe( 321 | name, [cb, arg](const char *name, shared_ptr &msg, uint32_t type) { 322 | caps_t cmsg = Caps::convert(msg); 323 | cb(name, cmsg, type, arg); 324 | caps_destroy(cmsg); 325 | }); 326 | } 327 | 328 | void flora_agent_unsubscribe(flora_agent_t agent, const char *name) { 329 | Agent *cxxagent = reinterpret_cast(agent); 330 | cxxagent->unsubscribe(name); 331 | } 332 | 333 | void flora_agent_declare_method(flora_agent_t agent, const char *name, 334 | flora_agent_declare_method_callback_t cb, 335 | void *arg) { 336 | Agent *cxxagent = reinterpret_cast(agent); 337 | cxxagent->declare_method(name, [cb, arg](const char *name, 338 | shared_ptr &msg, 339 | shared_ptr &reply) { 340 | caps_t cmsg = Caps::convert(msg); 341 | CReply *creply = new CReply(); 342 | creply->cxxreply = reply; 343 | cb(name, cmsg, reinterpret_cast(creply), arg); 344 | }); 345 | } 346 | 347 | void flora_agent_start(flora_agent_t agent, int32_t block) { 348 | Agent *cxxagent = reinterpret_cast(agent); 349 | cxxagent->start(block); 350 | } 351 | 352 | void flora_agent_close(flora_agent_t agent) { 353 | Agent *cxxagent = reinterpret_cast(agent); 354 | cxxagent->close(); 355 | } 356 | 357 | int32_t flora_agent_post(flora_agent_t agent, const char *name, caps_t msg, 358 | uint32_t msgtype) { 359 | Agent *cxxagent = reinterpret_cast(agent); 360 | shared_ptr cxxmsg = Caps::convert(msg); 361 | return cxxagent->post(name, cxxmsg, msgtype); 362 | } 363 | 364 | void cxxresp_to_cresp(Response &resp, flora_call_result &result); 365 | int32_t flora_agent_call(flora_agent_t agent, const char *name, caps_t msg, 366 | const char *target, flora_call_result *result, 367 | uint32_t timeout) { 368 | Agent *cxxagent = reinterpret_cast(agent); 369 | shared_ptr cxxmsg = Caps::convert(msg); 370 | Response resp; 371 | int32_t r = cxxagent->call(name, cxxmsg, target, resp, timeout); 372 | if (r != FLORA_CLI_SUCCESS) 373 | return r; 374 | if (result) 375 | cxxresp_to_cresp(resp, *result); 376 | return FLORA_CLI_SUCCESS; 377 | } 378 | 379 | int32_t flora_agent_call_nb(flora_agent_t agent, const char *name, caps_t msg, 380 | const char *target, flora_call_callback_t cb, 381 | void *arg, uint32_t timeout) { 382 | Agent *cxxagent = reinterpret_cast(agent); 383 | shared_ptr cxxmsg = Caps::convert(msg); 384 | return cxxagent->call(name, cxxmsg, target, 385 | [cb, arg](int32_t rescode, Response &resp) { 386 | flora_call_result result; 387 | if (rescode == FLORA_CLI_SUCCESS) { 388 | cxxresp_to_cresp(resp, result); 389 | cb(rescode, &result, arg); 390 | } else { 391 | cb(rescode, nullptr, arg); 392 | } 393 | }, 394 | timeout); 395 | } 396 | 397 | void flora_agent_delete(flora_agent_t agent) { 398 | delete reinterpret_cast(agent); 399 | } 400 | -------------------------------------------------------------------------------- /examples/all-demos.cc: -------------------------------------------------------------------------------- 1 | #include "all-demos.h" 2 | #include "rlog.h" 3 | #include 4 | 5 | #define SERVICE_URI "unix:flora.example.sock" 6 | // #define SERVICE_URI "tcp://0.0.0.0:37702/" 7 | 8 | using namespace std; 9 | using namespace flora; 10 | 11 | void DemoAllInOne::run() { 12 | run_start_stop_service(); 13 | run_post_msg(); 14 | run_recv_msg(); 15 | test_errors(); 16 | test_reply_after_close(); 17 | test_invoke_in_callback(); 18 | } 19 | 20 | void DemoAllInOne::start_service() { 21 | poll = Poll::new_instance(SERVICE_URI); 22 | dispatcher = Dispatcher::new_instance(); 23 | poll->start(dispatcher); 24 | dispatcher->run(); 25 | } 26 | 27 | void DemoAllInOne::stop_service() { 28 | dispatcher->close(); 29 | poll->stop(); 30 | poll.reset(); 31 | dispatcher.reset(); 32 | } 33 | 34 | void DemoAllInOne::run_start_stop_service() { 35 | KLOGI(TAG, "============run start stop service============"); 36 | start_service(); 37 | stop_service(); 38 | } 39 | 40 | static void config_agent(Agent &agent, char id) { 41 | char buf[64]; 42 | snprintf(buf, sizeof(buf), "%s#%c", SERVICE_URI, id); 43 | agent.config(FLORA_AGENT_CONFIG_URI, buf); 44 | agent.start(); 45 | } 46 | 47 | static void create_cagent(flora_agent_t *agent, char id) { 48 | char buf[64]; 49 | snprintf(buf, sizeof(buf), "%s#c-%c", SERVICE_URI, id); 50 | *agent = flora_agent_create(); 51 | flora_agent_config(*agent, FLORA_AGENT_CONFIG_URI, buf); 52 | flora_agent_start(*agent, 0); 53 | } 54 | 55 | static void delete_cagent(flora_agent_t agent) { 56 | flora_agent_close(agent); 57 | flora_agent_delete(agent); 58 | } 59 | 60 | static void print_response(const char *prefix, Response &resp) { 61 | int32_t iv = -1; 62 | resp.data->read(iv); 63 | KLOGI(TAG, "%s: response retcode %d, value %d, from %s", prefix, 64 | resp.ret_code, iv, resp.extra.c_str()); 65 | } 66 | 67 | static void print_c_response(const char *prefix, flora_call_result &resp) { 68 | int32_t iv = -1; 69 | caps_read_integer(resp.data, &iv); 70 | KLOGI(TAG, "%s: c response retcode %d, value %d, from %s", prefix, 71 | resp.ret_code, iv, resp.extra); 72 | } 73 | 74 | static void exam_call_callback(int32_t rescode, flora_call_result *result, 75 | void *arg) { 76 | if (rescode == FLORA_CLI_SUCCESS) { 77 | print_c_response("c non-blocking call", *result); 78 | flora_result_delete(result); 79 | } else { 80 | KLOGI(TAG, "c non-blocking call failed: %d", rescode); 81 | } 82 | } 83 | 84 | static void agent_post(Agent &agent, flora_agent_t cagent) { 85 | shared_ptr msg; 86 | agent.post("0", msg, FLORA_MSGTYPE_INSTANT); 87 | msg = Caps::new_instance(); 88 | msg->write(1); 89 | agent.post("1", msg, FLORA_MSGTYPE_PERSIST); 90 | 91 | flora_agent_post(cagent, "0", 0, FLORA_MSGTYPE_INSTANT); 92 | caps_t cmsg = caps_create(); 93 | caps_write_integer(cmsg, 1); 94 | flora_agent_post(cagent, "1", cmsg, FLORA_MSGTYPE_INSTANT); 95 | caps_destroy(cmsg); 96 | } 97 | 98 | static void agent_call(Agent &agent, flora_agent_t cagent, const char *target) { 99 | shared_ptr msg = Caps::new_instance(); 100 | Response resp; 101 | msg->write(101); 102 | int32_t r = agent.call("2", msg, target, resp); 103 | if (r != FLORA_CLI_SUCCESS) { 104 | KLOGI(TAG, "agent call 2 failed: %d", r); 105 | } else { 106 | print_response("blocking call", resp); 107 | } 108 | r = agent.call("3", msg, target, 109 | [](int32_t rcode, Response &resp) { 110 | if (rcode == FLORA_CLI_SUCCESS) { 111 | print_response("non-blocking call", resp); 112 | } else { 113 | KLOGI(TAG, "non-blocking call 3 failed: %d", rcode); 114 | } 115 | }, 116 | 0); 117 | if (r != FLORA_CLI_SUCCESS) { 118 | KLOGI(TAG, "agent call 3 failed: %d", r); 119 | } 120 | 121 | caps_t cmsg = 0; 122 | flora_call_result cresp; 123 | cmsg = caps_create(); 124 | caps_write_integer(cmsg, 110); 125 | r = flora_agent_call(cagent, "2", cmsg, target, &cresp, 0); 126 | if (r != FLORA_CLI_SUCCESS) { 127 | KLOGI(TAG, "c agent call 2 failed: %d", r); 128 | } else { 129 | print_c_response("c blocking call", cresp); 130 | } 131 | r = flora_agent_call_nb(cagent, "3", cmsg, target, exam_call_callback, 132 | nullptr, 0); 133 | if (r != FLORA_CLI_SUCCESS) { 134 | KLOGI(TAG, "c agent call 3 failed: %d", r); 135 | } 136 | } 137 | 138 | void DemoAllInOne::run_post_msg() { 139 | KLOGI(TAG, "============run post msg============"); 140 | start_service(); 141 | config_agent(agents[0], 'a'); 142 | create_cagent(cagents, 'a'); 143 | // wait agent connected 144 | sleep(1); 145 | agent_post(agents[0], cagents[0]); 146 | agent_call(agents[0], cagents[0], "a"); 147 | // wait recv responses 148 | sleep(1); 149 | delete_cagent(cagents[0]); 150 | agents[0].close(); 151 | stop_service(); 152 | } 153 | 154 | static void exam_subscribe_callback(const char *name, caps_t msg, uint32_t type, 155 | void *arg) { 156 | int32_t iv = -1; 157 | switch ((intptr_t)arg) { 158 | case 0: 159 | KLOGI(TAG, "0: recv msg %s, type %u, content %p", name, type, msg); 160 | break; 161 | case 1: 162 | caps_read_integer(msg, &iv); 163 | KLOGI(TAG, "1: recv msg %s, type %u, content %d", name, type, iv); 164 | break; 165 | } 166 | } 167 | 168 | static void exam_declare_method_callback(const char *name, caps_t msg, 169 | flora_call_reply_t reply, void *arg) { 170 | int32_t iv = -1; 171 | caps_t data; 172 | switch ((intptr_t)arg) { 173 | case 2: 174 | caps_read_integer(msg, &iv); 175 | KLOGI(TAG, "2: recv call %s, content %d", name, iv); 176 | data = caps_create(); 177 | caps_write_integer(data, 2); 178 | flora_call_reply_write_data(reply, data); 179 | flora_call_reply_end(reply); 180 | caps_destroy(data); 181 | break; 182 | case 3: 183 | caps_read_integer(msg, &iv); 184 | KLOGI(TAG, "3: recv call %s, content %d", name, iv); 185 | data = caps_create(); 186 | caps_write_integer(data, 3); 187 | flora_call_reply_write_data(reply, data); 188 | flora_call_reply_end(reply); 189 | caps_destroy(data); 190 | break; 191 | } 192 | } 193 | 194 | static void agent_subscribe(Agent &agent, flora_agent_t cagent) { 195 | agent.subscribe( 196 | "0", [](const char *name, shared_ptr &msg, uint32_t type) { 197 | KLOGI(TAG, "recv msg %s, type %u, content %p", name, type, msg.get()); 198 | }); 199 | agent.subscribe( 200 | "1", [](const char *name, shared_ptr &msg, uint32_t type) { 201 | int32_t iv = -1; 202 | msg->read(iv); 203 | KLOGI(TAG, "recv msg %s, type %u, content %d", name, type, iv); 204 | }); 205 | 206 | flora_agent_subscribe(cagent, "0", exam_subscribe_callback, (void *)0); 207 | flora_agent_subscribe(cagent, "1", exam_subscribe_callback, (void *)1); 208 | } 209 | 210 | static void agent_declare_methods(Agent &agent, flora_agent_t cagent) { 211 | agent.declare_method("2", [](const char *name, shared_ptr &msg, 212 | shared_ptr &reply) { 213 | int32_t iv = -1; 214 | msg->read(iv); 215 | KLOGI(TAG, "recv call %s, content %d", name, iv); 216 | shared_ptr data = Caps::new_instance(); 217 | data->write(2); 218 | reply->write_data(data); 219 | reply->end(); 220 | }); 221 | agent.declare_method("3", [](const char *name, shared_ptr &msg, 222 | shared_ptr &reply) { 223 | int32_t iv = -1; 224 | msg->read(iv); 225 | KLOGI(TAG, "recv call %s, content %d", name, iv); 226 | shared_ptr data = Caps::new_instance(); 227 | data->write(3); 228 | reply->write_data(data); 229 | reply->end(); 230 | }); 231 | 232 | flora_agent_declare_method(cagent, "2", exam_declare_method_callback, 233 | (void *)2); 234 | flora_agent_declare_method(cagent, "3", exam_declare_method_callback, 235 | (void *)3); 236 | } 237 | 238 | void DemoAllInOne::run_recv_msg() { 239 | KLOGI(TAG, "============run recv msg============"); 240 | start_service(); 241 | config_agent(agents[0], 'a'); 242 | config_agent(agents[1], 'b'); 243 | create_cagent(cagents, 'a'); 244 | create_cagent(cagents + 1, 'b'); 245 | agent_subscribe(agents[1], cagents[1]); 246 | agent_declare_methods(agents[1], cagents[1]); 247 | // wait agent connected and subscribe 248 | sleep(1); 249 | agent_post(agents[0], cagents[0]); 250 | agent_call(agents[0], cagents[0], "b"); 251 | // wait recv responses 252 | sleep(1); 253 | delete_cagent(cagents[0]); 254 | delete_cagent(cagents[1]); 255 | agents[0].close(); 256 | agents[1].close(); 257 | stop_service(); 258 | } 259 | 260 | void DemoAllInOne::test_errors() { 261 | KLOGI(TAG, "============test errors============"); 262 | start_service(); 263 | config_agent(agents[0], 'a'); 264 | config_agent(agents[1], 'b'); 265 | shared_ptr msg; 266 | int32_t r = agents[0].post("foo", msg, 2); 267 | KLOGI(TAG, "post msg with wrong type, return %d, except %d", r, 268 | FLORA_CLI_EINVAL); 269 | 270 | Response resp; 271 | r = agents[0].call("foo", msg, "b", resp, 0); 272 | KLOGI(TAG, "call method that not exists, return %d, except %d", r, 273 | FLORA_CLI_ENEXISTS); 274 | 275 | r = agents[0].call( 276 | "foo", msg, "b", 277 | [](int32_t rescode, Response &resp) { 278 | KLOGI(TAG, "call method that not exists, rescode %d, except %d", 279 | rescode, FLORA_CLI_ENEXISTS); 280 | }, 281 | 0); 282 | KLOGI(TAG, "call method return %d, except %d", r, FLORA_CLI_SUCCESS); 283 | 284 | agents[1].declare_method("foo", [](const char *name, shared_ptr &msg, 285 | shared_ptr &reply) { 286 | KLOGI(TAG, "method %s invoked: sleep 1 second", name); 287 | sleep(1); 288 | reply->end(); 289 | }); 290 | // wait declare method finish 291 | usleep(200000); 292 | r = agents[0].call("foo", msg, "b", resp, 1500); 293 | KLOGI(TAG, "call method foo: rescode %d, except %d", r, FLORA_CLI_SUCCESS); 294 | agents[0].call("foo", msg, "b", 295 | [](int32_t rescode, Response &resp) { 296 | KLOGI(TAG, "call method foo: rescode %d, except %d", rescode, 297 | FLORA_CLI_ETIMEOUT); 298 | }, 299 | 1); 300 | r = agents[0].call("foo", msg, "b", resp, 0); 301 | KLOGI(TAG, "call method foo: rescode %d, except %d", r, FLORA_CLI_ETIMEOUT); 302 | 303 | sleep(1); 304 | stop_service(); 305 | } 306 | 307 | void DemoAllInOne::test_reply_after_close() { 308 | KLOGI(TAG, "============test reply after close============"); 309 | start_service(); 310 | 311 | const char *targetName = "reply-after-close"; 312 | Agent agent; 313 | char buf[64]; 314 | shared_ptr outreply; 315 | snprintf(buf, sizeof(buf), "%s#%s", SERVICE_URI, targetName); 316 | agent.config(FLORA_AGENT_CONFIG_URI, buf); 317 | agent.declare_method("foo", 318 | [&outreply](const char *name, shared_ptr &args, 319 | shared_ptr &reply) { 320 | KLOGI(TAG, "remote method %s invoked", name); 321 | outreply = reply; 322 | }); 323 | agent.start(); 324 | // wait declare method finish 325 | usleep(200000); 326 | 327 | Response resp; 328 | shared_ptr empty_arg; 329 | int32_t r = agent.call("foo", empty_arg, targetName, resp, 100); 330 | if (r != FLORA_CLI_ETIMEOUT) { 331 | KLOGE(TAG, "agent call should timeout, but %d", r); 332 | agent.close(); 333 | stop_service(); 334 | return; 335 | } 336 | 337 | r = agent.call("foo", empty_arg, targetName, 338 | [](int32_t code, Response &resp) { 339 | KLOGI(TAG, "remote method foo return: %d, %d", code, 340 | resp.ret_code); 341 | }, 342 | 1000); 343 | if (r != FLORA_CLI_SUCCESS) { 344 | KLOGE(TAG, "agent call failed: %d", r); 345 | agent.close(); 346 | stop_service(); 347 | return; 348 | } 349 | usleep(200000); 350 | outreply.reset(); 351 | 352 | Agent otherAgent; 353 | otherAgent.config(FLORA_AGENT_CONFIG_URI, SERVICE_URI); 354 | otherAgent.start(); 355 | usleep(200000); 356 | r = otherAgent.call("foo", empty_arg, targetName, 357 | [](int32_t code, Response &resp) { 358 | KLOGI(TAG, "remote method foo return: %d, %d", code, 359 | resp.ret_code); 360 | }, 361 | 500); 362 | if (r != FLORA_CLI_SUCCESS) { 363 | KLOGE(TAG, "agent call failed: %d", r); 364 | agent.close(); 365 | stop_service(); 366 | return; 367 | } 368 | usleep(200000); 369 | agent.close(); 370 | outreply.reset(); 371 | 372 | sleep(1); 373 | stop_service(); 374 | } 375 | 376 | void DemoAllInOne::test_invoke_in_callback() { 377 | Agent agent; 378 | char buf[64]; 379 | const char *targetName = "invoke-in-callback"; 380 | 381 | start_service(); 382 | 383 | snprintf(buf, sizeof(buf), "%s#%s", SERVICE_URI, targetName); 384 | agent.config(FLORA_AGENT_CONFIG_URI, buf); 385 | agent.subscribe("foo", [&agent](const char *name, shared_ptr &, 386 | uint32_t) { 387 | shared_ptr empty; 388 | Response resp; 389 | int32_t r = agent.call("not-exists", empty, "blah", resp); 390 | KLOGI(TAG, "invoke 'call' in callback function, return %d, excepted %d", r, 391 | FLORA_CLI_EDEADLOCK); 392 | r = agent.call( 393 | "not-exists", empty, "blah", [&agent](int32_t code, Response &) { 394 | KLOGI(TAG, "invoke async 'call' in callback, return %d, excepted %d", 395 | code, FLORA_CLI_ENEXISTS); 396 | agent.close(); 397 | }); 398 | }); 399 | agent.start(); 400 | shared_ptr empty; 401 | agent.post("foo", empty); 402 | sleep(5); 403 | 404 | stop_service(); 405 | } 406 | --------------------------------------------------------------------------------