C/C++知识点之C语言JSON-RPC
小标 2018-12-03 来源 : 阅读 1584 评论 0

摘要:本文主要向大家介绍了C/C++知识点之C语言JSON-RPC,通过具体的内容向大家展示,希望对大家学习C/C++知识点有所帮助。

本文主要向大家介绍了C/C++知识点之C语言JSON-RPC,通过具体的内容向大家展示,希望对大家学习C/C++知识点有所帮助。

     近期对json-rpc比較感兴趣,思想非常easy,并且看到了非常多不同语言的实现。在github上 hmngomes 的 json-rpc-c (实现的是server端,基于TCP流),短小精悍,提供了非常好的框架。代码十分清晰。易于扩展,并且代码easy看懂,非常经典。该实现依赖于其他两个库 libev 和 cJSON。值得认真学习。
     測试的时候先启动server,而后通过 nc 命令发送对应的json格式数据,就会有对应的效果:
vonzhou@de15:~$ echo "{\"method\":\"sayHello\"}" | nc localhost 1234
{
     "result":     "Hello!"
}
vonzhou@de15:~$ echo "{\"method\":\"exit\"}" | nc localhost 1234
{
     "result":     "Bye!"
}




以下贴出代码,便于温习。
-------------------server.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "jsonrpc-c.h"


#define PORT 1234  //
 the port users will be connecting to


struct jrpc_server my_server;


cJSON * say_hello( jrpc_context *
 ctx, cJSON * params, cJSON *id) {
      return cJSON_CreateString( "Hello!" );
}


cJSON * exit_server( jrpc_context *
 ctx, cJSON * params, cJSON *id) {
     jrpc_server_stop(&my_server);
      return cJSON_CreateString( "Bye!" );
}


int main( void )
 {
     jrpc_server_init(&my_server, PORT);
     //注冊俩方法
     jrpc_register_procedure(&my_server, say_hello, "sayHello" ,
 NULL );
     jrpc_register_procedure(&my_server, exit_server, "exit" ,
 NULL );
     jrpc_server_run(&my_server);
     jrpc_server_destroy(&my_server);
      return 0;
}



-----------------json-rpc.h

#ifndef JSONRPCC_H_
#define JSONRPCC_H_


#include "cJSON.h"
#include 


/*
 *
 * //www.jsonrpc.org/specification
 *
 * code   message   meaning
 * -32700 Parse error   Invalid JSON was received by the server.
 * An error occurred on the server while parsing the JSON text.
 * -32600 Invalid Request    The JSON sent is not a valid Request object.
 * -32601 Method not found   The method does not exist / is not available.
 * -32602 Invalid paramsInvalid method parameter(s).
 * -32603 Internal errorInternal JSON-RPC error.
 * -32000 to -32099Server error  Reserved for implementation-defined server-errors.
 */


#define JRPC_PARSE_ERROR -32700
#define JRPC_INVALID_REQUEST -32600
#define JRPC_METHOD_NOT_FOUND -32601
#define JRPC_INVALID_PARAMS -32603
#define JRPC_INTERNAL_ERROR -32693




typedef struct {
      void * data;
      int error_code;
      char * error_message ;
} jrpc_context;


//JSON方法类型
typedef cJSON*
 (* jrpc_function )(jrpc_context *context, cJSON *params, cJSON*
 id);


struct jrpc_procedure {
      char * name;
  //方法名
      jrpc_function function;
  //方法地址
      void * data;
  //额外信息
};




struct jrpc_server {
      int port_number;
      struct ev_loop
 * loop;  //eventloop类型
      ev_io listen_watcher ;
  //
      int procedure_count;
  
      struct jrpc_procedure
 * procedures ;
      int debug_level;
};


struct jrpc_connection {
      struct ev_io io;
      int fd;
      int pos; //记录在buffer中的位置
      unsigned int buffer_size ;
      char * buffer;
      int debug_level;
};


int jrpc_server_init( struct jrpc_server
 *server, int port_number);


int jrpc_server_init_with_ev_loop( struct jrpc_server
 *server,
        int port_number, struct ev_loop
 *loop);


static int __jrpc_server_start (struct jrpc_server
 *server);


void jrpc_server_run( struct jrpc_server
 *server);


int jrpc_server_stop( struct jrpc_server
 *server);


void jrpc_server_destroy( struct jrpc_server
 *server);


static void jrpc_procedure_destroy (struct jrpc_procedure
 *procedure);


int jrpc_register_procedure( struct jrpc_server
 *server,
           jrpc_function function_pointer, char *name, void *data);


int jrpc_deregister_procedure( struct jrpc_server
 *server, char *name);


#endif

 
-----------------json-rpc.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#include "jsonrpc-c.h"


struct ev_loop *loop;


// get sockaddr, IPv4 or IPv6:
static void *get_in_addr( struct sockaddr
 *sa) {
      if (sa-> sa_family == AF_INET)
 {
           return &(((struct sockaddr_in*)
 sa)->sin_addr );
     }
      return &((( struct sockaddr_in6*)
 sa)->sin6_addr);
}


static int send_response( struct jrpc_connection
 * conn, char *response) {
      int fd
 = conn-> fd;
      if (conn-> debug_level >
 1)
           printf ("JSON
 Response:\n%s\n" , response);
      write(fd,
 response, strlen (response));
      write(fd, "\n" ,
 1);
      return 0;
}


static int send_error( struct jrpc_connection
 * conn, int code, char *
 message,
           cJSON * id) {
      int return_value
 = 0;
      cJSON *result_root = cJSON_CreateObject();
      cJSON *error_root = cJSON_CreateObject();
     cJSON_AddNumberToObject(error_root, "code" ,
 code);
     cJSON_AddStringToObject(error_root, "message" ,
 message);
     cJSON_AddItemToObject(result_root, "error" ,
 error_root);
     cJSON_AddItemToObject(result_root, "id" ,
 id);
      char *
 str_result = cJSON_Print(result_root);
     return_value = send_response(conn, str_result);
      free(str_result);
     cJSON_Delete(result_root);
      free(message);
      return return_value;
}


static int send_result( struct jrpc_connection
 * conn, cJSON * result,
           cJSON * id) {
      int return_value
 = 0;
      cJSON *result_root = cJSON_CreateObject();
      if (result)
          cJSON_AddItemToObject(result_root, "result" ,
 result);
     cJSON_AddItemToObject(result_root, "id" ,
 id);


      char *
 str_result = cJSON_Print(result_root);
     return_value = send_response(conn, str_result);
      free(str_result);
     cJSON_Delete(result_root);
      return return_value;
}


static int invoke_procedure( struct jrpc_server
 *server,
           struct jrpc_connection
 * conn, char *name, cJSON *params, cJSON *id)
 {
      cJSON *returned = NULL;
      int procedure_found
 = 0;
      jrpc_context ctx;
     ctx. error_code =
 0;
     ctx. error_message =
 NULL;
      int i
 = server-> procedure_count ;
      while (i--)
 {
           if (!strcmp (server-> procedures[i]. name,
 name)) {
              procedure_found = 1;
              ctx. data =
 server-> procedures [i].data ;
              returned = server->procedures [i].function (&ctx,
 params, id);
               break ;
          }
     }
      if (!procedure_found)
           return send_error(conn, JRPC_METHOD_NOT_FOUND,
                    strdup ("Method
 not found." ), id);
      else {
           if (ctx.error_code )
               return send_error(conn,
 ctx.error_code , ctx.error_message ,
 id);
           else
               return send_result(conn,
 returned, id);
     }
}


static int eval_request (struct jrpc_server
 *server,
           struct jrpc_connection
 * conn, cJSON *root) {
      cJSON *method, *params, *id;
     method = cJSON_GetObjectItem(root, "method" );
      if (method
 != NULL && method-> type == cJSON_String ) {
          params = cJSON_GetObjectItem(root, "params ");
           if (params
 == NULL|| params->type == cJSON_Array
          || params-> type == cJSON_Object )
 {
              id = cJSON_GetObjectItem(root, "id" );
               if (id
 == NULL|| id->type == cJSON_String
              || id-> type == cJSON_Number )
 {
               //We have to copy ID because using
 it on the reply and deleting the response Object will also delete ID
                    cJSON * id_copy = NULL;
                    if (id
 != NULL)
                        id_copy =
                                  (id-> type == cJSON_String)
 ? cJSON_CreateString(
                                           id-> valuestring) :
                                           cJSON_CreateNumber(id-> valueint);
                    if (server->debug_level )
                         printf ("Method
 Invoked: %s\n" , method->valuestring);
                    return invoke_procedure(server,
 conn, method->valuestring,
                             params, id_copy);
              }
          }
     }
     send_error(conn, JRPC_INVALID_REQUEST,
               strdup ("The
 JSON sent is not a valid Request object."), NULL);
      return -1;
}


static void close_connection( struct ev_loop
 *loop, ev_io *w) {
     ev_io_stop(loop, w);
      close((( struct jrpc_connection
 *) w)->fd );
      free((( struct jrpc_connection
 *) w)->buffer );
      free((( struct jrpc_connection
 *) w));
}


static void connection_cb( struct ev_loop
 *loop, ev_io *w, int revents)
 {
      struct jrpc_connection
 *conn;
      struct jrpc_server
 *server = ( struct jrpc_server *) w->data;
      size_t bytes_read
 = 0;
      //get our ‘subclassed‘
 event watcher
     conn = ( struct jrpc_connection
 *) w;
      int fd
 = conn-> fd;
      // 为这个session 分配的buffer满了,须要又一次分配空间,成倍增长
      if (conn-> pos ==
 (conn-> buffer_size - 1)) {
           char *
 new_buffer = realloc(conn-> buffer,
 conn->buffer_size *= 2);
           if (new_buffer
 == NULL) {
               perror ("Memory
 error" );
               return close_connection(loop,
 w);
          }
          conn-> buffer =
 new_buffer;
           //把后来增长的空间置空
           memset (conn->buffer +
 conn-> pos, 0, conn->buffer_size -
 conn->pos );
     }
      // can not fill the entire buffer, string must
 be NULL terminated
      int max_read_size
 = conn-> buffer_size - conn->pos -
 1;
      //从套接字中读取数据。-1 说明异常终止
      if ((bytes_read
 = read (fd, conn->buffer +
 conn-> pos, max_read_size))
              == -1) {
           perror ("read" );
           return close_connection(loop,
 w);
     }
      //为0说明client关闭连接
      if (!bytes_read)
 {
           // client closed the sending half of the
 connection
           if (server->debug_level )
               printf ("Client
 closed connection.\n" );
           return close_connection(loop,
 w);
     } else {
           cJSON *root;
           char *end_ptr
 = NULL;
          conn-> pos +=
 bytes_read;


           if ((root
 = cJSON_Parse_Stream(conn->buffer , &end_ptr)) != NULL)
 {
               if (server->debug_level >
 1) {
                    char *
 str_result = cJSON_Print(root);
                    printf ("Valid
 JSON Received:\n%s\n" , str_result);
                    free (str_result);
              }


               if (root->type == cJSON_Object)
 {
                    eval_request(server, conn, root);
              }
               //shift processed request, discarding
 it
               memmove (conn->buffer ,
 end_ptr, strlen (end_ptr) + 2);


              conn-> pos = strlen (end_ptr);
               memset (conn->buffer +
 conn-> pos, 0,
                        conn-> buffer_size -
 conn->pos - 1);


              cJSON_Delete(root);
          } else {
               // did we parse the all buffer? If
 so, just wait for more.
               // else there was an error before the
 buffer‘s end
               if (end_ptr
 != (conn->buffer + conn-> pos))
 {
                    if (server->debug_level )
 {
                         printf ("INVALID
 JSON Received:\n---\n%s\n---\n",
                                  conn-> buffer);
                   }
                   send_error(conn, JRPC_PARSE_ERROR,
                              strdup (
                                       "Parse error.
 Invalid JSON was received by the server."),
                             NULL);
                    return close_connection(loop,
 w);
              }
          }
     }


}


static void accept_cb( struct ev_loop
 *loop, ev_io *w, int revents)
 {
      char s[ INET6_ADDRSTRLEN];
      struct jrpc_connection
 *connection_watcher;
     connection_watcher = malloc (sizeof ( struct jrpc_connection));
      //通用socket addr
      struct sockaddr_storage
 their_addr; // connector‘s address information
      socklen_t sin_size;
     sin_size = sizeof their_addr;
     connection_watcher-> fd =
 accept(w-> fd, ( struct sockaddr
 *) &their_addr,
              &sin_size);
      if (connection_watcher-> fd ==
 -1) {
           perror ("accept" );
           free (connection_watcher);
     } else {
           if (((struct jrpc_server
 *) w->data)-> debug_level)
 {
              inet_ntop(their_addr. ss_family ,
                        get_in_addr(( struct sockaddr
 *) &their_addr), s, sizeof s);
               printf ("server:
 got connection from %s\n" , s);
          }
          ev_io_init(&connection_watcher-> io,
 connection_cb,
                   connection_watcher-> fd, EV_READ);
           //copy pointer to struct jrpc_server
          connection_watcher-> io. data =
 w->data ;
          connection_watcher-> buffer_size =
 1500;
          connection_watcher-> buffer = malloc (1500);
           memset (connection_watcher->buffer ,
 0, 1500);
          connection_watcher-> pos =
 0;
           //copy debug_level, struct jrpc_connection
 has no pointer to struct jrpc_server
          connection_watcher-> debug_level =
                   (( struct jrpc_server
 *) w->data)-> debug_level;
          ev_io_start(loop, &connection_watcher-> io);
     }
}


int jrpc_server_init( struct jrpc_server
 *server, int port_number) {
    loop = EV_DEFAULT;
    return jrpc_server_init_with_ev_loop(server,
 port_number, loop);
}


int jrpc_server_init_with_ev_loop( struct jrpc_server
 *server,
        int port_number, struct ev_loop
 *loop) {
      memset(server,
 0, sizeof (struct jrpc_server));
     server-> loop =
 loop;
     server-> port_number =
 port_number;
      char *
 debug_level_env = getenv ("JRPC_DEBUG" );
      if (debug_level_env
 == NULL)
          server-> debug_level =
 0;
      else {
          server-> debug_level = strtol (debug_level_env,
 NULL, 10);
           printf ("JSONRPC-C
 Debug level %d\n" , server->debug_level );
     }
      return __jrpc_server_start(server);
}


static int __jrpc_server_start (struct jrpc_server
 *server) {
      int sockfd;
      struct addrinfo
 hints, *servinfo, *p;
      struct sockaddr_in
 sockaddr;
      int len;
      int yes
 = 1;
      int rv;
      char PORT[6];
      sprintf(PORT, "%d" ,
 server->port_number );
      memset(&hints,
 0, sizeof hints);
     hints. ai_family = AF_UNSPEC;
     hints. ai_socktype = SOCK_STREAM;
     hints. ai_flags = AI_PASSIVE; //
 use my IP


      if ((rv
 = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
           fprintf (stderr, " getaddrinfo:
 %s\n" , gai_strerror(rv));
           return 1;
     }


// loop through all the results and bind to the first we can
      for (p
 = servinfo; p != NULL; p = p-> ai_next) {
           if ((sockfd
 = socket(p->ai_family , p->ai_socktype ,
 p->ai_protocol ))
                   == -1) {
               perror ("server:
 socket" );
               continue ;
          }


           if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
 &yes, sizeof (int ))
                   == -1) {
               perror (" setsockopt" );
               exit (1);
          }


           if (bind(sockfd,
 p->ai_addr , p->ai_addrlen )
 == -1) {
               close (sockfd);
               perror ("server:
 bind" );
               continue ;
          }


          len = sizeof (sockaddr);
           if (getsockname(sockfd,
 ( struct sockaddr *) &sockaddr, &len)
 == -1) {
               close (sockfd);
               perror ("server: getsockname" );
               continue ;
          }
          server-> port_number =
 ntohs( sockaddr.sin_port );


           break ;
     }


      if (p
 == NULL) {
           fprintf (stderr, "server:
 failed to bind\n" );
           return 2;
     }


     freeaddrinfo(servinfo); // all done with this
 structure


      if (listen(sockfd,
 5) == -1) {
           perror ("listen" );
           exit (1);
     }
      if (server-> debug_level )
           printf ("server:
 waiting for connections...\n" );


     ev_io_init(&server-> listen_watcher ,
 accept_cb, sockfd, EV_READ);
     server-> listen_watcher .data =
 server;
     ev_io_start(server-> loop,
 &server-> listen_watcher );
      return 0;
}


// Make the code work with both the old (ev_loop/ev_unloop)
// and new (ev_run/ev_break) versions of libev.
#ifdef EVUNLOOP_ALL
  #define EV_RUN
 ev_loop
  #define EV_BREAK
 ev_unloop
  #define EVBREAK_ALL
 EVUNLOOP_ALL
#else
  #define EV_RUN
 ev_run
  #define EV_BREAK
 ev_break
#endif


void jrpc_server_run( struct jrpc_server
 *server){
     EV_RUN(server-> loop,
 0);
}


int jrpc_server_stop( struct jrpc_server
 *server) {
     EV_BREAK(server-> loop, EVBREAK_ALL);
      return 0;
}


void jrpc_server_destroy( struct jrpc_server
 *server){
      /* Don‘t destroy server */
      int i;
      for (i
 = 0; i < server-> procedure_count ; i++){
          jrpc_procedure_destroy( &(server-> procedures [i])
 );
     }
      free(server-> procedures );
}


static void jrpc_procedure_destroy (struct jrpc_procedure
 *procedure){
      if (procedure-> name){
           free (procedure->name );
          procedure-> name =
 NULL;
     }
      if (procedure-> data){
           free (procedure->data );
          procedure-> data =
 NULL;
     }
}


int jrpc_register_procedure( struct jrpc_server
 *server,
           jrpc_function function_pointer, char *name, void *
 data) {
      int i
 = server-> procedure_count ++;
      if (!server-> procedures )
          server-> procedures = malloc ( sizeof( struct jrpc_procedure));
      else {
           //每次添加一个jrpc_procedure空间
           struct jrpc_procedure
 * ptr = realloc (server->procedures ,
                    sizeof (struct jrpc_procedure)
 * server->procedure_count );
           if (!ptr)
               return -1;
          server-> procedures =
 ptr;


     }
      //name是局部变量,所以要 strdup
      if ((server-> procedures [i].name = strdup(name))
 == NULL)
           return -1;
     server-> procedures [i].function =
 function_pointer;
     server-> procedures [i].data =
 data;
      return 0;
}


int jrpc_deregister_procedure( struct jrpc_server
 *server, char *name) {
      /* Search the procedure to deregister */
      int i;
      int found
 = 0;
      if (server-> procedures ){
           for (i
 = 0; i < server->procedure_count ; i++){
               if (found)
                   server-> procedures [i-1]
 = server->procedures [i];
               else if (! strcmp(name,
 server->procedures [i].name )){
                   found = 1;
                   jrpc_procedure_destroy( &(server->procedures [i])
 );
              }
          }
           if (found){
              server-> procedure_count --;
               if (server->procedure_count ){
                    struct jrpc_procedure
 * ptr = realloc (server->procedures ,
                         sizeof (struct jrpc_procedure)
 * server->procedure_count );
                    if (!ptr){
                         perror (" realloc" );
                         return -1;
                   }
                   server-> procedures =
 ptr;
              } else {
                   server-> procedures =
 NULL;
              }
          }
     } else {
           fprintf (stderr, "server
 : procedure ‘%s‘ not found\n", name);
           return -1;
     }
      return 0;
}

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标编程语言C/C+频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved