寫完 gRCP Server streaming 和 gRCP Client streaming 後,終於來到最後一個 Bidirectional streaming ,這個是雙向的 streaming, Client/Server 可以 收到
與 送出
多個訊息。
只要知道怎麼使用 Server streaming 和 Client streaming,要實作 Bidirectional streaming 其實是大同小異。 我們就接續 gRCP Client streaming 的範例,只是改成使用者可以上傳多個圖片,且上傳完一張後,伺服器會馬上回報接收到檔案。
下面是這次範例的 message type 。
proto1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| syntax = "proto3";
package pb;
message AddPhotoRequest { bytes data = 1; }
message AddPhotoResponse { bool isOk = 1; }
service Employee { rpc AddPhoto (stream AddPhotoRequest) returns (stream AddPhotoResponse); }
|
Server Code
Server code 算單純,開一個 for 迴圈接收 Client 傳過來的訊息,收到訊息後在馬上回應給 Client。 最後只要判斷 io.EOF
來中斷迴圈就好。
server1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package service
import ( "fmt" "io" "trygrpc/pb" )
type EmployeeService struct {}
func (e EmployeeService) AddPhoto(r pb.Employee_AddPhotoServer) error { for { res, err := r.Recv() if err == io.EOF { return nil } if err != nil { return err } fmt.Printf("Get bytes with len: %d\n", len(res.Data)) r.Send(&pb.AddPhotoResponse{IsOk: true }) } }
|
Client Code
Client Code 相對多一點,主要做了下列事情
- 開一個 goroutine 來接收伺服器回傳的訊息
- 使用 channel 確保有接收全部的伺服器回應後,才結束程式
- 讀取三個圖片,將圖片資料傳送給伺服器
client1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| package main
import ( "context" "fmt" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "io" "io/ioutil" "log" "trygrpc/pb" )
const ( address = "localhost:50051" )
func main() { creds, _ := credentials.NewClientTLSFromFile("../cert.pem", "")
conn, _ := grpc.Dial(address, grpc.WithTransportCredentials(creds), grpc.WithBlock())
defer conn.Close()
c := pb.NewEmployeeClient(conn) stream, _ := c.AddPhoto(context.Background()) filesPath := []string{"goteam1.png","goteam2.png","goteam3.png"}
doneCh := make(chan struct{})
go func(){ for { res, err := stream.Recv() if err == io.EOF { doneCh <- struct{}{} break }
if err != nil { log.Fatal(err) }
fmt.Println(res) } }()
for _, p := range filesPath { fbytes, _ := ioutil.ReadFile(p) stream.Send(&pb.AddPhotoRequest{Data: fbytes}) }
stream.CloseSend()
<-doneCh fmt.Println("完成傳送") }
|
執行結果
從執行結果可以看到,Server/Client 都同時收到訊息與發送訊息。
延伸閱讀
[gRPC Basics - Go]