在使用 EPRC 进行 TCP
通信的过程中,如果需要同时运行一个客户端和服务端就会出现异常。
这是因为其提供的 erpc_setup_tcp.cpp
只能初始化一个实例,那就需要对其进行理解并修改。
EPRC
能否支持在运行服务端的时候运行客户端?
从理论上来讲,服务端和客户端都维护自己对应的端口,读写数据都走自己的端口即可,互不相干。
从 ERPC 的 API 可以看到,服务端和客户端创建的代码是不一样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void erpc_client_init (erpc_transport_t transport, erpc_mbf_t message_buffer_factory) ;erpc_server_t erpc_server_init (erpc_transport_t transport, erpc_mbf_t message_buffer_factory) ;
那么应该是可以将二者区分开来应用的。
erpc_setup_tcp.cpp
的更改
在 erpc_setup_tcp.cpp
中,其使用
s_transport
来对 TCP
对象进行创建,但是模板类中的私有成员对象仅能存储一个对象元素:
1 2 3 4 5 6 uint64_t m_storage[(sizeof (T) + sizeof (uint64_t ) - 1 ) / sizeof (uint64_t )];
如果我们要同时使用一个服务端和客户端,就需要调用
erpc_transport_tcp_init
函数两次以创建两个对象,所以这里就需要对其进行更改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include "erpc_manually_constructed.h" #include "erpc_tcp_transport.h" #include "erpc_transport_setup.h" using namespace erpc;static ManuallyConstructed<TCPTransport> s_transport[2 ];static bool initialized[2 ];erpc_transport_t erpc_transport_tcp_init (const char *host, uint16_t port, bool isServer) { erpc_transport_t transport; if (initialized[isServer]) { return NULL ; } s_transport[isServer].construct (host, port, isServer); if (kErpcStatus_Success == s_transport[isServer]->open ()) { transport = reinterpret_cast <erpc_transport_t >(s_transport[isServer].get ()); initialized[isServer] = true ; } else { transport = NULL ; } return transport; } void erpc_transport_tcp_close (void ) { if (initialized[false ]) { s_transport[false ].get ()->close (true ); initialized[false ] = false ; } if (initialized[true ]) { s_transport[true ].get ()->close (true ); initialized[true ] = false ; } }
修改及验证
其实我们只需要在 上一篇
demo
的基础之上,再增加一个服务端,来接收原来服务端的数据即可验证。
新服务端的 IDL 文件
新服务端的 IDL 文件 erpcserver.erpc
依然很简单:
1 2 3 4 5 6 7 8 program erpcserver interface SERVER { oneway ServerHello (int32 val) }
然后再按照之前的方式生成文件即可。
test_erpcdemo_server.cpp
原服务端也需要增加初始化客户端的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <cstdlib> #include <iostream> #include <string> #include <erpc_client_setup.h> #include <erpc_server_setup.h> #include <erpc_transport_setup.h> #include "erpcdemo_server.h" #include "erpcserver.h" static const char * ret = "Hello, this is server!\n" ;binary_t * DemoHello (const binary_t * val) { std::cout << "client message: " << (char *)val->data; int len = std::strlen (ret) + 1 ; char * buf = (char *)std::malloc (len); std::strncpy (buf, ret, len - 1 ); binary_t * message = new binary_t {(uint8_t *)buf, (uint32_t )(len - 1 )}; ServerHello (-123456 ); return message; } int main (int argc, char *argv[]) { auto transport1 = erpc_transport_tcp_init ("127.0.0.1" , 60902 , false ); auto message_buffer_factory1 = erpc_mbf_dynamic_init (); erpc_client_init (transport1, message_buffer_factory1); auto transport = erpc_transport_tcp_init ("127.0.0.1" , 60901 , true ); erpc_mbf_t message_buffer_factory = erpc_mbf_dynamic_init (); erpc_server_init (transport, message_buffer_factory); erpc_add_service_to_server (create_DEMO_service ()); std::cout << "server is running!\n" ; erpc_server_run (); erpc_transport_tcp_close (); return 0 ; }
test_erpcserver_server.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <cstdlib> #include <iostream> #include <string> #include <erpc_server_setup.h> #include <erpc_transport_setup.h> #include "erpcserver_server.h" void ServerHello (int32_t val) { std::cout << "I get value from client: " << val << "\n" ; } int main (int argc, char *argv[]) { auto transport = erpc_transport_tcp_init ("127.0.0.1" , 60902 , true ); erpc_mbf_t message_buffer_factory = erpc_mbf_dynamic_init (); erpc_server_init (transport, message_buffer_factory); erpc_add_service_to_server (create_SERVER_service ()); std::cout << "server2 is running!\n" ; erpc_server_run (); erpc_transport_tcp_close (); return 0 ; }
NESTED_CALLS_DETECTION
NESTED_CALLS_DETECTION
宏是用于检测在服务端的函数被调用期间,是否又进行了其他接口函数的调用。这个宏默认是使能的。
> 服务端生成的自动代码中,会先后设置变量
nestingDetection
的值为 true
和
false
,以检测这期间是否有嵌套调用。
而如果我们修改当前代码到客户端和服务端同时使用时,便会给这个宏带来误导,导致客户端的调用函数可能会返回失败
kErpcStatus_NestedCallFailure
。
所以这里要在文件 erpc_config.h
中关闭该宏。
修改 Makefile
原服务端既是客户端也是服务端,且我们也新增了代码,所以需要修改如下:
1 2 3 4 5 6 7 INCLUDE = /usr/local/include /erpc LIBRARY = /usr/local/lib all: g++ test_erpcdemo_client.cpp erpcdemo_client.cpp erpc_setup_tcp.cpp -I${INCLUDE} -L${LIBRARY} -lerpc -lpthread -o client g++ test_erpcdemo_server.cpp erpcdemo_server.cpp erpcserver_client.cpp erpc_setup_tcp.cpp -I${INCLUDE} -L${LIBRARY} -lerpc -lpthread -o server g++ test_erpcserver_server.cpp erpcserver_server.cpp erpc_setup_tcp.cpp -I${INCLUDE} -L${LIBRARY} -lerpc -lpthread -o server2
验证
可以看到已经正常运行了:
新服务端:
1 2 3 $ ./server2 server2 is running! I get value from client: -123456
原服务端:
1 2 3 $ ./server server is running! client message: Hello, this is client!
原客户端:
1 2 $ ./client Get message of server: Hello, this is server!
总结
其实在 ERPC 源码中存在着不少静态全局变量,这是代码中的 Bad
smell,这些对象应该是能够被动态的创建和销毁的。