C/C++与Go之间的RPC

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_31930499/article/details/84749321

搜索C/C++与Go之间的RPC,大多数都是利用thrift框架,但是很少有不用这些框架通信的,所以写下这篇blog。

首先,我们都是知道Go语言之间的RPC,也知道Go语言RPC有三种方式:

(1)标准的RPC,基于http协议,采用gob编码,只能在Go语言进行RPC,不能跨语言(当然这不是绝对的,你可以使用C++在模拟gob编码来封装,同样也可以跨语言RPC)。

(2)基于JSON的RPC,大家都知道JSON是跨语言通用的协议,等会C/C++与Go之间的RPC也是采用这种协议实现。

(3)基于Protobuf的RPC。

具体可以参考:https://github.com/Unknwon/gcblog/blob/master/content/09-protorpc.md

介绍完一些基础概念,下面我们开始C/C++与Go之间的RPC:

上面也说了,主要采用JSON协议进行跨语言RPC,直接上代码,C/C++作为客户端,Go作为服务器

#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<pthread.h>
#include<unistd.h>

#define DEST_PORT 8096
#define DEST_IP "127.0.0.1"
#define MAX_DATA 1024

int main()
{
    int sockfd;
    struct sockaddr_in dest_addr;

    sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd==-1){
    	printf("socket failed:%d",errno);
    }

    dest_addr.sin_family=AF_INET;
    dest_addr.sin_port=htons(DEST_PORT);
    dest_addr.sin_addr.s_addr=inet_addr(DEST_IP);
    bzero(&(dest_addr.sin_zero),8);

    if(connect(sockfd,(struct sockaddr*)&dest_addr,sizeof(struct sockaddr))==-1){
    	printf("connect failed:%d",errno);
    } else{
        char data[] = "{\"id\":1000,\"method\":\"Arith.Divide\",\"params\":[{\"A\":9,\"B\":2}]}";
        send(sockfd,data,strlen(data),0);
        printf("send success\n");
        char buf[MAX_DATA] = {0};
        while(1)
        {
            recv(sockfd,buf,MAX_DATA,0);
            if(strlen(buf) > 0){
                printf("%s\n",buf);
                break;
            }
        }
    }
    close(sockfd);
    return 0;
}

 其实主要就是31行,封装的JSON协议:

id:调用标识符。可以为字符串,不推荐包含小数(不能准确二进制化),或为null(可能引起混乱)。

method:调用的方法名。

params:方法传入的参数,若无参数则为null。

package main
import (
	"errors"
	"fmt"
	"log"
	"net"
	"net/rpc"
	"net/rpc/jsonrpc"
	"os"
)

// 算数运算结构体
type Arith struct {
}

// 算数运算请求结构体
type ArithRequest struct {
	A int
	B int
}

// 算数运算响应结构体
type ArithResponse struct {
	Pro int // 乘积
	Quo int // 商
	Rem int // 余数
}

// 乘法运算方法
func (this *Arith) Multiply(req ArithRequest, res *ArithResponse) error {
	res.Pro = req.A * req.B
	return nil
}

// 除法运算方法
func (this *Arith) Divide(req ArithRequest, res *ArithResponse) error {
	if req.B == 0 {
		return errors.New("divide by zero")
	}
	res.Quo = req.A / req.B
	res.Rem = req.A % req.B
	return nil
}

func main() {
	rpc.Register(new(Arith)) // 注册rpc服务
	lis, err := net.Listen("tcp4", "127.0.0.1:8096")
	if err != nil {
		log.Fatalln("fatal error: ", err)
	}
	fmt.Fprintf(os.Stdout, "%s\n", "start connection")
	for {
		conn, err := lis.Accept() // 接收客户端连接请求
		if err != nil {
			continue
		}
		go func(conn net.Conn) { // 并发处理客户端请求
			fmt.Fprintf(os.Stdout, "%s,my add:%v peer add %v\n", "new client in coming",conn.LocalAddr(),conn.RemoteAddr())
			jsonrpc.ServeConn(conn)
		}(conn)
	}
}

运行:

先运行服务器再 运行客户端

id: 调用标识符,与调用方传入的标识一致,当请求中的id检查发生错误时(转换错误/无效请求),则必须返回null。

result:方法返回值,调用成功时,不能为null,调用错误时,必须为null。(对应的服务器注册函数的response指针参数)

error:调用时错误,无错误返回null,有错误时则返回一个错误对象。 (对应的服务器注册函数的返回值)

猜你喜欢

转载自blog.csdn.net/qq_31930499/article/details/84749321