代码地址
https://github.com/wanmei002/grpc-learn/tree/master/ch04
双端流的描述
客户端可以在建立的连接上间歇性发送数据;服务端接收数据,可选择性的发送响应数据给客户端,就像聊天室功能一样。
proto service 声明
service Product {
rpc AddProduct(stream Order) returns (stream Ret);
}
message Order {
string name = 1;
string desc = 2;
repeated string items = 3;
}
message Ret {
int32 code = 4;
int64 orderId= 5;
string msg = 6;
}
代码实现
服务端代码实现
实现 proto 声明的方法
func (s *server) AddProduct(stream product.Product_AddProductServer) error {
log.Println("start AddProduct")
// 开启一个协程 专门发送数据给客户端
go sendDataToClient(stream)
for {
od, err := stream.Recv()
if err != nil {
if err == io.EOF {
log.Println("server stream end")
retLock.Lock()
for _, v := range RetOrderInfo {
err = stream.Send(v)
if err != nil {
log.Println("server send failed(EOF);err:", err)
return err
}
}
retLock.Unlock()
log.Println("server send over")
return nil
}
log.Println("server recv failed; err:", err)
return err
}
// 控制开启协程的数量
<-ch
// 处理接收的数据的数据
go processData(od)
}
}
跑起来
func main() {
ch = make(chan struct{}, 10)
for i:=0; i<10; i++ {
ch <- struct{}{}
}
fmt.Println("server ch:", ch)
ls, err := net.Listen("tcp", ":8093")
if err != nil {
log.Println("listen failed; err:", err)
return
}
g := grpc.NewServer()
product.RegisterProductServer(g, &server{})
log.Println("start server")
if err = g.Serve(ls); err != nil {
log.Println("server serve failed; err:", err)
}
}
客户端代码
func main(){
d, err := grpc.Dial(":8093", grpc.WithInsecure())
if err != nil {
log.Println("client dial failed; err:", err)
return
}
defer d.Close()
client := product.NewProductClient(d)
stream, err := client.AddProduct(context.Background())
if err != nil {
log.Println("client add product failed; err:", err)
return
}
// 来一个协程专门接收数据
ctx, closeFunc := context.WithCancel(context.Background())
go RecvData(stream, closeFunc)
for i:=0; i<100; i++ {
o := &product.Order{
Name: fmt.Sprint("zzh-",i),
Desc: fmt.Sprint("desc-",i),
Items: make([]string, 1),
}
log.Println("client send data:", o)
err := stream.Send(o)
if err != nil {
log.Println("clinet send failed;err:", err)
}
}
err = stream.CloseSend()
<-ctx.Done()
fmt.Println("over")
}
func RecvData(stream product.Product_AddProductClient, cancelFunc context.CancelFunc) {
defer cancelFunc()
for {
res, err := stream.Recv()
if err != nil {
log.Println("client recv failed; err:", err)
return
}
log.Printf("client recv %+v\n", res)
}
}