go mod gRpc
我使用的是goland编辑器,以此为基础,进行项目的创建以及实现
创建项目,我使用的是 go mod 进行项目的管理
开始创建项目
我们虽然是使用go mod 进行项目管理,但是我们在创建项目的时候,一定不要使用go mod 来创建项目,因为后期可能会留下坑
继续:
我们进行go mod init GRPC 进行 mod 初始化
(这个是,我之前做过初始化的结果)
初始化完成后,我们在setting 文件中,启用go mod 来进行项目管理,
将这个框框打钩,apply->ok
这样之后,在下图中就可以看到 go mod 了,说明我们操作成功了
在这解释一下,这么操作的问题,可以避免 go mod 中 引包链接报红的问题
好的,开始进入到代码方面
结构就是这么简答的结构,这个例子是3年前以为大神写的
https://github.com/wongcony/grpcstudy.git
这个是代码原地址
service.go
package main
import (
pb "GRPC/proto"
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
"io"
"log"
"net"
"strconv"
"time"
)
const PORT = ":9077"
type SService struct{}
//简单模式
func (ss *SService) StudentAdd(ctx context.Context, in *pb.StudentResponse) (*pb.ResultResponse, error) {
fmt.Println("StudentAdd================简单模式================")
//简单模式不需要调用recv和send。数据直接通过方法的参数和返回值来接收发送。
re := &pb.ResultResponse{1, "noerror"}
return re, nil
}
//客户端流模式
func (ss *SService) GetHelloTest(stream pb.SelfManage_GetHelloTestServer) error {
fmt.Println("GetHelloTest================客户端模式================")
//服务器端的代码stream是SelfManage_GetHelloTestServer类型,这种类型只有recv和sendandclose两种方法接收和发送并关闭
//而客户端跟着相反是send和closeandrecv两种方法。
//客户端流模式要先接收然后再发送。
//使用for循环接收数据,因为接收的是流模式的StudentRequest类型,所以每次循环得到的是一个studentRequest类型
for {
in, err := stream.Recv() //通过for循环接收客户端传来的流,该方法协程不安全。
//必须把判断EOF的条件放在nil前面,因为如果获取完数据err会返回EOF类型
if err == io.EOF { //在读取完所有数据之后会返回EOF来执行该条件。
fmt.Println("read done")
//发送(只在读取完数据后发送了一次)
rp := &pb.ResultResponse{Affectrows: 1000, Errorinfo: "n xin"}
//发送并关闭。该方法的返回值是error类型。但客户端并未得到返回值,只收到sendandclose发送的数据。
//注意:该方法内部调用的SendMsg方法,但是该方法协程不安全。不同协程可以同时调用会出现问题。
return stream.SendAndClose(rp)
}
if err != nil {
fmt.Println("ERR IN GetHelloTest", err)
return err
}
//打印出每次接收的数据
fmt.Println("Recieved information:", in)
}
}
//服务器端流模式
func (ss *SService) GetNxinMethod(in *pb.StudentRequest, stream pb.SelfManage_GetNxinMethodServer) error {
fmt.Println("GetNxinMethod================服务器端模式================")
fmt.Println(in.Sid) //打印出来客户端参数传来的数据(此处无意义,只是用来判断客户端是否传来数据)
p := &pb.ResultResponse{10, "nnnnn"}
stream.Send(p) //服务器流模式发送数据。此处不用关闭发送,因为没找到关闭的方法。
return nil
}
//双向模式
func (ss *SService) GetStudent(stream pb.SelfManage_GetStudentServer) error {
fmt.Println("服务器端,双向模式 === >>")
for {
//接收
in, err := stream.Recv()
if err == io.EOF {
fmt.Println("read done")
return nil
}
if err != nil {
grpclog.Fatal("some error in recv", err)
}
fmt.Println(in, strconv.FormatInt(time.Now().UTC().UnixNano(), 10))
//发送
rp := &pb.ResultResponse{Affectrows: 1000, Errorinfo: "n xin"}
_ = stream.Send(rp)
}
return nil
}
func main() {
lis, err := net.Listen("tcp", PORT)
if err != nil {
grpclog.Fatal("error occur in listen")
log.Fatal("test ttt")
}
s := grpc.NewServer()
pb.RegisterSelfManageServer(s, &SService{})
_ = s.Serve(lis)
}
client.go
package main
import (
pb "GRPC/proto"
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
"io"
"strconv"
"time"
)
const address = "127.0.0.1:9077"
func StudentAddMethod(client pb.SelfManageClient, info *pb.StudentResponse) {
fmt.Println("StudentAdd_Method================简单模式================")
//简单模式下直接调用方法就可以传递参数和获得返回值
re, err := client.StudentAdd(context.Background(), info)
if err != nil {
fmt.Println("some error occur in studentadd", err)
}
fmt.Println("studentadd_method:", re.Affectrows, re.Errorinfo)
}
func GetHelloTestMethod(client pb.SelfManageClient) {
fmt.Println("GetHelloTest_Method================客户端模式================")
//此处相当于创建了一个GetHelloTest的通道。通过通道发送和接收数据。
re, err := client.GetHelloTest(context.Background())
if err != nil {
fmt.Println("some error occur in gethellotest", err)
}
//初始化要发送给服务器端的数据,并发送(此处连续发送10次)
for i := 0; i < 10; i++ {
sq := &pb.StudentRequest{Sid: 1}
re.Send(sq) //客户端要先发送数据再接收
}
//使用for循环接收数据
for {
//先关闭send然后再接收数据。该方法内部调用了CloseSend和RecvMsg方法。但是后者协程不安全
r, err2 := re.CloseAndRecv()
if err2 == io.EOF {
fmt.Println("recv done")
return
}
if err2 != nil {
fmt.Println("some error occur in gethellotest recv")
}
fmt.Println(r)
}
}
func GetNxinMethodMethod(client pb.SelfManageClient) {
fmt.Println("GetNxinMethod_Method================服务器端模式================")
s := &pb.StudentRequest{2}
//向服务器端以参数形式发送数据s,并创建了一个通道,通过通道来获取服务器端返回的流
re, err := client.GetNxinMethod(context.Background(), s)
if err != nil {
fmt.Println("some error occur in getnxinmethod")
}
for {
//上面已经以参数的形式发送了数据,此处直接使用Recv接收数据。因是参数发送,所有不用使用CloseAndRecv
v, e := re.Recv()
if e == io.EOF {
fmt.Println("recv done")
return
}
if e != nil {
fmt.Println("e not nil", e)
return
}
fmt.Println("get nxin method recv", v)
}
}
func GetStudentMethod(client pb.SelfManageClient) {
fmt.Println("GetStudent_Method================双向模式================")
re, err := client.GetStudent(context.Background())
if err != nil {
fmt.Println("get student error", err)
}
//创建一个通道作为控制协程协作
waitc := make(chan struct{})
//开启协程进行接收信息
go func() {
for {
in, errr := re.Recv()
if errr == io.EOF {
fmt.Println("read done ")
close(waitc)
return
}
if errr != nil {
fmt.Println("get student recv {}", errr)
}
fmt.Println("Got message at point(%s, %d)", in.Errorinfo, in.Affectrows, strconv.FormatInt(time.Now().UTC().UnixNano(), 10))
}
}()
//主进程继续执行发送
s1 := &pb.StudentRequest{Sid: 2}
er := re.Send(s1)
if er != nil {
fmt.Println("get student send err ", er)
}
_ = re.CloseSend()
<-waitc
}
func main() {
fmt.Println("客户端 === >>")
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
grpclog.Fatal("some error in dial")
}
defer conn.Close()
client := pb.NewSelfManageClient(conn)
s := &pb.StudentResponse{Sid: 1, Age: 2, Name: "wang", Telephone: "1888888", Address: "beijing"}
StudentAddMethod(client, s)
GetHelloTestMethod(client)
GetNxinMethodMethod(client)
GetStudentMethod(client)
}
student.pb.go
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: student.proto
/*
Package Model is a generated protocol buffer package.
It is generated from these files:
student.proto
It has these top-level messages:
StudentResponse
StudentRequest
ResultResponse
*/
package Model
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import (
context "context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type StudentResponse struct {
Sid int32 `protobuf:"varint,1,opt,name=sid" json:"sid,omitempty"`
Age int32 `protobuf:"varint,2,opt,name=age" json:"age,omitempty"`
Name string `protobuf:"bytes,3,opt,name=name" json:"name,omitempty"`
Telephone string `protobuf:"bytes,4,opt,name=telephone" json:"telephone,omitempty"`
Address string `protobuf:"bytes,5,opt,name=address" json:"address,omitempty"`
}
func (m *StudentResponse) Reset() { *m = StudentResponse{} }
func (m *StudentResponse) String() string { return proto.CompactTextString(m) }
func (*StudentResponse) ProtoMessage() {}
func (*StudentResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *StudentResponse) GetSid() int32 {
if m != nil {
return m.Sid
}
return 0
}
func (m *StudentResponse) GetAge() int32 {
if m != nil {
return m.Age
}
return 0
}
func (m *StudentResponse) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *StudentResponse) GetTelephone() string {
if m != nil {
return m.Telephone
}
return ""
}
func (m *StudentResponse) GetAddress() string {
if m != nil {
return m.Address
}
return ""
}
type StudentRequest struct {
Sid int32 `protobuf:"varint,1,opt,name=sid" json:"sid,omitempty"`
}
func (m *StudentRequest) Reset() { *m = StudentRequest{} }
func (m *StudentRequest) String() string { return proto.CompactTextString(m) }
func (*StudentRequest) ProtoMessage() {}
func (*StudentRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *StudentRequest) GetSid() int32 {
if m != nil {
return m.Sid
}
return 0
}
type ResultResponse struct {
Affectrows int32 `protobuf:"varint,1,opt,name=affectrows" json:"affectrows,omitempty"`
Errorinfo string `protobuf:"bytes,2,opt,name=errorinfo" json:"errorinfo,omitempty"`
}
func (m *ResultResponse) Reset() { *m = ResultResponse{} }
func (m *ResultResponse) String() string { return proto.CompactTextString(m) }
func (*ResultResponse) ProtoMessage() {}
func (*ResultResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *ResultResponse) GetAffectrows() int32 {
if m != nil {
return m.Affectrows
}
return 0
}
func (m *ResultResponse) GetErrorinfo() string {
if m != nil {
return m.Errorinfo
}
return ""
}
func init() {
proto.RegisterType((*StudentResponse)(nil), "Model.StudentResponse")
proto.RegisterType((*StudentRequest)(nil), "Model.StudentRequest")
proto.RegisterType((*ResultResponse)(nil), "Model.ResultResponse")
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for SelfManage service
type SelfManageClient interface {
StudentAdd(ctx context.Context, in *StudentResponse, opts ...grpc.CallOption) (*ResultResponse, error)
GetHelloTest(ctx context.Context, opts ...grpc.CallOption) (SelfManage_GetHelloTestClient, error)
GetNxinMethod(ctx context.Context, in *StudentRequest, opts ...grpc.CallOption) (SelfManage_GetNxinMethodClient, error)
GetStudent(ctx context.Context, opts ...grpc.CallOption) (SelfManage_GetStudentClient, error)
}
type selfManageClient struct {
cc *grpc.ClientConn
}
func NewSelfManageClient(cc *grpc.ClientConn) SelfManageClient {
return &selfManageClient{cc}
}
func (c *selfManageClient) StudentAdd(ctx context.Context, in *StudentResponse, opts ...grpc.CallOption) (*ResultResponse, error) {
out := new(ResultResponse)
err := grpc.Invoke(ctx, "/Model.SelfManage/StudentAdd", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *selfManageClient) GetHelloTest(ctx context.Context, opts ...grpc.CallOption) (SelfManage_GetHelloTestClient, error) {
stream, err := grpc.NewClientStream(ctx, &_SelfManage_serviceDesc.Streams[0], c.cc, "/Model.SelfManage/GetHelloTest", opts...)
if err != nil {
return nil, err
}
x := &selfManageGetHelloTestClient{stream}
return x, nil
}
type SelfManage_GetHelloTestClient interface {
Send(*StudentRequest) error
CloseAndRecv() (*ResultResponse, error)
grpc.ClientStream
}
type selfManageGetHelloTestClient struct {
grpc.ClientStream
}
func (x *selfManageGetHelloTestClient) Send(m *StudentRequest) error {
return x.ClientStream.SendMsg(m)
}
func (x *selfManageGetHelloTestClient) CloseAndRecv() (*ResultResponse, error) {
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
m := new(ResultResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *selfManageClient) GetNxinMethod(ctx context.Context, in *StudentRequest, opts ...grpc.CallOption) (SelfManage_GetNxinMethodClient, error) {
stream, err := grpc.NewClientStream(ctx, &_SelfManage_serviceDesc.Streams[1], c.cc, "/Model.SelfManage/GetNxinMethod", opts...)
if err != nil {
return nil, err
}
x := &selfManageGetNxinMethodClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type SelfManage_GetNxinMethodClient interface {
Recv() (*ResultResponse, error)
grpc.ClientStream
}
type selfManageGetNxinMethodClient struct {
grpc.ClientStream
}
func (x *selfManageGetNxinMethodClient) Recv() (*ResultResponse, error) {
m := new(ResultResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *selfManageClient) GetStudent(ctx context.Context, opts ...grpc.CallOption) (SelfManage_GetStudentClient, error) {
stream, err := grpc.NewClientStream(ctx, &_SelfManage_serviceDesc.Streams[2], c.cc, "/Model.SelfManage/GetStudent", opts...)
if err != nil {
return nil, err
}
x := &selfManageGetStudentClient{stream}
return x, nil
}
type SelfManage_GetStudentClient interface {
Send(*StudentRequest) error
Recv() (*ResultResponse, error)
grpc.ClientStream
}
type selfManageGetStudentClient struct {
grpc.ClientStream
}
func (x *selfManageGetStudentClient) Send(m *StudentRequest) error {
return x.ClientStream.SendMsg(m)
}
func (x *selfManageGetStudentClient) Recv() (*ResultResponse, error) {
m := new(ResultResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// Server API for SelfManage service
type SelfManageServer interface {
StudentAdd(context.Context, *StudentResponse) (*ResultResponse, error)
GetHelloTest(SelfManage_GetHelloTestServer) error
GetNxinMethod(*StudentRequest, SelfManage_GetNxinMethodServer) error
GetStudent(SelfManage_GetStudentServer) error
}
func RegisterSelfManageServer(s *grpc.Server, srv SelfManageServer) {
s.RegisterService(&_SelfManage_serviceDesc, srv)
}
func _SelfManage_StudentAdd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(StudentResponse)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SelfManageServer).StudentAdd(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Model.SelfManage/StudentAdd",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SelfManageServer).StudentAdd(ctx, req.(*StudentResponse))
}
return interceptor(ctx, in, info, handler)
}
func _SelfManage_GetHelloTest_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(SelfManageServer).GetHelloTest(&selfManageGetHelloTestServer{stream})
}
type SelfManage_GetHelloTestServer interface {
SendAndClose(*ResultResponse) error
Recv() (*StudentRequest, error)
grpc.ServerStream
}
type selfManageGetHelloTestServer struct {
grpc.ServerStream
}
func (x *selfManageGetHelloTestServer) SendAndClose(m *ResultResponse) error {
return x.ServerStream.SendMsg(m)
}
func (x *selfManageGetHelloTestServer) Recv() (*StudentRequest, error) {
m := new(StudentRequest)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func _SelfManage_GetNxinMethod_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(StudentRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(SelfManageServer).GetNxinMethod(m, &selfManageGetNxinMethodServer{stream})
}
type SelfManage_GetNxinMethodServer interface {
Send(*ResultResponse) error
grpc.ServerStream
}
type selfManageGetNxinMethodServer struct {
grpc.ServerStream
}
func (x *selfManageGetNxinMethodServer) Send(m *ResultResponse) error {
return x.ServerStream.SendMsg(m)
}
func _SelfManage_GetStudent_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(SelfManageServer).GetStudent(&selfManageGetStudentServer{stream})
}
type SelfManage_GetStudentServer interface {
Send(*ResultResponse) error
Recv() (*StudentRequest, error)
grpc.ServerStream
}
type selfManageGetStudentServer struct {
grpc.ServerStream
}
func (x *selfManageGetStudentServer) Send(m *ResultResponse) error {
return x.ServerStream.SendMsg(m)
}
func (x *selfManageGetStudentServer) Recv() (*StudentRequest, error) {
m := new(StudentRequest)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
var _SelfManage_serviceDesc = grpc.ServiceDesc{
ServiceName: "Model.SelfManage",
HandlerType: (*SelfManageServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "StudentAdd",
Handler: _SelfManage_StudentAdd_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "GetHelloTest",
Handler: _SelfManage_GetHelloTest_Handler,
ClientStreams: true,
},
{
StreamName: "GetNxinMethod",
Handler: _SelfManage_GetNxinMethod_Handler,
ServerStreams: true,
},
{
StreamName: "GetStudent",
Handler: _SelfManage_GetStudent_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "student.proto",
}
func init() { proto.RegisterFile("student.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 289 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0x41, 0x4b, 0xc3, 0x30,
0x14, 0xc7, 0xcd, 0xb6, 0x2a, 0x7d, 0xb8, 0x29, 0x81, 0x49, 0x10, 0x91, 0xd1, 0x53, 0x4f, 0x65,
0xe8, 0x59, 0xd8, 0x4e, 0xf5, 0xd2, 0x1d, 0x3a, 0xbf, 0x40, 0x34, 0xaf, 0x5b, 0x21, 0x26, 0x35,
0x49, 0xd1, 0xab, 0x5f, 0xc1, 0x4f, 0x2c, 0xc9, 0xaa, 0xd5, 0x89, 0x87, 0xdd, 0x5e, 0x7f, 0xff,
0xd7, 0xc7, 0xef, 0x3d, 0x02, 0x63, 0xeb, 0x5a, 0x81, 0xca, 0x65, 0x8d, 0xd1, 0x4e, 0xd3, 0xa8,
0xd0, 0x02, 0x65, 0xf2, 0x4e, 0xe0, 0x6c, 0xbd, 0x0b, 0x4a, 0xb4, 0x8d, 0x56, 0x16, 0xe9, 0x39,
0x0c, 0x6d, 0x2d, 0x18, 0x99, 0x91, 0x34, 0x2a, 0x7d, 0xe9, 0x09, 0xdf, 0x20, 0x1b, 0xec, 0x08,
0xdf, 0x20, 0xa5, 0x30, 0x52, 0xfc, 0x19, 0xd9, 0x70, 0x46, 0xd2, 0xb8, 0x0c, 0x35, 0xbd, 0x82,
0xd8, 0xa1, 0xc4, 0x66, 0xab, 0x15, 0xb2, 0x51, 0x08, 0x7a, 0x40, 0x19, 0x9c, 0x70, 0x21, 0x0c,
0x5a, 0xcb, 0xa2, 0x90, 0x7d, 0x7d, 0x26, 0x09, 0x4c, 0xbe, 0x15, 0x5e, 0x5a, 0xb4, 0xee, 0xaf,
0x41, 0xb2, 0x82, 0x49, 0x89, 0xb6, 0x95, 0xbd, 0xe5, 0x35, 0x00, 0xaf, 0x2a, 0x7c, 0x72, 0x46,
0xbf, 0xda, 0xae, 0xf5, 0x07, 0xf1, 0x36, 0x68, 0x8c, 0x36, 0xb5, 0xaa, 0x74, 0x30, 0x8f, 0xcb,
0x1e, 0xdc, 0x7c, 0x0c, 0x00, 0xd6, 0x28, 0xab, 0x82, 0x2b, 0xbf, 0xce, 0x1d, 0x40, 0xa7, 0xb0,
0x14, 0x82, 0x5e, 0x64, 0xe1, 0x38, 0xd9, 0xde, 0x61, 0x2e, 0xa7, 0x1d, 0xff, 0x6d, 0x92, 0x1c,
0xd1, 0x05, 0x9c, 0xe6, 0xe8, 0xee, 0x51, 0x4a, 0xfd, 0xe0, 0xfd, 0xa7, 0xfb, 0x03, 0xc2, 0x5a,
0xff, 0xfe, 0x9f, 0x12, 0xba, 0x84, 0x71, 0x8e, 0x6e, 0xf5, 0x56, 0xab, 0x02, 0xdd, 0x56, 0x8b,
0x43, 0x47, 0xcc, 0x09, 0x5d, 0x00, 0xe4, 0xe8, 0xba, 0xfe, 0xc3, 0x15, 0xe6, 0xe4, 0xf1, 0x38,
0x3c, 0x8d, 0xdb, 0xcf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x11, 0x3c, 0x0a, 0x6f, 0x2b, 0x02, 0x00,
0x00,
}
Student.proto
syntax="proto3";
package Model;
message StudentResponse{
int32 sid=1;
int32 age=2;
string name=3;
string telephone=4;
string address=5;
}
message StudentRequest{
int32 sid=1;
}
message ResultResponse{
int32 affectrows=1;
string errorinfo=2;
}
service SelfManage{
rpc StudentAdd(StudentResponse)returns(ResultResponse){};//gRPC简单模式
rpc GetHelloTest(stream StudentRequest)returns(ResultResponse){};//客户端流模式(客户端发送stream)
rpc GetNxinMethod(StudentRequest)returns(stream ResultResponse){};//服务端流模式(从服务端返回stream)
rpc GetStudent(stream StudentRequest)returns(stream ResultResponse){};//双向流模式
}
在此将代码贴出来,大家可以直接查看,使用,测试
我去搞一下proto的使用,代码什么的没看懂的可以留言,各路大神帮忙解答一下,小弟告谢,我还得学习学习