Procházet zdrojové kódy

jrpc server and client blocking OK

Lyon před 1 rokem
rodič
revize
c66dddaca3

+ 286 - 146
package/jrpc/jrpc.c

@@ -30,7 +30,34 @@
 #include <stdlib.h>
 #include <string.h>
 
-// 示例函数:add
+/* private function */
+static int JRPC_send_message_with_retry(JRPC* self,
+                                        const char* request_str,
+                                        int retry_count,
+                                        unsigned long ack_timeout,
+                                        int id,
+                                        int type,
+                                        const char* label);
+
+static void JRPC_send_acknowledgement(JRPC* self,
+                                      int id,
+                                      ack_status status,
+                                      const char* label);
+
+static const char* JRPC_type_2_string(int type) {
+    switch (type) {
+        case TYPE_REQUEST:
+            return STR_TYPE_REQUEST;
+        case TYPE_ACK:
+            return STR_TYPE_ACK;
+        case TYPE_RESULT:
+            return STR_TYPE_RESULT;
+        default:
+            return "UNKNOWN";
+    }
+}
+
+// Example function: add
 cJSON* add(cJSON* params[], int param_count) {
     int a = params[0]->valueint;
     int b = params[1]->valueint;
@@ -39,7 +66,7 @@ cJSON* add(cJSON* params[], int param_count) {
     return result;
 }
 
-// 示例非阻塞函数:add_nonblocking
+// Example non-blocking function: add_nonblocking
 int add_nonblocking(int id, cJSON* params[], int param_count, JRPC* self) {
     int a = params[0]->valueint;
     int b = params[1]->valueint;
@@ -49,7 +76,7 @@ int add_nonblocking(int id, cJSON* params[], int param_count, JRPC* self) {
     return 0;
 }
 
-// 示例函数:subtract
+// Example function: subtract
 cJSON* subtract(cJSON* params[], int param_count) {
     int a = params[0]->valueint;
     int b = params[1]->valueint;
@@ -66,7 +93,8 @@ rpc_mapping_nonblocking default_nonblocking_rpc_map[] = {
     {"add_nonblocking", add_nonblocking, 2},
     RPC_MAP_END};
 
-void JRPC_send_acknowledgement(JRPC* self, int id, ack_status status) {
+// Function to create an acknowledgement string
+char* create_acknowledgement_string(int id, ack_status status) {
     cJSON* response = cJSON_CreateObject();
     cJSON_AddStringToObject(response, STR_JSON_RPC_FIELD, STR_JSON_RPC_VERSION);
     cJSON_AddStringToObject(
@@ -77,15 +105,24 @@ void JRPC_send_acknowledgement(JRPC* self, int id, ack_status status) {
                                            : STR_UNKNOWN_STATUS);
     cJSON_AddNumberToObject(response, STR_ID_FIELD, id);
     cJSON_AddNumberToObject(response, STR_TYPE_FIELD,
-                            TYPE_ACK);  // 添加类型字段
+                            TYPE_ACK);  // Add type field
 
     char* response_str = cJSON_Print(response);
-    printf("Acknowledgement: %s\n", response_str);
+    cJSON_Delete(response);
+    return response_str;
+}
+
+// Function to send an acknowledgement
+static void JRPC_send_acknowledgement(JRPC* self,
+                                      int id,
+                                      ack_status status,
+                                      const char* label) {
+    char* response_str = create_acknowledgement_string(id, status);
+    printf("[%s] ACK: %s\n", label, response_str);
 
     self->send(response_str);
 
-    free(response_str);
-    cJSON_Delete(response);
+    cJSON_free(response_str);
 }
 
 void JRPC_send_response(JRPC* self, int id, cJSON* result) {
@@ -94,17 +131,18 @@ void JRPC_send_response(JRPC* self, int id, cJSON* result) {
     cJSON_AddItemToObject(response, STR_RESULT_FIELD, result);
     cJSON_AddNumberToObject(response, STR_ID_FIELD, id);
     cJSON_AddNumberToObject(response, STR_TYPE_FIELD,
-                            TYPE_RESULT);  // 添加类型字段
+                            TYPE_RESULT);  // Add type field
 
     char* response_str = cJSON_Print(response);
-    printf("Response: %s\n", response_str);
+    printf("[Server] Response: %s\n", response_str);
 
     self->send(response_str);
 
-    free(response_str);
+    cJSON_free(response_str);
+    cJSON_Delete(response);
 }
 
-void JRPC_handle_request(JRPC* self, const char* json_str) {
+void JRPC_server_handle(JRPC* self, const char* json_str) {
     cJSON* json = cJSON_Parse(json_str);
     if (json == NULL) {
         printf("Error parsing JSON\n");
@@ -120,7 +158,7 @@ void JRPC_handle_request(JRPC* self, const char* json_str) {
     if (!cJSON_IsString(jsonrpc) || !cJSON_IsString(method) ||
         !cJSON_IsArray(params) || !cJSON_IsNumber(id) ||
         !cJSON_IsNumber(type)) {
-        printf("Invalid JSON RPC request format\n");
+        printf("[Server] Invalid JSON RPC request format: %s\n", json_str);
         cJSON_Delete(json);
         return;
     }
@@ -141,51 +179,48 @@ void JRPC_handle_request(JRPC* self, const char* json_str) {
     rpc_function_nonblocking func_nonblocking =
         JRPC_find_nonblocking_rpc_function(self, method->valuestring,
                                            &expected_param_count);
-    if (func_nonblocking != NULL) {
-        int param_count = cJSON_GetArraySize(params);
-        if (expected_param_count != PARAM_COUNT_NO_CHECK &&
-            param_count != expected_param_count) {
-            JRPC_send_acknowledgement(self, id->valueint, ACK_INVALID_PARAMS);
-            cJSON_Delete(json);
-            return;
-        }
+    rpc_function func = NULL;
+    int is_nonblocking = (func_nonblocking != NULL);
 
-        // 发送成功ACK
-        JRPC_send_acknowledgement(self, id->valueint, ACK_SUCCESS);
-
-        // 调用非阻塞函数
-        cJSON* param_array[param_count];
-        for (int i = 0; i < param_count; i++) {
-            param_array[i] = cJSON_GetArrayItem(params, i);
-        }
-
-        func_nonblocking(id->valueint, param_array, param_count, self);
-    } else {
-        rpc_function func = JRPC_find_rpc_function(self, method->valuestring,
-                                                   &expected_param_count);
+    if (!is_nonblocking) {
+        func = JRPC_find_rpc_function(self, method->valuestring,
+                                      &expected_param_count);
         if (func == NULL) {
-            JRPC_send_acknowledgement(self, id->valueint, ACK_METHOD_NOT_FOUND);
+            JRPC_send_acknowledgement(self, id->valueint, ACK_METHOD_NOT_FOUND,
+                                      "Server");
             cJSON_Delete(json);
             return;
         }
+    }
 
-        int param_count = cJSON_GetArraySize(params);
-        if (expected_param_count != PARAM_COUNT_NO_CHECK &&
-            param_count != expected_param_count) {
-            JRPC_send_acknowledgement(self, id->valueint, ACK_INVALID_PARAMS);
-            cJSON_Delete(json);
-            return;
-        }
+    int param_count = cJSON_GetArraySize(params);
+    if (expected_param_count != PARAM_COUNT_NO_CHECK &&
+        param_count != expected_param_count) {
+        JRPC_send_acknowledgement(self, id->valueint, ACK_INVALID_PARAMS,
+                                  "Server");
+        cJSON_Delete(json);
+        return;
+    }
+
+    char* ack_str = create_acknowledgement_string(id->valueint, ACK_SUCCESS);
+    if (JRPC_send_message_with_retry(self, ack_str, RETRY_COUNT, ACK_TIMEOUT,
+                                     id->valueint, TYPE_ACK, "Server") != 0) {
+        cJSON_Delete(json);
+        return;
+    }
 
-        // 发送成功ACK
-        JRPC_send_acknowledgement(self, id->valueint, ACK_SUCCESS);
+    if (ack_str) {
+        cJSON_free(ack_str);
+    }
 
-        // 调用函数
-        cJSON* param_array[param_count];
-        for (int i = 0; i < param_count; i++) {
-            param_array[i] = cJSON_GetArrayItem(params, i);
-        }
+    cJSON* param_array[param_count];
+    for (int i = 0; i < param_count; i++) {
+        param_array[i] = cJSON_GetArrayItem(params, i);
+    }
 
+    if (is_nonblocking) {
+        func_nonblocking(id->valueint, param_array, param_count, self);
+    } else {
         cJSON* result = func(param_array, param_count);
         JRPC_send_response(self, id->valueint, result);
     }
@@ -202,7 +237,7 @@ rpc_function JRPC_find_rpc_function(JRPC* self,
             return self->map[i].func;
         }
     }
-    return NULL;  // 没有找到对应的函数
+    return NULL;  // Function not found
 }
 
 rpc_function_nonblocking JRPC_find_nonblocking_rpc_function(JRPC* self,
@@ -214,15 +249,15 @@ rpc_function_nonblocking JRPC_find_nonblocking_rpc_function(JRPC* self,
             return self->nonblocking_map[i].func;
         }
     }
-    return NULL;  // 没有找到对应的函数
+    return NULL;  // Function not found
 }
 
-// 缓存添加函数
+// Cache add function
 void JRPC_cache_add(JRPC* self, cJSON* item) {
     if (self->cache_count < CACHE_SIZE) {
         self->cache[self->cache_count++] = item;
     } else {
-        // 缓存已满,删除最旧的
+        // Cache full, delete oldest
         cJSON_Delete(self->cache[0]);
         for (int i = 0; i < CACHE_SIZE - 1; i++) {
             self->cache[i] = self->cache[i + 1];
@@ -231,16 +266,18 @@ void JRPC_cache_add(JRPC* self, cJSON* item) {
     }
 }
 
-// 缓存获取函数
+// Cache get function
 cJSON* JRPC_cache_get(JRPC* self, int id, int type) {
     for (int i = 0; i < self->cache_count; i++) {
         cJSON* cached_json = self->cache[i];
-        cJSON* cached_id = cJSON_GetObjectItem(cached_json, STR_ID_FIELD);
+        cJSON* cached_id =
+
+            cJSON_GetObjectItem(cached_json, STR_ID_FIELD);
         cJSON* cached_type = cJSON_GetObjectItem(cached_json, STR_TYPE_FIELD);
         if (cached_id && cJSON_IsNumber(cached_id) &&
             cached_id->valueint == id && cached_type &&
             cJSON_IsNumber(cached_type) && cached_type->valueint == type) {
-            // 找到匹配项,返回并从缓存中删除
+            // Found match, return and remove from cache
             cJSON* result = cached_json;
             for (int j = i; j < self->cache_count - 1; j++) {
                 self->cache[j] = self->cache[j + 1];
@@ -253,17 +290,19 @@ cJSON* JRPC_cache_get(JRPC* self, int id, int type) {
 }
 
 cJSON* JRPC_receive_with_id_and_type(JRPC* self, int id, int type) {
-    // 先检查缓存
+    // Check cache first
     cJSON* cached_json = JRPC_cache_get(self, id, type);
     if (cached_json != NULL) {
         return cached_json;
     }
 
-    // 缓存中没有匹配项,从接收接口获取
+    // No match in cache, receive from interface
     char* received_str = self->receive();
     if (received_str != NULL) {
         cJSON* received_json = cJSON_Parse(received_str);
-        free(received_str);
+        if (self->receive_need_free) {
+            free(received_str);
+        }
         if (received_json != NULL) {
             cJSON* received_id =
                 cJSON_GetObjectItem(received_json, STR_ID_FIELD);
@@ -275,7 +314,7 @@ cJSON* JRPC_receive_with_id_and_type(JRPC* self, int id, int type) {
                 received_type->valueint == type) {
                 return received_json;
             } else {
-                // 缓存数据
+                // Cache data
                 JRPC_cache_add(self, received_json);
             }
         }
@@ -283,12 +322,44 @@ cJSON* JRPC_receive_with_id_and_type(JRPC* self, int id, int type) {
     return NULL;
 }
 
+static int JRPC_send_message_with_retry(JRPC* self,
+                                        const char* request_str,
+                                        int retry_count,
+                                        unsigned long ack_timeout,
+                                        int id,
+                                        int type,
+                                        const char* label) {
+    for (int retry = 0; retry < retry_count; retry++) {
+        printf("[%s] Send and await %s with retry [%d]: %s\n", label,
+               JRPC_type_2_string(type), retry, request_str);
+        self->send(request_str);
+
+        unsigned long start_time = self->tick();
+        cJSON* ack_json = NULL;
+        while (1) {
+            ack_json = JRPC_receive_with_id_and_type(self, id, type);
+            if (ack_json != NULL) {
+                printf("[%s] Received ACK, id: %d\n", label, id);
+                cJSON_Delete(ack_json);
+                return 0;  // Received correct ACK
+            }
+            if (self->tick() - start_time >= ack_timeout) {
+                printf("[%s] ACK timeout, retrying...\n", label);
+                break;
+            }
+            self->yield();  // Thread switch
+        }
+    }
+    printf("[%s] Failed to receive ACK after %d retries\n", label, retry_count);
+    return -1;  // Failed to receive correct ACK after retries
+}
+
 void JRPC_send_request_no_blocking(JRPC* self,
                                    const char* method,
                                    cJSON* params[],
                                    int param_count,
                                    rpc_callback callback) {
-    // 构建请求
+    // Build request
     int id = ++self->current_id;
     cJSON* request = cJSON_CreateObject();
     cJSON_AddStringToObject(request, STR_JSON_RPC_FIELD, STR_JSON_RPC_VERSION);
@@ -301,46 +372,21 @@ void JRPC_send_request_no_blocking(JRPC* self,
     cJSON_AddItemToObject(request, STR_PARAMS_FIELD, params_array);
     cJSON_AddNumberToObject(request, STR_ID_FIELD, id);
     cJSON_AddNumberToObject(request, STR_TYPE_FIELD,
-                            TYPE_REQUEST);  // 添加类型字段
+                            TYPE_REQUEST);  // Add type field
 
     char* request_str = cJSON_Print(request);
-    printf("Sending Request (no_blocking): %s\n", request_str);
-
-    // 发送请求并重试
-    int retry;
-    for (retry = 0; retry < RETRY_COUNT; retry++) {
-        self->send(request_str);
-
-        unsigned long start_time = self->tick();
-        cJSON* ack_json = NULL;
-        while (1) {
-            ack_json = JRPC_receive_with_id_and_type(self, id, TYPE_ACK);
-            if (ack_json != NULL) {
-                printf("Received ACK, id: %d\n", id);
-                cJSON_Delete(ack_json);
-                break;  // 收到正确的ACK
-            }
-            if (self->tick() - start_time >= ACK_TIMEOUT) {
-                printf("ACK timeout, retrying...\n");
-                break;
-            }
-            self->yield();  // 多线程切换
-        }
+    printf("[Client] Sending Request (no_blocking): %s\n", request_str);
 
-        if (ack_json != NULL) {
-            break;
-        }
+    if (JRPC_send_message_with_retry(self, request_str, RETRY_COUNT,
+                                     ACK_TIMEOUT, id, TYPE_ACK,
+                                     "Client") != 0) {
+        // If ACK received
+        callback(NULL);  // Simulate callback, no result
     }
 
-    // 如果收到ACK
-    if (retry < RETRY_COUNT) {
-        // 调用回调函数处理结果
-        callback(NULL);  // 模拟调用回调函数,不传递结果
-    } else {
-        printf("Failed to receive ACK after %d retries\n", RETRY_COUNT);
-    }
+    JRPC_send_acknowledgement(self, id, ACK_SUCCESS, "Client");
 
-    free(request_str);
+    cJSON_free(request_str);
     cJSON_Delete(request);
 }
 
@@ -348,7 +394,7 @@ cJSON* JRPC_send_request_blocking(JRPC* self,
                                   const char* method,
                                   cJSON* params[],
                                   int param_count) {
-    // 构建请求
+    // Build request
     int id = ++self->current_id;
     cJSON* request = cJSON_CreateObject();
     cJSON_AddStringToObject(request, STR_JSON_RPC_FIELD, STR_JSON_RPC_VERSION);
@@ -361,70 +407,54 @@ cJSON* JRPC_send_request_blocking(JRPC* self,
     cJSON_AddItemToObject(request, STR_PARAMS_FIELD, params_array);
     cJSON_AddNumberToObject(request, STR_ID_FIELD, id);
     cJSON_AddNumberToObject(request, STR_TYPE_FIELD,
-                            TYPE_REQUEST);  // 添加类型字段
+                            TYPE_REQUEST);  // Add type field
 
     char* request_str = cJSON_Print(request);
-    printf("Sending Request (blocking): %s\n", request_str);
-
-    cJSON* ack_json = NULL;
-    // 发送请求并重试
-    for (int retry = 0; retry < RETRY_COUNT; retry++) {
-        self->send(request_str);
+    printf("[Client] Sending Request (blocking): %s\n", request_str);
 
-        unsigned long start_time = self->tick();
-        while (1) {
-            ack_json = JRPC_receive_with_id_and_type(self, id, TYPE_ACK);
-            if (ack_json != NULL) {
-                cJSON_Delete(ack_json);
-                break;  // 收到正确的ACK
-            }
-            if (self->tick() - start_time >= ACK_TIMEOUT) {
-                printf("ACK timeout, retrying...\n");
-                break;
-            }
-            self->yield();  // 多线程切换
-        }
-
-        if (ack_json != NULL) {
-            break;
-        }
-    }
-
-    if (ack_json == NULL) {
-        printf("Failed to receive ACK after %d retries\n", RETRY_COUNT);
-        free(request_str);
+    if (JRPC_send_message_with_retry(self, request_str, RETRY_COUNT,
+                                     ACK_TIMEOUT, id, TYPE_ACK,
+                                     "Client") != 0) {
+        cJSON_free(request_str);
         cJSON_Delete(request);
         return NULL;
     }
-
-    printf("Received ACK: %d\n", id);
-
-    // 等待执行完毕响应
+    JRPC_send_acknowledgement(self, id, ACK_SUCCESS, "Client");
+    // Wait for response
     unsigned long start_time = self->tick();
     while (1) {
         cJSON* response_json =
             JRPC_receive_with_id_and_type(self, id, TYPE_RESULT);
         if (response_json != NULL) {
             cJSON_Delete(request);
-            free(request_str);
+            cJSON_free(request_str);
+            char* response_str = cJSON_Print(response_json);
+            printf("[Client] Received Response: %s\n", response_str);
+            cJSON_free(response_str);
             return response_json;
         }
         if (self->tick() - start_time >= BLOCKING_TIMEOUT) {
-            printf("Response timeout\n");
-            free(request_str);
+            printf("[Client] Response timeout\n");
+            cJSON_free(request_str);
             cJSON_Delete(request);
             return NULL;
         }
-        self->yield();  // 多线程切换
+        self->yield();  // Thread switch
     }
 }
 
-// 模拟发送函数
+// Mock send function with validation
+static char* mock_sent_message = NULL;
+
 static void mock_send(const char* message) {
-    printf("mock send: %s\n", message);
+    printf("[Mock] send: %s\n", message);
+    if (mock_sent_message) {
+        free(mock_sent_message);
+    }
+    mock_sent_message = strdup(message);  // Capture sent message
 }
 
-// 模拟接收函数(非阻塞)
+// Mock receive function (non-blocking)
 static char* mock_receive(void) {
     static int call_count = 0;
     call_count++;
@@ -446,15 +476,36 @@ static char* mock_receive(void) {
     }
 }
 
-// 模拟 yield 函数
+static char* mock_receive_server_test(void) {
+    static int call_count = 0;
+    call_count++;
+    switch (call_count) {
+        case 3:
+            return strdup(
+                "{\"jsonrpc\": \"1.0\", \"status\": \"received\", \"id\": 1, "
+                "\"type\": 1}");
+        case 6:
+            return strdup(
+                "{\"jsonrpc\": \"1.0\", \"status\": \"received\", \"id\": 2, "
+                "\"type\": 1}");
+        case 9:
+            return strdup(
+                "{\"jsonrpc\": \"1.0\", \"status\": \"received\", \"id\": 3, "
+                "\"type\": 1}");
+        default:
+            return NULL;
+    }
+}
+
+// Mock yield function
 static void mock_yield(void) {
-    printf("$");
+    printf("[Y]");
 }
 
-// 模拟 tick 函数
+// Mock tick function
 static unsigned long mock_tick_ms(void) {
     static unsigned long tick = 0;
-    tick += 100;  // 模拟每次调用增加100ms
+    tick += 100;  // Simulate 100ms per call
     return tick;
 }
 
@@ -463,30 +514,31 @@ static void result_callback(cJSON* result) {
            result ? cJSON_Print(result) : "No result");
 }
 
-int jrpc_base_test() {
+int jrpc_test_client() {
     int ret = 0;
     JRPC jrpc = {default_rpc_map,
                  default_nonblocking_rpc_map,
                  mock_send,
                  mock_receive,
+                 1,
                  mock_yield,
                  mock_tick_ms,
                  0,
                  {NULL},
                  0};
 
-    // 测试 no_blocking
+    // Test no_blocking
     cJSON* params1[] = {cJSON_CreateNumber(5), cJSON_CreateNumber(3)};
     JRPC_send_request_no_blocking(&jrpc, "add_nonblocking", params1, 2,
                                   result_callback);
 
-    // 测试 blocking
+    // Test blocking
     cJSON* params2[] = {cJSON_CreateNumber(5), cJSON_CreateNumber(3)};
     cJSON* response = JRPC_send_request_blocking(&jrpc, "add", params2, 2);
     char* call_result = cJSON_Print(response);
-    printf("Blocking call result: %s\n", call_result);
-    free(call_result);
-    // 计算结果应为 8
+    printf("[Client] Blocking call result: %s\n", call_result);
+    cJSON_free(call_result);
+    // Result should be 8
     if (response == NULL ||
         cJSON_GetObjectItem(response, "result")->valueint != 8) {
         ret = -1;
@@ -498,5 +550,93 @@ int jrpc_base_test() {
         cJSON_Delete(params2[i]);
     }
 
+    if (mock_sent_message) {
+        free(mock_sent_message);
+        mock_sent_message = NULL;
+    }
+
+    return ret;
+}
+
+int jrpc_compare_json_strings(const char* json_str1, const char* json_str2) {
+    cJSON* json1 = cJSON_Parse(json_str1);
+    cJSON* json2 = cJSON_Parse(json_str2);
+
+    if (json1 == NULL || json2 == NULL) {
+        if (json1) {
+            cJSON_Delete(json1);
+        } else {
+            printf("json1 is NULL\n");
+        }
+        if (json2) {
+            cJSON_Delete(json2);
+        } else {
+            printf("json2 is NULL\n");
+        }
+        return -1;
+    }
+
+    int result = cJSON_Compare(json1, json2, 1) ? 0 : -1;
+    if (0 != result) {
+        printf("Json compare failed\n");
+        printf("json1: %s\n", json_str1);
+        printf("json2: %s\n", json_str2);
+    }
+
+    cJSON_Delete(json1);
+    cJSON_Delete(json2);
+
+    return result;
+}
+
+int jrpc_validate_response(const char* expected_response) {
+    if (mock_sent_message == NULL) {
+        return -1;
+    }
+
+    return jrpc_compare_json_strings(mock_sent_message, expected_response);
+}
+
+int jrpc_test_server() {
+    JRPC jrpc = {default_rpc_map,
+                 default_nonblocking_rpc_map,
+                 mock_send,
+                 mock_receive_server_test,
+                 1,
+                 mock_yield,
+                 mock_tick_ms,
+                 0,
+                 {NULL},
+                 0};
+
+    const char* requests[] = {
+        "{\"jsonrpc\": \"1.0\", \"method\": \"add\", \"params\": [5, 3], "
+        "\"id\": 1, \"type\": 0}",
+        "{\"jsonrpc\": \"1.0\", \"method\": \"subtract\", \"params\": [10, 4], "
+        "\"id\": 2, \"type\": 0}",
+        "{\"jsonrpc\": \"1.0\", \"method\": \"add_nonblocking\", \"params\": "
+        "[2, 2], \"id\": 3, \"type\": 0}",
+    };
+
+    const char* expected_responses[] = {
+        "{\"jsonrpc\": \"1.0\", \"result\": 8, \"id\": 1, \"type\": 2}",
+        "{\"jsonrpc\": \"1.0\", \"result\": 6, \"id\": 2, \"type\": 2}",
+        "{\"jsonrpc\": \"1.0\", \"result\": 4, \"id\": 3, \"type\": 2}"};
+
+    int ret = 0;
+
+    for (int i = 0; i < sizeof(requests) / sizeof(requests[0]); i++) {
+        JRPC_server_handle(&jrpc, requests[i]);
+        if (jrpc_validate_response(expected_responses[i]) != 0) {
+            ret = -1;
+            break;
+        }
+    }
+
+    if (mock_sent_message) {
+        free(mock_sent_message);
+        mock_sent_message = NULL;
+    }
+
     return ret;
 }

+ 31 - 41
package/jrpc/jrpc.h

@@ -1,37 +1,14 @@
-/*
- * This file is part of the PikaPython project.
- * http://github.com/pikastech/pikapython
- *
- * MIT License
- *
- * Copyright (c) 2024 lyon liang6516@outlook.com
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-// jrpc.h
 #ifndef JRPC_H
 #define JRPC_H
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #include "cJSON.h"
+#include <stdint.h>
 
-// 宏定义字符串常量
+// Define string constants
 #define STR_JSON_RPC_VERSION "1.0"
 #define STR_JSON_RPC_FIELD "jsonrpc"
 #define STR_METHOD_FIELD "method"
@@ -48,19 +25,23 @@
     { NULL, NULL, 0 }
 #define PARAM_COUNT_NO_CHECK -1
 
-// 超时宏定义
+// Timeout definitions
 #define ACK_TIMEOUT 1000
-#define BLOCKING_TIMEOUT 5000
+#define BLOCKING_TIMEOUT 20000
 #define RETRY_COUNT 5
 
-// 数据包类型的宏定义
+// Packet type definitions
 #define TYPE_REQUEST 0
 #define TYPE_ACK 1
 #define TYPE_RESULT 2
 
+#define STR_TYPE_REQUEST "REQ"
+#define STR_TYPE_ACK "ACK"
+#define STR_TYPE_RESULT "RES"
+
 #define STR_TYPE_FIELD "type"
 
-// 缓存数组大小
+// Cache array size
 #define CACHE_SIZE 16
 
 typedef enum {
@@ -80,13 +61,13 @@ typedef int (*rpc_function_nonblocking)(int id,
 typedef struct {
     const char* name;
     rpc_function func;
-    int param_count;  // 参数数量
+    int param_count;  // Number of parameters
 } rpc_mapping;
 
 typedef struct {
     const char* name;
     rpc_function_nonblocking func;
-    int param_count;  // 参数数量
+    int param_count;  // Number of parameters
 } rpc_mapping_nonblocking;
 
 typedef void (*rpc_callback)(cJSON* result);
@@ -100,16 +81,16 @@ struct JRPC_ {
     rpc_mapping_nonblocking* nonblocking_map;
     send_function send;
     receive_function receive;
+    uint8_t receive_need_free;
     yield_function yield;
     tick_function tick;
     int current_id;
-    cJSON* cache[CACHE_SIZE];  // 添加缓存
+    cJSON* cache[CACHE_SIZE];  // Add cache
     int cache_count;
 };
 
-// 函数声明
-void JRPC_send_acknowledgement(JRPC* self, int id, ack_status status);
-void JRPC_handle_request(JRPC* self, const char* json_str);
+// Function declarations
+void JRPC_server_handle(JRPC* self, const char* json_str);
 rpc_function JRPC_find_rpc_function(JRPC* self,
                                     const char* name,
                                     int* param_count);
@@ -128,6 +109,15 @@ cJSON* JRPC_send_request_blocking(JRPC* self,
                                   int param_count);
 
 cJSON* JRPC_receive_with_id_and_type(JRPC* self, int id, int type);
+int jrpc_validate_response(const char* expected_response);
+int jrpc_compare_json_strings(const char* json_str1, const char* json_str2);
+int jrpc_validate_response(const char* expected_response);
+
+int jrpc_test_client();
+int jrpc_test_server();
+
+#ifdef __cplusplus
+}
+#endif
 
-int jrpc_base_test();
-#endif  // jrpc.h
+#endif  // JRPC_H

+ 3 - 1
port/linux/.vscode/launch.json

@@ -28,7 +28,9 @@
                 // "--gtest_filter=time*"
                 // "--gtest_filter=flashdb.tsdb1"
                 // "--gtest_filter=flashdb.base"
-                "--gtest_filter=jrpc.base"
+                // "--gtest_filter=jrpc.server"
+                // "--gtest_filter=jrpc.client"
+                "--gtest_filter=jrpc.BlockingRequestBetweenTwoJRPC"
             ],
             "stopAtEntry": false,
             "cwd": "${workspaceFolder}",

+ 286 - 146
port/linux/package/pikascript/pikascript-lib/jrpc/jrpc.c

@@ -30,7 +30,34 @@
 #include <stdlib.h>
 #include <string.h>
 
-// 示例函数:add
+/* private function */
+static int JRPC_send_message_with_retry(JRPC* self,
+                                        const char* request_str,
+                                        int retry_count,
+                                        unsigned long ack_timeout,
+                                        int id,
+                                        int type,
+                                        const char* label);
+
+static void JRPC_send_acknowledgement(JRPC* self,
+                                      int id,
+                                      ack_status status,
+                                      const char* label);
+
+static const char* JRPC_type_2_string(int type) {
+    switch (type) {
+        case TYPE_REQUEST:
+            return STR_TYPE_REQUEST;
+        case TYPE_ACK:
+            return STR_TYPE_ACK;
+        case TYPE_RESULT:
+            return STR_TYPE_RESULT;
+        default:
+            return "UNKNOWN";
+    }
+}
+
+// Example function: add
 cJSON* add(cJSON* params[], int param_count) {
     int a = params[0]->valueint;
     int b = params[1]->valueint;
@@ -39,7 +66,7 @@ cJSON* add(cJSON* params[], int param_count) {
     return result;
 }
 
-// 示例非阻塞函数:add_nonblocking
+// Example non-blocking function: add_nonblocking
 int add_nonblocking(int id, cJSON* params[], int param_count, JRPC* self) {
     int a = params[0]->valueint;
     int b = params[1]->valueint;
@@ -49,7 +76,7 @@ int add_nonblocking(int id, cJSON* params[], int param_count, JRPC* self) {
     return 0;
 }
 
-// 示例函数:subtract
+// Example function: subtract
 cJSON* subtract(cJSON* params[], int param_count) {
     int a = params[0]->valueint;
     int b = params[1]->valueint;
@@ -66,7 +93,8 @@ rpc_mapping_nonblocking default_nonblocking_rpc_map[] = {
     {"add_nonblocking", add_nonblocking, 2},
     RPC_MAP_END};
 
-void JRPC_send_acknowledgement(JRPC* self, int id, ack_status status) {
+// Function to create an acknowledgement string
+char* create_acknowledgement_string(int id, ack_status status) {
     cJSON* response = cJSON_CreateObject();
     cJSON_AddStringToObject(response, STR_JSON_RPC_FIELD, STR_JSON_RPC_VERSION);
     cJSON_AddStringToObject(
@@ -77,15 +105,24 @@ void JRPC_send_acknowledgement(JRPC* self, int id, ack_status status) {
                                            : STR_UNKNOWN_STATUS);
     cJSON_AddNumberToObject(response, STR_ID_FIELD, id);
     cJSON_AddNumberToObject(response, STR_TYPE_FIELD,
-                            TYPE_ACK);  // 添加类型字段
+                            TYPE_ACK);  // Add type field
 
     char* response_str = cJSON_Print(response);
-    printf("Acknowledgement: %s\n", response_str);
+    cJSON_Delete(response);
+    return response_str;
+}
+
+// Function to send an acknowledgement
+static void JRPC_send_acknowledgement(JRPC* self,
+                                      int id,
+                                      ack_status status,
+                                      const char* label) {
+    char* response_str = create_acknowledgement_string(id, status);
+    printf("[%s] ACK: %s\n", label, response_str);
 
     self->send(response_str);
 
-    free(response_str);
-    cJSON_Delete(response);
+    cJSON_free(response_str);
 }
 
 void JRPC_send_response(JRPC* self, int id, cJSON* result) {
@@ -94,17 +131,18 @@ void JRPC_send_response(JRPC* self, int id, cJSON* result) {
     cJSON_AddItemToObject(response, STR_RESULT_FIELD, result);
     cJSON_AddNumberToObject(response, STR_ID_FIELD, id);
     cJSON_AddNumberToObject(response, STR_TYPE_FIELD,
-                            TYPE_RESULT);  // 添加类型字段
+                            TYPE_RESULT);  // Add type field
 
     char* response_str = cJSON_Print(response);
-    printf("Response: %s\n", response_str);
+    printf("[Server] Response: %s\n", response_str);
 
     self->send(response_str);
 
-    free(response_str);
+    cJSON_free(response_str);
+    cJSON_Delete(response);
 }
 
-void JRPC_handle_request(JRPC* self, const char* json_str) {
+void JRPC_server_handle(JRPC* self, const char* json_str) {
     cJSON* json = cJSON_Parse(json_str);
     if (json == NULL) {
         printf("Error parsing JSON\n");
@@ -120,7 +158,7 @@ void JRPC_handle_request(JRPC* self, const char* json_str) {
     if (!cJSON_IsString(jsonrpc) || !cJSON_IsString(method) ||
         !cJSON_IsArray(params) || !cJSON_IsNumber(id) ||
         !cJSON_IsNumber(type)) {
-        printf("Invalid JSON RPC request format\n");
+        printf("[Server] Invalid JSON RPC request format: %s\n", json_str);
         cJSON_Delete(json);
         return;
     }
@@ -141,51 +179,48 @@ void JRPC_handle_request(JRPC* self, const char* json_str) {
     rpc_function_nonblocking func_nonblocking =
         JRPC_find_nonblocking_rpc_function(self, method->valuestring,
                                            &expected_param_count);
-    if (func_nonblocking != NULL) {
-        int param_count = cJSON_GetArraySize(params);
-        if (expected_param_count != PARAM_COUNT_NO_CHECK &&
-            param_count != expected_param_count) {
-            JRPC_send_acknowledgement(self, id->valueint, ACK_INVALID_PARAMS);
-            cJSON_Delete(json);
-            return;
-        }
+    rpc_function func = NULL;
+    int is_nonblocking = (func_nonblocking != NULL);
 
-        // 发送成功ACK
-        JRPC_send_acknowledgement(self, id->valueint, ACK_SUCCESS);
-
-        // 调用非阻塞函数
-        cJSON* param_array[param_count];
-        for (int i = 0; i < param_count; i++) {
-            param_array[i] = cJSON_GetArrayItem(params, i);
-        }
-
-        func_nonblocking(id->valueint, param_array, param_count, self);
-    } else {
-        rpc_function func = JRPC_find_rpc_function(self, method->valuestring,
-                                                   &expected_param_count);
+    if (!is_nonblocking) {
+        func = JRPC_find_rpc_function(self, method->valuestring,
+                                      &expected_param_count);
         if (func == NULL) {
-            JRPC_send_acknowledgement(self, id->valueint, ACK_METHOD_NOT_FOUND);
+            JRPC_send_acknowledgement(self, id->valueint, ACK_METHOD_NOT_FOUND,
+                                      "Server");
             cJSON_Delete(json);
             return;
         }
+    }
 
-        int param_count = cJSON_GetArraySize(params);
-        if (expected_param_count != PARAM_COUNT_NO_CHECK &&
-            param_count != expected_param_count) {
-            JRPC_send_acknowledgement(self, id->valueint, ACK_INVALID_PARAMS);
-            cJSON_Delete(json);
-            return;
-        }
+    int param_count = cJSON_GetArraySize(params);
+    if (expected_param_count != PARAM_COUNT_NO_CHECK &&
+        param_count != expected_param_count) {
+        JRPC_send_acknowledgement(self, id->valueint, ACK_INVALID_PARAMS,
+                                  "Server");
+        cJSON_Delete(json);
+        return;
+    }
+
+    char* ack_str = create_acknowledgement_string(id->valueint, ACK_SUCCESS);
+    if (JRPC_send_message_with_retry(self, ack_str, RETRY_COUNT, ACK_TIMEOUT,
+                                     id->valueint, TYPE_ACK, "Server") != 0) {
+        cJSON_Delete(json);
+        return;
+    }
 
-        // 发送成功ACK
-        JRPC_send_acknowledgement(self, id->valueint, ACK_SUCCESS);
+    if (ack_str) {
+        cJSON_free(ack_str);
+    }
 
-        // 调用函数
-        cJSON* param_array[param_count];
-        for (int i = 0; i < param_count; i++) {
-            param_array[i] = cJSON_GetArrayItem(params, i);
-        }
+    cJSON* param_array[param_count];
+    for (int i = 0; i < param_count; i++) {
+        param_array[i] = cJSON_GetArrayItem(params, i);
+    }
 
+    if (is_nonblocking) {
+        func_nonblocking(id->valueint, param_array, param_count, self);
+    } else {
         cJSON* result = func(param_array, param_count);
         JRPC_send_response(self, id->valueint, result);
     }
@@ -202,7 +237,7 @@ rpc_function JRPC_find_rpc_function(JRPC* self,
             return self->map[i].func;
         }
     }
-    return NULL;  // 没有找到对应的函数
+    return NULL;  // Function not found
 }
 
 rpc_function_nonblocking JRPC_find_nonblocking_rpc_function(JRPC* self,
@@ -214,15 +249,15 @@ rpc_function_nonblocking JRPC_find_nonblocking_rpc_function(JRPC* self,
             return self->nonblocking_map[i].func;
         }
     }
-    return NULL;  // 没有找到对应的函数
+    return NULL;  // Function not found
 }
 
-// 缓存添加函数
+// Cache add function
 void JRPC_cache_add(JRPC* self, cJSON* item) {
     if (self->cache_count < CACHE_SIZE) {
         self->cache[self->cache_count++] = item;
     } else {
-        // 缓存已满,删除最旧的
+        // Cache full, delete oldest
         cJSON_Delete(self->cache[0]);
         for (int i = 0; i < CACHE_SIZE - 1; i++) {
             self->cache[i] = self->cache[i + 1];
@@ -231,16 +266,18 @@ void JRPC_cache_add(JRPC* self, cJSON* item) {
     }
 }
 
-// 缓存获取函数
+// Cache get function
 cJSON* JRPC_cache_get(JRPC* self, int id, int type) {
     for (int i = 0; i < self->cache_count; i++) {
         cJSON* cached_json = self->cache[i];
-        cJSON* cached_id = cJSON_GetObjectItem(cached_json, STR_ID_FIELD);
+        cJSON* cached_id =
+
+            cJSON_GetObjectItem(cached_json, STR_ID_FIELD);
         cJSON* cached_type = cJSON_GetObjectItem(cached_json, STR_TYPE_FIELD);
         if (cached_id && cJSON_IsNumber(cached_id) &&
             cached_id->valueint == id && cached_type &&
             cJSON_IsNumber(cached_type) && cached_type->valueint == type) {
-            // 找到匹配项,返回并从缓存中删除
+            // Found match, return and remove from cache
             cJSON* result = cached_json;
             for (int j = i; j < self->cache_count - 1; j++) {
                 self->cache[j] = self->cache[j + 1];
@@ -253,17 +290,19 @@ cJSON* JRPC_cache_get(JRPC* self, int id, int type) {
 }
 
 cJSON* JRPC_receive_with_id_and_type(JRPC* self, int id, int type) {
-    // 先检查缓存
+    // Check cache first
     cJSON* cached_json = JRPC_cache_get(self, id, type);
     if (cached_json != NULL) {
         return cached_json;
     }
 
-    // 缓存中没有匹配项,从接收接口获取
+    // No match in cache, receive from interface
     char* received_str = self->receive();
     if (received_str != NULL) {
         cJSON* received_json = cJSON_Parse(received_str);
-        free(received_str);
+        if (self->receive_need_free) {
+            free(received_str);
+        }
         if (received_json != NULL) {
             cJSON* received_id =
                 cJSON_GetObjectItem(received_json, STR_ID_FIELD);
@@ -275,7 +314,7 @@ cJSON* JRPC_receive_with_id_and_type(JRPC* self, int id, int type) {
                 received_type->valueint == type) {
                 return received_json;
             } else {
-                // 缓存数据
+                // Cache data
                 JRPC_cache_add(self, received_json);
             }
         }
@@ -283,12 +322,44 @@ cJSON* JRPC_receive_with_id_and_type(JRPC* self, int id, int type) {
     return NULL;
 }
 
+static int JRPC_send_message_with_retry(JRPC* self,
+                                        const char* request_str,
+                                        int retry_count,
+                                        unsigned long ack_timeout,
+                                        int id,
+                                        int type,
+                                        const char* label) {
+    for (int retry = 0; retry < retry_count; retry++) {
+        printf("[%s] Send and await %s with retry [%d]: %s\n", label,
+               JRPC_type_2_string(type), retry, request_str);
+        self->send(request_str);
+
+        unsigned long start_time = self->tick();
+        cJSON* ack_json = NULL;
+        while (1) {
+            ack_json = JRPC_receive_with_id_and_type(self, id, type);
+            if (ack_json != NULL) {
+                printf("[%s] Received ACK, id: %d\n", label, id);
+                cJSON_Delete(ack_json);
+                return 0;  // Received correct ACK
+            }
+            if (self->tick() - start_time >= ack_timeout) {
+                printf("[%s] ACK timeout, retrying...\n", label);
+                break;
+            }
+            self->yield();  // Thread switch
+        }
+    }
+    printf("[%s] Failed to receive ACK after %d retries\n", label, retry_count);
+    return -1;  // Failed to receive correct ACK after retries
+}
+
 void JRPC_send_request_no_blocking(JRPC* self,
                                    const char* method,
                                    cJSON* params[],
                                    int param_count,
                                    rpc_callback callback) {
-    // 构建请求
+    // Build request
     int id = ++self->current_id;
     cJSON* request = cJSON_CreateObject();
     cJSON_AddStringToObject(request, STR_JSON_RPC_FIELD, STR_JSON_RPC_VERSION);
@@ -301,46 +372,21 @@ void JRPC_send_request_no_blocking(JRPC* self,
     cJSON_AddItemToObject(request, STR_PARAMS_FIELD, params_array);
     cJSON_AddNumberToObject(request, STR_ID_FIELD, id);
     cJSON_AddNumberToObject(request, STR_TYPE_FIELD,
-                            TYPE_REQUEST);  // 添加类型字段
+                            TYPE_REQUEST);  // Add type field
 
     char* request_str = cJSON_Print(request);
-    printf("Sending Request (no_blocking): %s\n", request_str);
-
-    // 发送请求并重试
-    int retry;
-    for (retry = 0; retry < RETRY_COUNT; retry++) {
-        self->send(request_str);
-
-        unsigned long start_time = self->tick();
-        cJSON* ack_json = NULL;
-        while (1) {
-            ack_json = JRPC_receive_with_id_and_type(self, id, TYPE_ACK);
-            if (ack_json != NULL) {
-                printf("Received ACK, id: %d\n", id);
-                cJSON_Delete(ack_json);
-                break;  // 收到正确的ACK
-            }
-            if (self->tick() - start_time >= ACK_TIMEOUT) {
-                printf("ACK timeout, retrying...\n");
-                break;
-            }
-            self->yield();  // 多线程切换
-        }
+    printf("[Client] Sending Request (no_blocking): %s\n", request_str);
 
-        if (ack_json != NULL) {
-            break;
-        }
+    if (JRPC_send_message_with_retry(self, request_str, RETRY_COUNT,
+                                     ACK_TIMEOUT, id, TYPE_ACK,
+                                     "Client") != 0) {
+        // If ACK received
+        callback(NULL);  // Simulate callback, no result
     }
 
-    // 如果收到ACK
-    if (retry < RETRY_COUNT) {
-        // 调用回调函数处理结果
-        callback(NULL);  // 模拟调用回调函数,不传递结果
-    } else {
-        printf("Failed to receive ACK after %d retries\n", RETRY_COUNT);
-    }
+    JRPC_send_acknowledgement(self, id, ACK_SUCCESS, "Client");
 
-    free(request_str);
+    cJSON_free(request_str);
     cJSON_Delete(request);
 }
 
@@ -348,7 +394,7 @@ cJSON* JRPC_send_request_blocking(JRPC* self,
                                   const char* method,
                                   cJSON* params[],
                                   int param_count) {
-    // 构建请求
+    // Build request
     int id = ++self->current_id;
     cJSON* request = cJSON_CreateObject();
     cJSON_AddStringToObject(request, STR_JSON_RPC_FIELD, STR_JSON_RPC_VERSION);
@@ -361,70 +407,54 @@ cJSON* JRPC_send_request_blocking(JRPC* self,
     cJSON_AddItemToObject(request, STR_PARAMS_FIELD, params_array);
     cJSON_AddNumberToObject(request, STR_ID_FIELD, id);
     cJSON_AddNumberToObject(request, STR_TYPE_FIELD,
-                            TYPE_REQUEST);  // 添加类型字段
+                            TYPE_REQUEST);  // Add type field
 
     char* request_str = cJSON_Print(request);
-    printf("Sending Request (blocking): %s\n", request_str);
-
-    cJSON* ack_json = NULL;
-    // 发送请求并重试
-    for (int retry = 0; retry < RETRY_COUNT; retry++) {
-        self->send(request_str);
+    printf("[Client] Sending Request (blocking): %s\n", request_str);
 
-        unsigned long start_time = self->tick();
-        while (1) {
-            ack_json = JRPC_receive_with_id_and_type(self, id, TYPE_ACK);
-            if (ack_json != NULL) {
-                cJSON_Delete(ack_json);
-                break;  // 收到正确的ACK
-            }
-            if (self->tick() - start_time >= ACK_TIMEOUT) {
-                printf("ACK timeout, retrying...\n");
-                break;
-            }
-            self->yield();  // 多线程切换
-        }
-
-        if (ack_json != NULL) {
-            break;
-        }
-    }
-
-    if (ack_json == NULL) {
-        printf("Failed to receive ACK after %d retries\n", RETRY_COUNT);
-        free(request_str);
+    if (JRPC_send_message_with_retry(self, request_str, RETRY_COUNT,
+                                     ACK_TIMEOUT, id, TYPE_ACK,
+                                     "Client") != 0) {
+        cJSON_free(request_str);
         cJSON_Delete(request);
         return NULL;
     }
-
-    printf("Received ACK: %d\n", id);
-
-    // 等待执行完毕响应
+    JRPC_send_acknowledgement(self, id, ACK_SUCCESS, "Client");
+    // Wait for response
     unsigned long start_time = self->tick();
     while (1) {
         cJSON* response_json =
             JRPC_receive_with_id_and_type(self, id, TYPE_RESULT);
         if (response_json != NULL) {
             cJSON_Delete(request);
-            free(request_str);
+            cJSON_free(request_str);
+            char* response_str = cJSON_Print(response_json);
+            printf("[Client] Received Response: %s\n", response_str);
+            cJSON_free(response_str);
             return response_json;
         }
         if (self->tick() - start_time >= BLOCKING_TIMEOUT) {
-            printf("Response timeout\n");
-            free(request_str);
+            printf("[Client] Response timeout\n");
+            cJSON_free(request_str);
             cJSON_Delete(request);
             return NULL;
         }
-        self->yield();  // 多线程切换
+        self->yield();  // Thread switch
     }
 }
 
-// 模拟发送函数
+// Mock send function with validation
+static char* mock_sent_message = NULL;
+
 static void mock_send(const char* message) {
-    printf("mock send: %s\n", message);
+    printf("[Mock] send: %s\n", message);
+    if (mock_sent_message) {
+        free(mock_sent_message);
+    }
+    mock_sent_message = strdup(message);  // Capture sent message
 }
 
-// 模拟接收函数(非阻塞)
+// Mock receive function (non-blocking)
 static char* mock_receive(void) {
     static int call_count = 0;
     call_count++;
@@ -446,15 +476,36 @@ static char* mock_receive(void) {
     }
 }
 
-// 模拟 yield 函数
+static char* mock_receive_server_test(void) {
+    static int call_count = 0;
+    call_count++;
+    switch (call_count) {
+        case 3:
+            return strdup(
+                "{\"jsonrpc\": \"1.0\", \"status\": \"received\", \"id\": 1, "
+                "\"type\": 1}");
+        case 6:
+            return strdup(
+                "{\"jsonrpc\": \"1.0\", \"status\": \"received\", \"id\": 2, "
+                "\"type\": 1}");
+        case 9:
+            return strdup(
+                "{\"jsonrpc\": \"1.0\", \"status\": \"received\", \"id\": 3, "
+                "\"type\": 1}");
+        default:
+            return NULL;
+    }
+}
+
+// Mock yield function
 static void mock_yield(void) {
-    printf("$");
+    printf("[Y]");
 }
 
-// 模拟 tick 函数
+// Mock tick function
 static unsigned long mock_tick_ms(void) {
     static unsigned long tick = 0;
-    tick += 100;  // 模拟每次调用增加100ms
+    tick += 100;  // Simulate 100ms per call
     return tick;
 }
 
@@ -463,30 +514,31 @@ static void result_callback(cJSON* result) {
            result ? cJSON_Print(result) : "No result");
 }
 
-int jrpc_base_test() {
+int jrpc_test_client() {
     int ret = 0;
     JRPC jrpc = {default_rpc_map,
                  default_nonblocking_rpc_map,
                  mock_send,
                  mock_receive,
+                 1,
                  mock_yield,
                  mock_tick_ms,
                  0,
                  {NULL},
                  0};
 
-    // 测试 no_blocking
+    // Test no_blocking
     cJSON* params1[] = {cJSON_CreateNumber(5), cJSON_CreateNumber(3)};
     JRPC_send_request_no_blocking(&jrpc, "add_nonblocking", params1, 2,
                                   result_callback);
 
-    // 测试 blocking
+    // Test blocking
     cJSON* params2[] = {cJSON_CreateNumber(5), cJSON_CreateNumber(3)};
     cJSON* response = JRPC_send_request_blocking(&jrpc, "add", params2, 2);
     char* call_result = cJSON_Print(response);
-    printf("Blocking call result: %s\n", call_result);
-    free(call_result);
-    // 计算结果应为 8
+    printf("[Client] Blocking call result: %s\n", call_result);
+    cJSON_free(call_result);
+    // Result should be 8
     if (response == NULL ||
         cJSON_GetObjectItem(response, "result")->valueint != 8) {
         ret = -1;
@@ -498,5 +550,93 @@ int jrpc_base_test() {
         cJSON_Delete(params2[i]);
     }
 
+    if (mock_sent_message) {
+        free(mock_sent_message);
+        mock_sent_message = NULL;
+    }
+
+    return ret;
+}
+
+int jrpc_compare_json_strings(const char* json_str1, const char* json_str2) {
+    cJSON* json1 = cJSON_Parse(json_str1);
+    cJSON* json2 = cJSON_Parse(json_str2);
+
+    if (json1 == NULL || json2 == NULL) {
+        if (json1) {
+            cJSON_Delete(json1);
+        } else {
+            printf("json1 is NULL\n");
+        }
+        if (json2) {
+            cJSON_Delete(json2);
+        } else {
+            printf("json2 is NULL\n");
+        }
+        return -1;
+    }
+
+    int result = cJSON_Compare(json1, json2, 1) ? 0 : -1;
+    if (0 != result) {
+        printf("Json compare failed\n");
+        printf("json1: %s\n", json_str1);
+        printf("json2: %s\n", json_str2);
+    }
+
+    cJSON_Delete(json1);
+    cJSON_Delete(json2);
+
+    return result;
+}
+
+int jrpc_validate_response(const char* expected_response) {
+    if (mock_sent_message == NULL) {
+        return -1;
+    }
+
+    return jrpc_compare_json_strings(mock_sent_message, expected_response);
+}
+
+int jrpc_test_server() {
+    JRPC jrpc = {default_rpc_map,
+                 default_nonblocking_rpc_map,
+                 mock_send,
+                 mock_receive_server_test,
+                 1,
+                 mock_yield,
+                 mock_tick_ms,
+                 0,
+                 {NULL},
+                 0};
+
+    const char* requests[] = {
+        "{\"jsonrpc\": \"1.0\", \"method\": \"add\", \"params\": [5, 3], "
+        "\"id\": 1, \"type\": 0}",
+        "{\"jsonrpc\": \"1.0\", \"method\": \"subtract\", \"params\": [10, 4], "
+        "\"id\": 2, \"type\": 0}",
+        "{\"jsonrpc\": \"1.0\", \"method\": \"add_nonblocking\", \"params\": "
+        "[2, 2], \"id\": 3, \"type\": 0}",
+    };
+
+    const char* expected_responses[] = {
+        "{\"jsonrpc\": \"1.0\", \"result\": 8, \"id\": 1, \"type\": 2}",
+        "{\"jsonrpc\": \"1.0\", \"result\": 6, \"id\": 2, \"type\": 2}",
+        "{\"jsonrpc\": \"1.0\", \"result\": 4, \"id\": 3, \"type\": 2}"};
+
+    int ret = 0;
+
+    for (int i = 0; i < sizeof(requests) / sizeof(requests[0]); i++) {
+        JRPC_server_handle(&jrpc, requests[i]);
+        if (jrpc_validate_response(expected_responses[i]) != 0) {
+            ret = -1;
+            break;
+        }
+    }
+
+    if (mock_sent_message) {
+        free(mock_sent_message);
+        mock_sent_message = NULL;
+    }
+
     return ret;
 }

+ 31 - 41
port/linux/package/pikascript/pikascript-lib/jrpc/jrpc.h

@@ -1,37 +1,14 @@
-/*
- * This file is part of the PikaPython project.
- * http://github.com/pikastech/pikapython
- *
- * MIT License
- *
- * Copyright (c) 2024 lyon liang6516@outlook.com
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-// jrpc.h
 #ifndef JRPC_H
 #define JRPC_H
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #include "cJSON.h"
+#include <stdint.h>
 
-// 宏定义字符串常量
+// Define string constants
 #define STR_JSON_RPC_VERSION "1.0"
 #define STR_JSON_RPC_FIELD "jsonrpc"
 #define STR_METHOD_FIELD "method"
@@ -48,19 +25,23 @@
     { NULL, NULL, 0 }
 #define PARAM_COUNT_NO_CHECK -1
 
-// 超时宏定义
+// Timeout definitions
 #define ACK_TIMEOUT 1000
-#define BLOCKING_TIMEOUT 5000
+#define BLOCKING_TIMEOUT 20000
 #define RETRY_COUNT 5
 
-// 数据包类型的宏定义
+// Packet type definitions
 #define TYPE_REQUEST 0
 #define TYPE_ACK 1
 #define TYPE_RESULT 2
 
+#define STR_TYPE_REQUEST "REQ"
+#define STR_TYPE_ACK "ACK"
+#define STR_TYPE_RESULT "RES"
+
 #define STR_TYPE_FIELD "type"
 
-// 缓存数组大小
+// Cache array size
 #define CACHE_SIZE 16
 
 typedef enum {
@@ -80,13 +61,13 @@ typedef int (*rpc_function_nonblocking)(int id,
 typedef struct {
     const char* name;
     rpc_function func;
-    int param_count;  // 参数数量
+    int param_count;  // Number of parameters
 } rpc_mapping;
 
 typedef struct {
     const char* name;
     rpc_function_nonblocking func;
-    int param_count;  // 参数数量
+    int param_count;  // Number of parameters
 } rpc_mapping_nonblocking;
 
 typedef void (*rpc_callback)(cJSON* result);
@@ -100,16 +81,16 @@ struct JRPC_ {
     rpc_mapping_nonblocking* nonblocking_map;
     send_function send;
     receive_function receive;
+    uint8_t receive_need_free;
     yield_function yield;
     tick_function tick;
     int current_id;
-    cJSON* cache[CACHE_SIZE];  // 添加缓存
+    cJSON* cache[CACHE_SIZE];  // Add cache
     int cache_count;
 };
 
-// 函数声明
-void JRPC_send_acknowledgement(JRPC* self, int id, ack_status status);
-void JRPC_handle_request(JRPC* self, const char* json_str);
+// Function declarations
+void JRPC_server_handle(JRPC* self, const char* json_str);
 rpc_function JRPC_find_rpc_function(JRPC* self,
                                     const char* name,
                                     int* param_count);
@@ -128,6 +109,15 @@ cJSON* JRPC_send_request_blocking(JRPC* self,
                                   int param_count);
 
 cJSON* JRPC_receive_with_id_and_type(JRPC* self, int id, int type);
+int jrpc_validate_response(const char* expected_response);
+int jrpc_compare_json_strings(const char* json_str1, const char* json_str2);
+int jrpc_validate_response(const char* expected_response);
+
+int jrpc_test_client();
+int jrpc_test_server();
+
+#ifdef __cplusplus
+}
+#endif
 
-int jrpc_base_test();
-#endif  // jrpc.h
+#endif  // JRPC_H

+ 279 - 2
port/linux/test/module-test.cpp

@@ -1,4 +1,7 @@
 #include "test_common.h"
+#include <thread>
+#include <atomic>
+#include <semaphore.h>
 TEST_START
 #if PIKA_SYNTAX_IMPORT_EX_ENABLE
 TEST(module, cmodule_import) {
@@ -901,8 +904,282 @@ TEST_RUN_SINGLE_FILE(lvgl,
 TEST_RUN_SINGLE_FILE(lvgl, lv_tim, "test/python/pika_lvgl/lv_tim.py");
 TEST_RUN_SINGLE_FILE(lvgl, lv_uidemo, "test/python/pika_lvgl/lv_uidemo.py");
 
-TEST(jrpc, base) {
-    ASSERT_EQ(jrpc_base_test(), 0);
+TEST(jrpc, client) {
+    ASSERT_EQ(jrpc_test_client(), 0);
+}
+
+TEST(jrpc, server) {
+    ASSERT_EQ(jrpc_test_server(), 0);
+}
+
+// Mock functions for testing
+static char* mock_receive_message = NULL;
+static char* mock_sent_message = NULL;
+
+void mock_send(const char* message) {
+    if (mock_sent_message) {
+        free(mock_sent_message);
+    }
+    mock_sent_message = strdup(message);
+}
+
+char* mock_receive(void) {
+    if (mock_receive_message) {
+        char* temp = strdup(mock_receive_message);
+        free(mock_receive_message);
+        mock_receive_message = NULL;
+        return temp;
+    }
+    return NULL;
+}
+
+void mock_yield(void) {
+    sched_yield();
+}
+static unsigned long mock_tick_ms(void) {
+    return pika_platform_get_tick();
+}
+
+rpc_mapping gtest_rpc_map[] = {{"add",
+                                [](cJSON* params[], int param_count) -> cJSON* {
+                                    int a = params[0]->valueint;
+                                    int b = params[1]->valueint;
+                                    return cJSON_CreateNumber(a + b);
+                                },
+                                2},
+                               RPC_MAP_END};
+
+rpc_mapping_nonblocking gtest_nonblocking_rpc_map[] = {RPC_MAP_END};
+
+TEST(jrpc, InvalidJSONFormat) {
+    JRPC jrpc = {gtest_rpc_map,
+                 gtest_nonblocking_rpc_map,
+                 mock_send,
+                 mock_receive,
+                 1,
+                 mock_yield,
+                 mock_tick_ms,
+                 0,
+                 {NULL},
+                 0};
+    const char* request =
+        "{\"jsonrpc\": \"1.0\", \"method\": \"add\", \"params\": [3, 4], "
+        "\"id\": 1, \"type\": ";  // Invalid JSON
+    mock_sent_message = NULL;
+
+    JRPC_server_handle(&jrpc, request);
+
+    ASSERT_EQ(mock_sent_message, nullptr);
+
+    if (NULL != mock_sent_message) {
+        free(mock_sent_message);
+        mock_sent_message = NULL;
+    }
+}
+
+TEST(jrpc, MethodNotFound) {
+    JRPC jrpc = {gtest_rpc_map,
+                 gtest_nonblocking_rpc_map,
+                 mock_send,
+                 mock_receive,
+                 1,
+                 mock_yield,
+                 mock_tick_ms,
+                 0,
+                 {NULL},
+                 0};
+    const char* request =
+        "{\"jsonrpc\": \"1.0\", \"method\": \"subtract\", \"params\": [3, 4], "
+        "\"id\": 1, \"type\": 0}";
+    const char* expected_response =
+        "{\"jsonrpc\": \"1.0\", \"status\": \"method not found\", \"id\": 1, "
+        "\"type\": 1}";
+
+    JRPC_server_handle(&jrpc, request);
+
+    ASSERT_EQ(jrpc_compare_json_strings(mock_sent_message, expected_response),
+              0);
+    if (NULL != mock_sent_message) {
+        free(mock_sent_message);
+        mock_sent_message = NULL;
+    }
+}
+
+TEST(jrpc, ParameterCountMismatch) {
+    JRPC jrpc = {gtest_rpc_map,
+                 gtest_nonblocking_rpc_map,
+                 mock_send,
+                 mock_receive,
+                 1,
+                 mock_yield,
+                 mock_tick_ms,
+                 0,
+                 {NULL},
+                 0};
+    const char* request =
+        "{\"jsonrpc\": \"1.0\", \"method\": \"add\", \"params\": [3], "
+        "\"id\": 1, \"type\": 0}";
+    const char* expected_response =
+        "{\"jsonrpc\": \"1.0\", \"status\": \"invalid params\", \"id\": 1, "
+        "\"type\": 1}";
+
+    JRPC_server_handle(&jrpc, request);
+
+    ASSERT_EQ(jrpc_compare_json_strings(mock_sent_message, expected_response),
+              0);
+    if (NULL != mock_sent_message) {
+        free(mock_sent_message);
+        mock_sent_message = NULL;
+    }
+}
+
+#if 0
+TEST(jrpc, InvalidParameterType) {
+    JRPC jrpc = {gtest_rpc_map,
+                 gtest_nonblocking_rpc_map,
+                 mock_send,
+                 mock_receive,
+                 1, mock_yield,
+                 mock_tick_ms,
+                 0,
+                 {NULL},
+                 0};
+    const char* request =
+        "{\"jsonrpc\": \"1.0\", \"method\": \"add\", \"params\": [3, \"a\"], "
+        "\"id\": 1, \"type\": 0}";
+    const char* expected_response =
+        "{\"jsonrpc\": \"1.0\", \"status\": \"invalid params\", \"id\": 1, "
+        "\"type\": 1}";
+
+    JRPC_server_handle(&jrpc, request);
+
+    ASSERT_EQ(jrpc_compare_json_strings(mock_sent_message, expected_response),
+              0);
+    if (NULL != mock_sent_message) {
+        free(mock_sent_message);
+        mock_sent_message = NULL;
+    }
+}
+#endif
+
+// Buffer for communication between client and server
+static char* server_receive_buffer = NULL;
+static char* client_receive_buffer = NULL;
+
+// Server send function
+static void jrpc_server_send(const char* message) {
+    if (client_receive_buffer) {
+        free(client_receive_buffer);
+    }
+    client_receive_buffer = strdup(message);
+}
+
+// Server receive function
+static char* jrpc_server_receive(void) {
+    if (server_receive_buffer) {
+        char* temp = strdup(server_receive_buffer);
+        free(server_receive_buffer);
+        server_receive_buffer = NULL;
+        return temp;
+    }
+    return NULL;
+}
+
+// Client send function
+static void jrpc_client_send(const char* message) {
+    if (server_receive_buffer) {
+        free(server_receive_buffer);
+    }
+    server_receive_buffer = strdup(message);
+}
+
+// Client receive function
+static char* jrpc_client_receive(void) {
+    if (client_receive_buffer) {
+        char* temp = strdup(client_receive_buffer);
+        free(client_receive_buffer);
+        client_receive_buffer = NULL;
+        return temp;
+    }
+    return NULL;
+}
+
+// Global atomic variable to signal the server to stop
+std::atomic<bool> server_running(true);
+
+// Function to periodically call JRPC_server_handle
+static void server_handle(JRPC* server) {
+    while (server_running) {
+        char* message = jrpc_server_receive();
+        if (NULL != message) {
+            JRPC_server_handle(server, message);
+            free(message);
+        }
+        std::this_thread::sleep_for(std::chrono::milliseconds(
+            10));  // Adjust the sleep duration as needed
+    }
+}
+
+// Test case
+TEST(jrpc, BlockingRequestBetweenTwoJRPC) {
+    // Server JRPC
+    JRPC server = {gtest_rpc_map,
+                   gtest_nonblocking_rpc_map,
+                   jrpc_server_send,
+                   jrpc_server_receive,
+                   1,
+                   mock_yield,
+                   mock_tick_ms,
+                   0,
+                   {NULL},
+                   0};
+
+    // Client JRPC
+    JRPC client = {gtest_rpc_map,
+                   gtest_nonblocking_rpc_map,
+                   jrpc_client_send,
+                   jrpc_client_receive,
+                   1,
+                   mock_yield,
+                   mock_tick_ms,
+                   0,
+                   {NULL},
+                   0};
+
+    // Create a thread to run server_handle
+    std::thread server_thread(server_handle, &server);
+
+    // Client sends request to Server
+    const char* request_method = "add";
+    cJSON* params[] = {cJSON_CreateNumber(10), cJSON_CreateNumber(20)};
+    cJSON* response =
+        JRPC_send_request_blocking(&client, request_method, params, 2);
+
+    // Verify that client received correct response
+    ASSERT_EQ(cJSON_GetObjectItem(response, "result")->valueint, 30);
+
+    // Clean up cJSON objects
+    if (response != NULL) {
+        cJSON_Delete(response);
+    }
+    for (int i = 0; i < 2; i++) {
+        cJSON_Delete(params[i]);
+    }
+
+    // Signal the server to stop and wait for the server thread to finish
+    server_running = false;
+    server_thread.join();
+
+    // Clean up mock_sent_message
+    if (NULL != mock_sent_message) {
+        free(mock_sent_message);
+        mock_sent_message = NULL;
+    }
+
+    if (NULL != server_receive_buffer) {
+        free(server_receive_buffer);
+        server_receive_buffer = NULL;
+    }
 }
 
 TEST_END

+ 1 - 1
src/PikaVersion.h

@@ -2,4 +2,4 @@
 #define PIKA_VERSION_MINOR 13
 #define PIKA_VERSION_MICRO 3
 
-#define PIKA_EDIT_TIME "2024/07/04 17:32:37"
+#define PIKA_EDIT_TIME "2024/07/05 00:27:26"