@@ -55,6 +55,7 @@ and easy interface updating.
55
55
* For installation instructions, see Go’s [ Getting Started] ( https://golang.org/doc/install ) guide.
56
56
* [ ** Protocol buffer** ] ( https://developers.google.com/protocol-buffers ) ** compiler** , ` protoc ` , [ version 3] ( https://protobuf.dev/programming-guides/proto3 ) .
57
57
* For installation instructions, see [ Protocol Buffer Compiler Installation] ( https://grpc.io/docs/protoc-installation/ ) .
58
+ * NOTE: Must need a version of Protoc 3.27.1 or higher.
58
59
* ** Go plugins** for the protocol compiler:
59
60
* Install the protocol compiler plugins for Go using the following commands.
60
61
@@ -227,7 +228,7 @@ rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
227
228
```
228
229
229
230
> [ !TIP]
230
- > For the complete .proto file, see [ routeguide/route_guide.proto] ( / completed/routeguide/route_guide.proto ) .
231
+ > For the complete .proto file, see [ routeguide/route_guide.proto] ( https://github.com/grpc-ecosystem/grpc-codelabs/blob/bdabe90d07065752a66cf449c2513803b35e6cd3/codelabs/grpc-go-streaming/ completed/routeguide/route_guide.pb.go ) .
231
232
232
233
## Generating client and server code
233
234
@@ -394,8 +395,17 @@ func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error
394
395
return err
395
396
}
396
397
key := serialize (in.Location )
397
- ... // look for notes to be sent to client
398
- for _ , note := range s.routeNotes [key] {
398
+
399
+ s.mu .Lock ()
400
+ s.routeNotes [key] = append (s.routeNotes [key], in)
401
+ // Note: this copy prevents blocking other clients while serving this one.
402
+ // We don't need to do a deep copy, because elements in the slice are
403
+ // insert-only and never modified.
404
+ rn := make ([]*pb.RouteNote , len (s.routeNotes [key]))
405
+ copy (rn, s.routeNotes [key])
406
+ s.mu .Unlock ()
407
+
408
+ for _ , note := range rn {
399
409
if err := stream.Send (note); err != nil {
400
410
return err
401
411
}
@@ -424,16 +434,16 @@ do this for our `RouteGuide` service:
424
434
> port can be configured by passing in ` port ` flag. Defaults to ` 50051 `
425
435
426
436
``` go
427
- lis , err := net.Listen (" tcp" , fmt.Sprintf (" localhost:%d " , port))
437
+ lis , err := net.Listen (" tcp" , fmt.Sprintf (" localhost:%d " , * port))
428
438
if err != nil {
429
439
log.Fatalf (" failed to listen: %v " , err)
430
440
}
431
- var opts []grpc.ServerOption
432
- grpcServer := grpc.NewServer (opts...)
433
441
434
- s := &routeGuideServer{}
442
+ grpcServer := grpc.NewServer ()
443
+
444
+ s := &routeGuideServer{routeNotes: make (map [string ][]*pb.RouteNote )}
435
445
s.loadFeatures ()
436
- pb.RegisterRouteGuideServer (grpcServer, newServer () )
446
+ pb.RegisterRouteGuideServer (grpcServer, s )
437
447
grpcServer.Serve (lis)
438
448
```
439
449
@@ -464,6 +474,7 @@ with the server. We create this by passing the server address and port number to
464
474
> serverAddr can be configured by passing in ` addr ` flag. Defaults to ` localhost:50051 `
465
475
466
476
``` go
477
+ // Set up a connection to the gRPC server.
467
478
conn , err := grpc.NewClient (" dns:///" +*serverAddr, grpc.WithTransportCredentials (insecure.NewCredentials ()))
468
479
if err != nil {
469
480
log.Fatalf (" fail to dial: %v " , err)
@@ -480,6 +491,7 @@ making Go function calls. We get it using the `NewRouteGuideClient` method
480
491
provided by the pb package generated from the example ` .proto ` file.
481
492
482
493
``` go
494
+ // Create a new RouteGuide stub.
483
495
client := pb.NewRouteGuideClient (conn)
484
496
```
485
497
@@ -496,19 +508,23 @@ returns a stream of geographical `Feature`s.
496
508
497
509
``` go
498
510
rect := &pb.Rectangle { ... } // initialize a pb.Rectangle
511
+ log.Printf (" Looking for features within %v " , rect)
499
512
stream , err := client.ListFeatures (context.Background (), rect)
500
513
if err != nil {
501
- ...
514
+ log. Fatalf ( " client.ListFeatures failed: %v " , err)
502
515
}
503
516
for {
504
- feature , err := stream.Recv ()
505
- if err == io.EOF {
506
- break
507
- }
508
- if err != nil {
509
- log.Fatalf (" %v .ListFeatures(_) = _, %v " , client, err)
510
- }
511
- log.Println (feature)
517
+ // For server-to-client streaming RPCs, you call stream.Recv() until it
518
+ // returns io.EOF.
519
+ feature , err := stream.Recv ()
520
+ if err == io.EOF {
521
+ break
522
+ }
523
+ if err != nil {
524
+ log.Fatalf (" client.ListFeatures failed: %v " , err)
525
+ }
526
+ log.Printf (" Feature: name: %q , point:(%v , %v )" , feature.GetName (),
527
+ feature.GetLocation ().GetLatitude (), feature.GetLocation ().GetLongitude ())
512
528
}
513
529
```
514
530
@@ -539,18 +555,20 @@ for i := 0; i < pointCount; i++ {
539
555
points = append (points, randomPoint (r))
540
556
}
541
557
log.Printf (" Traversing %d points." , len (points))
542
- stream , err := client.RecordRoute (context.Background ())
558
+ c2sStream , err := client.RecordRoute (context.TODO ())
543
559
if err != nil {
544
- log.Fatalf (" %v .RecordRoute(_) = _, %v " , client , err)
560
+ log.Fatalf (" client .RecordRoute failed: %v " , err)
545
561
}
562
+ // Stream each point to the server.
546
563
for _ , point := range points {
547
- if err := stream .Send (point); err != nil {
548
- log.Fatalf (" %v . Send(%v ) = %v " , stream , point, err)
564
+ if err := c2sStream .Send (point); err != nil {
565
+ log.Fatalf (" client.RecordRoute: stream. Send(%v ) failed: %v " , point, err)
549
566
}
550
567
}
551
- reply , err := stream.CloseAndRecv ()
568
+ // Close the stream and receive the RouteSummary from the server.
569
+ reply , err := c2sStream.CloseAndRecv ()
552
570
if err != nil {
553
- log.Fatalf (" %v .CloseAndRecv() got error %v , want %v " , stream, err, nil )
571
+ log.Fatalf (" client.RecordRoute failed: %v " , err)
554
572
}
555
573
log.Printf (" Route summary: %v " , reply)
556
574
```
@@ -572,29 +590,36 @@ return values via our method’s stream while the server is still writing messag
572
590
to their message stream.
573
591
574
592
``` go
575
- stream , err := client.RouteChat (context.Background ())
576
- waitc := make (chan struct {})
593
+ biDiStream , err := client.RouteChat (context.Background ())
594
+ if err != nil {
595
+ log.Fatalf (" client.RouteChat failed: %v " , err)
596
+ }
597
+ // this channal is used to wait for the receive goroutine to finish.
598
+ recvDoneCh := make (chan struct {})
599
+ // receive goroutine.
577
600
go func () {
578
601
for {
579
- in , err := stream .Recv ()
602
+ in , err := biDiStream .Recv ()
580
603
if err == io.EOF {
581
604
// read done.
582
- close (waitc )
605
+ close (recvDoneCh )
583
606
return
584
607
}
585
608
if err != nil {
586
- log.Fatalf (" Failed to receive a note : %v " , err)
609
+ log.Fatalf (" client.RouteChat failed : %v " , err)
587
610
}
588
611
log.Printf (" Got message %s at point(%d , %d )" , in.Message , in.Location .Latitude , in.Location .Longitude )
589
612
}
590
613
}()
614
+ // send messages simultaneously.
591
615
for _ , note := range notes {
592
- if err := stream .Send (note); err != nil {
593
- log.Fatalf (" Failed to send a note : %v " , err)
616
+ if err := biDiStream .Send (note); err != nil {
617
+ log.Fatalf (" client.RouteChat: stream.Send( %v ) failed : %v " , note , err)
594
618
}
595
619
}
596
- stream.CloseSend ()
597
- <- waitc
620
+ biDiStream.CloseSend ()
621
+ // wait for the receive goroutine to finish.
622
+ <- recvDoneCh
598
623
```
599
624
600
625
The syntax for reading and writing here is very similar to our client-side
0 commit comments