版权声明:本文为博主原创文章,未经博主允许不得转载。 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,有错误时则返回一个错误对象。 (对应的服务器注册函数的返回值)