Server

Principles

Figure 1 Creation and response process on a server shows the key process of creating and processing messages on the server (OpenHiva::ServiceServer).

Figure 1 Creation and response process on the server
  • The service name must be globally unique.
  • The server process and client process must have the same user permission because shared memory file read and write are involved.
  • Only one thread is started on a server. The callback processing is serial, and the callback processing of different services is parallel.
  • For the same service, the input parameters such as serviceName, reqDataSize, resDataSize, and blockNum on the client must be the same as those on the server.
  • If the processing speed of the server is too slow, the call on the client may time out.
  1. Initialize resources.

    Before invoking the OpenHiva interface, invoke the OpenHiva::Init interface for initialization. If the return value is 0, the initialization is successful. Otherwise, the initialization fails.

  2. Create a server.
    1. Before constructing a server object (ServiceServer), you must register a node, that is, create a node handle OpenHiva::Node n.
    2. Use BuildShmOptions in OpenHiva::ServiceOptions to set server attributes, such as reqDataSize, resDataSize, and blockNum in Sample Code. Note that the values of these attributes cannot be 0. The message size is calculated as follows: msgSize = reqDataSize or resDataSize + Message header (64 bytes). The size of a single message cannot exceed 30 MB. The product of blockNum and msgSize must be less than 300 MB.
    3. Use the created node handle n to invoke the CreateServer interface to create a ServiceServer object, set the name of the service to be accessed (serviceName), and initialize the callback function for processing client requests.
  3. Enable the server.

    After the OpenHiva::ServiceServer object is created, the server cannot work properly. You need to invoke the Enable interface to enable the server. If the return value is 0, the function is enabled successfully. Otherwise, the function fails to be enabled.

    If the server is not enabled or fails to be enabled, the InvokeService interface (for sending requests) invoked by the client times out.

  4. Receive and respond to requests.

    After the server is enabled, it can receive the request (req) sent by the client, invoke the registered callback function for processing, and write the response back to the input parameter (res) of the client. Note that the callback function must check the length, which cannot exceed the threshold.

  5. Release resources.

    Before the process ends, the ServiceServer object and OpenHiva resources are released in sequence. After resources are released, the defined OpenHiva interfaces cannot be used.

Sample Code

The following is a code example of key steps for the server to create and respond to a request, which is for reference only:

#include <iostream>
#include <string>
#include <memory>
#include <unistd.h>
#include "open/init.h"
#include "open/node.h"
#include "open/service_server.h"
#include "open/service_options.h"
#include "std_msgs/include/StringMessage.h"

static constexpr uint32_t MAX_NAME_SIZE = 100;
static constexpr uint32_t DEFAULT_SLEEP_TIME_IN_CALLBACK = 10;            // unit: ms
uint32_t g_cbSleep = DEFAULT_SLEEP_TIME_IN_CALLBACK;
int cnt = 0;

void Usage()
{
    std::cout << "Usage: pmupload test_create_server nodeName topicName groupName bindType reqSize resSize blockNum enableOrDisable" << std::endl;
    std::cout << "if argc==2, server will use default value: pmupload test_create_server testNodeServer testService testGroupServer 0 100 100 10 0" << std::endl;
    std::cout << "bindType can be 0 1 2" << std::endl;
    std::cout << "Pointing: req/res size must bigger than messageSize + 8, or serialize will fail" << std::endl;
}

// Users define callback functions based on their own service logic to process request messages.
bool ResCb(Hiva::StdMsgs::StringMessage &reqBuf, Hiva::StdMsgs::StringMessage &resBuf)
{
    HIVA_INFO("callback req %s.", reqBuf.stringData.c_str());
    std::string key = "sssss";
    resBuf.stringData = reqBuf.stringData + key;
    HIVA_WARN("cb_sleep=%u ms", g_cbSleep);
    usleep(g_cbSleep * 1000);
    return true;
}

int main(int argc, char **argv)
{
    // 1. Initialize resources.
    std::string nodeName = "testNodeServer";
    std::string groupName = "testGroupServer";
    std::string serviceName = "testService";
    OpenHiva::ScheduleType scheType = OpenHiva::ScheduleType(0);
    int32_t reqDataSize = 100;
    int32_t resDataSize = 100;
    int32_t blockNum = 10;
    bool enableOrDisable = false;
    int argNum = 9;
    if (argc < argNum) {
        Usage();
        if (argc != 2) {
            return 0;
        }
    } else {
        nodeName = argv[1];
        serviceName = argv[2];
        groupName = argv[3];
        scheType = (OpenHiva::ScheduleType)strtol(argv[4], NULL, 10);
        reqDataSize = (int32_t)strtol(argv[5], NULL, 10);
        resDataSize = (int32_t)strtol(argv[6], NULL, 10);
        blockNum = (int32_t)strtol(argv[7], NULL, 10);
        enableOrDisable = (bool)strtol(argv[8], NULL, 10);
    }
    // Define a thread group.
    std::vector<OpenHiva::ScheduleGroup> scheGrpVec;
    OpenHiva::ScheduleGroup scheGrp;
    scheGrp.groupName = groupName;
    scheGrp.scheduleType = scheType;
    scheGrpVec.push_back(scheGrp);
    // Invoke the resource initialization interface.
    OpenHiva::Init(argc, argv, scheGrpVec);
    HIVA_EVENT("service server init ok!");
    HIVA_INFO("reqDataSize=%d.", reqDataSize);
    HIVA_INFO("resDataSize=%d.", resDataSize);
    HIVA_INFO("blockNum=%d.", blockNum);

    // 2. Create a server.
    // Construct the Node object.
    OpenHiva::Node node(nodeName);
    // Construct ServiceOptions.
    OpenHiva::ServiceOptions serOps;
    serOps.BuildShmOptions(reqDataSize, resDataSize, blockNum);
    // Invoke the CreateServer interface through the NodeHandle object to create the ServiceServer object.
    std::function<bool(Hiva::StdMsgs::StringMessage&, Hiva::StdMsgs::StringMessage&)> callback = ResCb;
    std::shared_ptr<OpenHiva::ServiceServer> server = node.CreateServer(serviceName, serOps, callback);
    // When constructing the ServiceServer object fails, invoke the Shutdown function to release resources and exit.
    if (server == nullptr) {
        HIVA_ERROR("create server failed");
        OpenHiva::Shutdown();
        return 0;
    }

    // 3. Enable the server to receive and respond to the request message.
    auto ret = server->Enable();
    bool enableFlag = true;
    // When the function fails to be enabled, the Shutdown function is invoked to release resources and exit.
    if (ret != Hiva::HIVA_SUCCESS) {
        HIVA_ERROR("enable server failed");
        OpenHiva::Shutdown();
        return 0;
    }
    // Check the Hiva node status. If the node is enabled, true is returned. If the node is shut down or fails to be initialized, false is returned. In this case, packets cannot be sent or received correctly.
    while (OpenHiva::Ready()) {
        if (enableOrDisable) {
            if (cnt % 10 == 5) {
                if (enableFlag) {
                    server->Disable();
                    enableFlag = false;
                    HIVA_EVENT("service server disable");
                } else {
                    server->Enable();
                    enableFlag = true;
                    HIVA_EVENT("service server enable");
                }
            }
        }
        cnt++;
        sleep(1);
    }

    // 4. Release resources.
    server->Disable();
    server->Destroy();
    OpenHiva::Shutdown();
    return 0;
}