123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- /*
- *
- * Copyright 2015 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
- //go:generate protoc -I ../routeguide --go_out=plugins=grpc:../routeguide ../routeguide/route_guide.proto
- // Package main implements a simple gRPC server that demonstrates how to use gRPC-Go libraries
- // to perform unary, client streaming, server streaming and full duplex RPCs.
- //
- // It implements the route guide service whose definition can be found in routeguide/route_guide.proto.
- package main
- import (
- "context"
- "encoding/json"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "math"
- "net"
- "sync"
- "time"
- "google.golang.org/grpc"
- "google.golang.org/grpc/credentials"
- "google.golang.org/grpc/testdata"
- "github.com/golang/protobuf/proto"
- pb "google.golang.org/grpc/examples/route_guide/routeguide"
- )
- var (
- tls = flag.Bool("tls", false, "Connection uses TLS if true, else plain TCP")
- certFile = flag.String("cert_file", "", "The TLS cert file")
- keyFile = flag.String("key_file", "", "The TLS key file")
- jsonDBFile = flag.String("json_db_file", "testdata/route_guide_db.json", "A json file containing a list of features")
- port = flag.Int("port", 10000, "The server port")
- )
- type routeGuideServer struct {
- savedFeatures []*pb.Feature // read-only after initialized
- mu sync.Mutex // protects routeNotes
- routeNotes map[string][]*pb.RouteNote
- }
- // GetFeature returns the feature at the given point.
- func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) {
- for _, feature := range s.savedFeatures {
- if proto.Equal(feature.Location, point) {
- return feature, nil
- }
- }
- // No feature was found, return an unnamed feature
- return &pb.Feature{Location: point}, nil
- }
- // ListFeatures lists all features contained within the given bounding Rectangle.
- func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error {
- for _, feature := range s.savedFeatures {
- if inRange(feature.Location, rect) {
- if err := stream.Send(feature); err != nil {
- return err
- }
- }
- }
- return nil
- }
- // RecordRoute records a route composited of a sequence of points.
- //
- // It gets a stream of points, and responds with statistics about the "trip":
- // number of points, number of known features visited, total distance traveled, and
- // total time spent.
- func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) error {
- var pointCount, featureCount, distance int32
- var lastPoint *pb.Point
- startTime := time.Now()
- for {
- point, err := stream.Recv()
- if err == io.EOF {
- endTime := time.Now()
- return stream.SendAndClose(&pb.RouteSummary{
- PointCount: pointCount,
- FeatureCount: featureCount,
- Distance: distance,
- ElapsedTime: int32(endTime.Sub(startTime).Seconds()),
- })
- }
- if err != nil {
- return err
- }
- pointCount++
- for _, feature := range s.savedFeatures {
- if proto.Equal(feature.Location, point) {
- featureCount++
- }
- }
- if lastPoint != nil {
- distance += calcDistance(lastPoint, point)
- }
- lastPoint = point
- }
- }
- // RouteChat receives a stream of message/location pairs, and responds with a stream of all
- // previous messages at each of those locations.
- func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error {
- for {
- in, err := stream.Recv()
- if err == io.EOF {
- return nil
- }
- if err != nil {
- return err
- }
- key := serialize(in.Location)
- s.mu.Lock()
- s.routeNotes[key] = append(s.routeNotes[key], in)
- // Note: this copy prevents blocking other clients while serving this one.
- // We don't need to do a deep copy, because elements in the slice are
- // insert-only and never modified.
- rn := make([]*pb.RouteNote, len(s.routeNotes[key]))
- copy(rn, s.routeNotes[key])
- s.mu.Unlock()
- for _, note := range rn {
- if err := stream.Send(note); err != nil {
- return err
- }
- }
- }
- }
- // loadFeatures loads features from a JSON file.
- func (s *routeGuideServer) loadFeatures(filePath string) {
- file, err := ioutil.ReadFile(filePath)
- if err != nil {
- log.Fatalf("Failed to load default features: %v", err)
- }
- if err := json.Unmarshal(file, &s.savedFeatures); err != nil {
- log.Fatalf("Failed to load default features: %v", err)
- }
- }
- func toRadians(num float64) float64 {
- return num * math.Pi / float64(180)
- }
- // calcDistance calculates the distance between two points using the "haversine" formula.
- // The formula is based on http://mathforum.org/library/drmath/view/51879.html.
- func calcDistance(p1 *pb.Point, p2 *pb.Point) int32 {
- const CordFactor float64 = 1e7
- const R float64 = float64(6371000) // earth radius in metres
- lat1 := toRadians(float64(p1.Latitude) / CordFactor)
- lat2 := toRadians(float64(p2.Latitude) / CordFactor)
- lng1 := toRadians(float64(p1.Longitude) / CordFactor)
- lng2 := toRadians(float64(p2.Longitude) / CordFactor)
- dlat := lat2 - lat1
- dlng := lng2 - lng1
- a := math.Sin(dlat/2)*math.Sin(dlat/2) +
- math.Cos(lat1)*math.Cos(lat2)*
- math.Sin(dlng/2)*math.Sin(dlng/2)
- c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
- distance := R * c
- return int32(distance)
- }
- func inRange(point *pb.Point, rect *pb.Rectangle) bool {
- left := math.Min(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude))
- right := math.Max(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude))
- top := math.Max(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude))
- bottom := math.Min(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude))
- if float64(point.Longitude) >= left &&
- float64(point.Longitude) <= right &&
- float64(point.Latitude) >= bottom &&
- float64(point.Latitude) <= top {
- return true
- }
- return false
- }
- func serialize(point *pb.Point) string {
- return fmt.Sprintf("%d %d", point.Latitude, point.Longitude)
- }
- func newServer() *routeGuideServer {
- s := &routeGuideServer{routeNotes: make(map[string][]*pb.RouteNote)}
- s.loadFeatures(*jsonDBFile)
- return s
- }
- func main() {
- flag.Parse()
- lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))
- if err != nil {
- log.Fatalf("failed to listen: %v", err)
- }
- var opts []grpc.ServerOption
- if *tls {
- if *certFile == "" {
- *certFile = testdata.Path("server1.pem")
- }
- if *keyFile == "" {
- *keyFile = testdata.Path("server1.key")
- }
- creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
- if err != nil {
- log.Fatalf("Failed to generate credentials %v", err)
- }
- opts = []grpc.ServerOption{grpc.Creds(creds)}
- }
- grpcServer := grpc.NewServer(opts...)
- pb.RegisterRouteGuideServer(grpcServer, newServer())
- grpcServer.Serve(lis)
- }
|