寫完 gRCP Server streaminggRCP Client streaming 後,終於來到最後一個 Bidirectional streaming ,這個是雙向的 streaming, Client/Server 可以 收到送出 多個訊息。

只要知道怎麼使用 Server streaming 和 Client streaming,要實作 Bidirectional streaming 其實是大同小異。 我們就接續 gRCP Client streaming 的範例,只是改成使用者可以上傳多個圖片,且上傳完一張後,伺服器會馬上回報接收到檔案。

下面是這次範例的 message type 。

proto
  • proto
1
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 來中斷迴圈就好。

server
  • go
1
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 相對多一點,主要做了下列事情

  1. 開一個 goroutine 來接收伺服器回傳的訊息
  2. 使用 channel 確保有接收全部的伺服器回應後,才結束程式
  3. 讀取三個圖片,將圖片資料傳送給伺服器
client
  • go
1
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() {
// Create tls based credential.
creds, _ := credentials.NewClientTLSFromFile("../cert.pem", "")

// Set up a connection to the server.
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]