go使用grpc实现go与go,go与C#相互调用
Posted lishuangquan1987
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go使用grpc实现go与go,go与C#相互调用相关的知识,希望对你有一定的参考价值。
示例源代码地址
https://github.com/lishuangquan1987/grpctest
protoc下载
protoc
是protobuf的编译工具,能根据.proto
文件生成为各种语言的源文件。
protoc
集成了如下语言的转换:
protoc
的下载地址:https://github.com/protocolbuffers/protobuf/releases
我是在window上开发的,所以选择protoc-21.9-win64.zip
下载之后解压:
在bin的目录下存在protoc.exe
:
将protoc.exe
的路径加入到系统环境变量:
验证protoc
是否安装完成:
打开控制台输入:
protoc --version
出现如下输出,表示安装完成:
protoc-gen-go安装
前面已经下载了protoc.exe
,要想把.proto
文件转换为.go
源代码,必须使用protoc
和插件protoc-gen-go
protoc-gen-go的安装使用如下命令:
go install github.com/golang/protobuf/protoc-gen-go@latest
安装之后,会在%USERPROFILE%\\go\\bin
如下目录生成一个protoc-gen-go.exe
文件:
将.proto
文件转换为.go
文件
在proto文件的目录下,输入如下指令来生成go文件:
protoc --go_out=plugins=grpc:. *.proto
编写go语言的服务端和客户端
本示例想实现一个客户端和服务端相互通讯聊天的例子实现如下:
- 客户端可以任意时刻给服务端发消息
- 服务端可以任意时刻给客户端发消息
- 客户端发送消息只有服务端能收到
- 服务端发送消息,所有的客户端都能收到
定义.proto
文件
syntax="proto3";
//注意这里不要写错
option go_package="./;testpb";
message CallRequest
string data=1;
message CallResponse
string data=1;
service TestService
rpc CallEachOther (stream CallRequest) returns (stream CallResponse);
项目的结构
其中protos是client和server共用的proto文件以及生成的.proto文件。
根据.proto
文件生成.go
文件
打开集成终端:
进入到protos
目录:
cd protos
输入如下指令:
protoc --go_out=plugins=grpc:. *.proto
可以看到生成了test.pb.go
文件:
更改test.pb.go
的包名为protos
初始化module
go mod init grpctest
服务端与客户端共用一个Module,分别在两个不同的文件有两个main.go
文件作为各自的入口
因为前面已经生成了test.pb.go
源码文件,只需要如下命令即可自动下载grpc通讯所需要的包:
go mod tidy
go 服务端开发
定义服务端的testservice
testservice
主要是实现grpc接口中的方法和启动grpc服务。
grpctest/test_server/test_service/test_service.go
的代码如下:
package testservice
import (
"fmt"
uuid "github.com/satori/go.uuid"
"google.golang.org/grpc"
proto "grpctest/protos"
"net"
"sync"
)
var Service *TestService
type ClientInfo struct
UUID string
chWrite chan string
chRead chan string
chWriteErr chan struct
chReadErr chan struct
chFinish chan struct
type TestService struct
mu sync.Mutex
Clients map[string]*ClientInfo
func (s *TestService) CallEachOther(t proto.TestService_CallEachOtherServer) error
//加入Client
client := &ClientInfo
UUID: uuid.NewV4().String(),
chWrite: make(chan string),
chRead: make(chan string),
chWriteErr: make(chan struct),
chReadErr: make(chan struct),
chFinish: make(chan struct, 2),
s.Clients[client.UUID] = client
fmt.Printf("客户端:%s已连接\\n", client.UUID)
//写
go func()
for
select
case str, ok := <-client.chWrite:
if !ok
client.chFinish <- struct
client.chWriteErr <- struct
return //通道关闭,调用完成
err := t.Send(&proto.CallResponseData: str)
if err != nil
client.chWriteErr <- struct
client.chFinish <- struct
return
case <-client.chReadErr: //读错误时,写要停止
return
()
go func()
for
r, err := t.Recv()
if err != nil
client.chReadErr <- struct
client.chFinish <- struct
return
fmt.Printf("[%s-接收]:%s\\n", client.UUID, r.Data)
()
select
case <-client.chFinish:
delete(s.Clients, client.UUID)
fmt.Printf("客户端:%s已断开\\n", client.UUID)
return nil
func Send(msg string)
for _, v := range Service.Clients
v.chWrite <- msg
fmt.Printf("已向%d个客户端发送了消息:%s", len(Service.Clients), msg)
func StartService()
lis, err := net.Listen("tcp", "0.0.0.0:9091")
if err != nil
fmt.Printf("listen error:%v\\n", err)
return
s := grpc.NewServer()
Service = &TestService
Clients: map[string]*ClientInfo,
proto.RegisterTestServiceServer(s, Service)
if err := s.Serve(lis); err != nil
fmt.Printf("serve error:%v\\n", err)
return
调用:
grpctest/test_server/main.go
:
package main
import (
"fmt"
testservice "grpctest/test_server/test_service"
)
func main()
fmt.Println("启动...")
go testservice.StartService()
for
fmt.Println("请输入要发送的字符串:")
var str string
fmt.Scanln(&str)
testservice.Send(str)
go客户端开发
定义客户端的test_service
grpctest/test_client/test_service/test_service.go
:
package testservice
import (
"fmt"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
proto "grpctest/protos"
)
func NewClientService() *ClientService
return &ClientService
chFinish: make(chan struct, 2),
chWriteErr: make(chan struct, 1),
chReadErr: make(chan struct, 1),
type ClientService struct
chFinish chan struct
chReadErr chan struct
chWriteErr chan struct
func (s *ClientService) StartService()
conn, err := grpc.Dial("0.0.0.0:9091", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
if err != nil
fmt.Printf("dial error:%v\\n", err)
return
defer conn.Close()
c := proto.NewTestServiceClient(conn)
callClient, err := c.CallEachOther(context.Background())
if err != nil
fmt.Printf("call CallEachOther fail:%v\\n", err)
return
//读
go func()
for
select
case <-s.chWriteErr:
return
default:
break
data, err := callClient.Recv()
if err != nil
fmt.Printf("recv error :%v\\n", err)
s.chReadErr <- struct
s.chFinish <- struct
return
fmt.Printf("[接受]:%s\\n", data.Data)
()
//写
go func()
for
select
case <-s.chReadErr:
return
default:
break
fmt.Println("请输入要发送的字符串:")
var str string
fmt.Scanln(&str)
err := callClient.Send(&proto.CallRequest
Data: str,
)
if err != nil
s.chWriteErr <- struct
s.chFinish <- struct
return
()
select
case <-s.chFinish:
break
调用:
grpctest/test_client/main.go
:
package main
import (
"fmt"
testservice "grpctest/test_client/test_service"
)
func main()
for
fmt.Println("开始连接服务器...")
s := testservice.NewClientService()
s.StartService()
fmt.Println("与服务器连接断开...")
最后go server 和 go client通讯的界面如下:
c# client
安装所需要的grpc包
- Google.Protobuf
- Grpc.Core
- Grpc.Tools
根据.proto
文件生成.cs
文件
安装Grpc.Tools
之后,会在packages
目录下生成Grpc.Tools.2.50.0
目录,打开tools/windows_x64目录,可以看到有grpc_csharp_plugin.exe
和protoc.exe
文件:
- 拷贝
test.proto
到packages\\Grpc.Tools.2.50.0\\tools\\windows_x64(grpc_charp_plugins.exe)
所在的目录,在protos
目录打开终端,输入如下命令生成.cs
文件
protoc -I . --csharp_out . --grpc_out . --plugin=protoc-gen-grpc=grpc_csharp_plugin.exe *.proto
- 将生成的文件
Test.cs
和TestGrpc.cs
拷贝到项目中:
编写客户端代码
using Grpc.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace grpctest_csharp
internal class Program
static void Main(string[] args)
Console.Title="C# gRPC客户端";
var channel = new Channel("127.0.0.1:9091",ChannelCredentials.Insecure);
var client=new TestService.TestServiceClient(channel);
var callEachotherContext = client.CallEachOther();
Task.Factory.StartNew(async () =>
while (await callEachotherContext.ResponseStream.MoveNext())
Console.WriteLine("接收:0", callEachotherContext.ResponseStream.Current.Data);
);
while (true)
Console.WriteLine("请输入要发送的内容");
var str = Console.ReadLine();
callEachotherContext.RequestStream.WriteAsync(new CallRequest() Data = str ).Wait();
最后运行的结果如下:
以上是关于go使用grpc实现go与go,go与C#相互调用的主要内容,如果未能解决你的问题,请参考以下文章
go语言使用go-sciter创建桌面应用 事件处理,函数与方法定义,go与tiscript之间相互调用
Go语言入门篇-gRPC基于golang & java简单实现