C/C++与Go之间的RPC

目前, rpc框架主要沿着两条路线发展,一个是目标为了跨语言,服务端可以用不同的语言实现,客户端也可以用不同的语言实现,不同的语言实现的客户端和服务器端可以互相调用。很显然,要支持不同的语言,需要基于那种语言实现相同协议的框架,并且协议设计应该也是跨语言的,其中比较典型的是 grpc,基于同一个IDL,可以生成不同语言的代码,并且语言的支持也非常的多。另一个rpc框架发展的目标是支持服务治理,主要的精力放在服务发现、路由、容错处理等方面,主要围绕一个语言开发,可能也有一些第三方曲折的实现服务的调用和服务的实现,这其中的代表,也是比较早的开源的框架就是阿里巴巴的dubbo。下文主要讲解前者。

搜索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。

 

介绍完一些基础概念,下面我们开始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,有错误时则返回一个错误对象。 (对应的服务器注册函数的返回值)


参考原文:

https://github.com/Unknwon/gcblog/blob/master/content/09-protorpc.md

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值