├── README.md ├── client └── main.go ├── dockerfile ├── go.mod ├── go.sum ├── main.go └── proto ├── service.pb.go └── service.proto /README.md: -------------------------------------------------------------------------------- 1 | # docker_grpc_chat_tutorial 2 | -------------------------------------------------------------------------------- /client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "crypto/sha256" 6 | "docker_example/proto" 7 | "flag" 8 | "fmt" 9 | "os" 10 | 11 | "encoding/hex" 12 | "log" 13 | "sync" 14 | "time" 15 | 16 | "golang.org/x/net/context" 17 | "google.golang.org/grpc" 18 | ) 19 | 20 | var client proto.BroadcastClient 21 | var wait *sync.WaitGroup 22 | 23 | func init() { 24 | wait = &sync.WaitGroup{} 25 | } 26 | 27 | func connect(user *proto.User) error { 28 | var streamerror error 29 | 30 | stream, err := client.CreateStream(context.Background(), &proto.Connect{ 31 | User: user, 32 | Active: true, 33 | }) 34 | 35 | if err != nil { 36 | return fmt.Errorf("connection failed: %v", err) 37 | } 38 | 39 | wait.Add(1) 40 | go func(str proto.Broadcast_CreateStreamClient) { 41 | defer wait.Done() 42 | 43 | for { 44 | msg, err := str.Recv() 45 | if err != nil { 46 | streamerror = fmt.Errorf("Error reading message: %v", err) 47 | break 48 | } 49 | 50 | fmt.Printf("%v : %s\n", msg.Id, msg.Content) 51 | 52 | } 53 | }(stream) 54 | 55 | return streamerror 56 | } 57 | 58 | func main() { 59 | timestamp := time.Now() 60 | done := make(chan int) 61 | 62 | name := flag.String("N", "Anon", "The name of the user") 63 | flag.Parse() 64 | 65 | id := sha256.Sum256([]byte(timestamp.String() + *name)) 66 | 67 | conn, err := grpc.Dial("localhost:8080", grpc.WithInsecure()) 68 | if err != nil { 69 | log.Fatalf("Couldnt connect to service: %v", err) 70 | } 71 | 72 | client = proto.NewBroadcastClient(conn) 73 | user := &proto.User{ 74 | Id: hex.EncodeToString(id[:]), 75 | Name: *name, 76 | } 77 | 78 | connect(user) 79 | 80 | wait.Add(1) 81 | go func() { 82 | defer wait.Done() 83 | 84 | scanner := bufio.NewScanner(os.Stdin) 85 | for scanner.Scan() { 86 | msg := &proto.Message{ 87 | Id: user.Id, 88 | Content: scanner.Text(), 89 | Timestamp: timestamp.String(), 90 | } 91 | 92 | _, err := client.BroadcastMessage(context.Background(), msg) 93 | if err != nil { 94 | fmt.Printf("Error Sending Message: %v", err) 95 | break 96 | } 97 | } 98 | 99 | }() 100 | 101 | go func() { 102 | wait.Wait() 103 | close(done) 104 | }() 105 | 106 | <-done 107 | } 108 | -------------------------------------------------------------------------------- /dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine as build-env 2 | 3 | ENV GO111MODULE=on 4 | 5 | RUN apk update && apk add bash ca-certificates git gcc g++ libc-dev 6 | 7 | RUN mkdir /docker_example 8 | RUN mkdir -p /docker_example/proto 9 | 10 | WORKDIR /docker_example 11 | 12 | COPY ./proto/service.pb.go /docker_example/proto 13 | COPY ./main.go /docker_example 14 | 15 | COPY go.mod . 16 | COPY go.sum . 17 | 18 | RUN go mod download 19 | 20 | RUN go build -o docker_example . 21 | 22 | CMD ./docker_example -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module docker_example 2 | 3 | require ( 4 | github.com/golang/protobuf v1.2.0 5 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd 6 | google.golang.org/grpc v1.18.0 7 | ) 8 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 3 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 4 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 5 | github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= 6 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 7 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 8 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 9 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 10 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU= 11 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 12 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 13 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 14 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 15 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 16 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 17 | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 18 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 19 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= 20 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 21 | google.golang.org/grpc v1.18.0 h1:IZl7mfBGfbhYx2p2rKRtYgDFw6SBz+kclmxYrCksPPA= 22 | google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 23 | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 24 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "docker_example/proto" 5 | "log" 6 | "net" 7 | "os" 8 | "sync" 9 | 10 | context "golang.org/x/net/context" 11 | grpc "google.golang.org/grpc" 12 | glog "google.golang.org/grpc/grpclog" 13 | ) 14 | 15 | var grpcLog glog.LoggerV2 16 | 17 | func init() { 18 | grpcLog = glog.NewLoggerV2(os.Stdout, os.Stdout, os.Stdout) 19 | } 20 | 21 | type Connection struct { 22 | stream proto.Broadcast_CreateStreamServer 23 | id string 24 | active bool 25 | error chan error 26 | } 27 | 28 | type Server struct { 29 | Connection []*Connection 30 | } 31 | 32 | func (s *Server) CreateStream(pconn *proto.Connect, stream proto.Broadcast_CreateStreamServer) error { 33 | conn := &Connection{ 34 | stream: stream, 35 | id: pconn.User.Id, 36 | active: true, 37 | error: make(chan error), 38 | } 39 | 40 | s.Connection = append(s.Connection, conn) 41 | 42 | return <-conn.error 43 | } 44 | 45 | func (s *Server) BroadcastMessage(ctx context.Context, msg *proto.Message) (*proto.Close, error) { 46 | wait := sync.WaitGroup{} 47 | done := make(chan int) 48 | 49 | for _, conn := range s.Connection { 50 | wait.Add(1) 51 | 52 | go func(msg *proto.Message, conn *Connection) { 53 | defer wait.Done() 54 | 55 | if conn.active { 56 | err := conn.stream.Send(msg) 57 | grpcLog.Info("Sending message to: ", conn.stream) 58 | 59 | if err != nil { 60 | grpcLog.Errorf("Error with Stream: %v - Error: %v", conn.stream, err) 61 | conn.active = false 62 | conn.error <- err 63 | } 64 | } 65 | }(msg, conn) 66 | 67 | } 68 | 69 | go func() { 70 | wait.Wait() 71 | close(done) 72 | }() 73 | 74 | <-done 75 | return &proto.Close{}, nil 76 | } 77 | 78 | func main() { 79 | var connections []*Connection 80 | 81 | server := &Server{connections} 82 | 83 | grpcServer := grpc.NewServer() 84 | listener, err := net.Listen("tcp", ":8080") 85 | if err != nil { 86 | log.Fatalf("error creating the server %v", err) 87 | } 88 | 89 | grpcLog.Info("Starting server at port :8080") 90 | 91 | proto.RegisterBroadcastServer(grpcServer, server) 92 | grpcServer.Serve(listener) 93 | } 94 | -------------------------------------------------------------------------------- /proto/service.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: service.proto 3 | 4 | package proto 5 | 6 | import proto "github.com/golang/protobuf/proto" 7 | import fmt "fmt" 8 | import math "math" 9 | 10 | import ( 11 | context "golang.org/x/net/context" 12 | grpc "google.golang.org/grpc" 13 | ) 14 | 15 | // Reference imports to suppress errors if they are not otherwise used. 16 | var _ = proto.Marshal 17 | var _ = fmt.Errorf 18 | var _ = math.Inf 19 | 20 | // This is a compile-time assertion to ensure that this generated file 21 | // is compatible with the proto package it is being compiled against. 22 | // A compilation error at this line likely means your copy of the 23 | // proto package needs to be updated. 24 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 25 | 26 | type User struct { 27 | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` 28 | Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` 29 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 30 | XXX_unrecognized []byte `json:"-"` 31 | XXX_sizecache int32 `json:"-"` 32 | } 33 | 34 | func (m *User) Reset() { *m = User{} } 35 | func (m *User) String() string { return proto.CompactTextString(m) } 36 | func (*User) ProtoMessage() {} 37 | func (*User) Descriptor() ([]byte, []int) { 38 | return fileDescriptor_service_40d27f16416a858b, []int{0} 39 | } 40 | func (m *User) XXX_Unmarshal(b []byte) error { 41 | return xxx_messageInfo_User.Unmarshal(m, b) 42 | } 43 | func (m *User) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 44 | return xxx_messageInfo_User.Marshal(b, m, deterministic) 45 | } 46 | func (dst *User) XXX_Merge(src proto.Message) { 47 | xxx_messageInfo_User.Merge(dst, src) 48 | } 49 | func (m *User) XXX_Size() int { 50 | return xxx_messageInfo_User.Size(m) 51 | } 52 | func (m *User) XXX_DiscardUnknown() { 53 | xxx_messageInfo_User.DiscardUnknown(m) 54 | } 55 | 56 | var xxx_messageInfo_User proto.InternalMessageInfo 57 | 58 | func (m *User) GetId() string { 59 | if m != nil { 60 | return m.Id 61 | } 62 | return "" 63 | } 64 | 65 | func (m *User) GetName() string { 66 | if m != nil { 67 | return m.Name 68 | } 69 | return "" 70 | } 71 | 72 | type Message struct { 73 | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` 74 | Content string `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` 75 | Timestamp string `protobuf:"bytes,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` 76 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 77 | XXX_unrecognized []byte `json:"-"` 78 | XXX_sizecache int32 `json:"-"` 79 | } 80 | 81 | func (m *Message) Reset() { *m = Message{} } 82 | func (m *Message) String() string { return proto.CompactTextString(m) } 83 | func (*Message) ProtoMessage() {} 84 | func (*Message) Descriptor() ([]byte, []int) { 85 | return fileDescriptor_service_40d27f16416a858b, []int{1} 86 | } 87 | func (m *Message) XXX_Unmarshal(b []byte) error { 88 | return xxx_messageInfo_Message.Unmarshal(m, b) 89 | } 90 | func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 91 | return xxx_messageInfo_Message.Marshal(b, m, deterministic) 92 | } 93 | func (dst *Message) XXX_Merge(src proto.Message) { 94 | xxx_messageInfo_Message.Merge(dst, src) 95 | } 96 | func (m *Message) XXX_Size() int { 97 | return xxx_messageInfo_Message.Size(m) 98 | } 99 | func (m *Message) XXX_DiscardUnknown() { 100 | xxx_messageInfo_Message.DiscardUnknown(m) 101 | } 102 | 103 | var xxx_messageInfo_Message proto.InternalMessageInfo 104 | 105 | func (m *Message) GetId() string { 106 | if m != nil { 107 | return m.Id 108 | } 109 | return "" 110 | } 111 | 112 | func (m *Message) GetContent() string { 113 | if m != nil { 114 | return m.Content 115 | } 116 | return "" 117 | } 118 | 119 | func (m *Message) GetTimestamp() string { 120 | if m != nil { 121 | return m.Timestamp 122 | } 123 | return "" 124 | } 125 | 126 | type Connect struct { 127 | User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` 128 | Active bool `protobuf:"varint,2,opt,name=active,proto3" json:"active,omitempty"` 129 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 130 | XXX_unrecognized []byte `json:"-"` 131 | XXX_sizecache int32 `json:"-"` 132 | } 133 | 134 | func (m *Connect) Reset() { *m = Connect{} } 135 | func (m *Connect) String() string { return proto.CompactTextString(m) } 136 | func (*Connect) ProtoMessage() {} 137 | func (*Connect) Descriptor() ([]byte, []int) { 138 | return fileDescriptor_service_40d27f16416a858b, []int{2} 139 | } 140 | func (m *Connect) XXX_Unmarshal(b []byte) error { 141 | return xxx_messageInfo_Connect.Unmarshal(m, b) 142 | } 143 | func (m *Connect) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 144 | return xxx_messageInfo_Connect.Marshal(b, m, deterministic) 145 | } 146 | func (dst *Connect) XXX_Merge(src proto.Message) { 147 | xxx_messageInfo_Connect.Merge(dst, src) 148 | } 149 | func (m *Connect) XXX_Size() int { 150 | return xxx_messageInfo_Connect.Size(m) 151 | } 152 | func (m *Connect) XXX_DiscardUnknown() { 153 | xxx_messageInfo_Connect.DiscardUnknown(m) 154 | } 155 | 156 | var xxx_messageInfo_Connect proto.InternalMessageInfo 157 | 158 | func (m *Connect) GetUser() *User { 159 | if m != nil { 160 | return m.User 161 | } 162 | return nil 163 | } 164 | 165 | func (m *Connect) GetActive() bool { 166 | if m != nil { 167 | return m.Active 168 | } 169 | return false 170 | } 171 | 172 | type Close struct { 173 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 174 | XXX_unrecognized []byte `json:"-"` 175 | XXX_sizecache int32 `json:"-"` 176 | } 177 | 178 | func (m *Close) Reset() { *m = Close{} } 179 | func (m *Close) String() string { return proto.CompactTextString(m) } 180 | func (*Close) ProtoMessage() {} 181 | func (*Close) Descriptor() ([]byte, []int) { 182 | return fileDescriptor_service_40d27f16416a858b, []int{3} 183 | } 184 | func (m *Close) XXX_Unmarshal(b []byte) error { 185 | return xxx_messageInfo_Close.Unmarshal(m, b) 186 | } 187 | func (m *Close) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 188 | return xxx_messageInfo_Close.Marshal(b, m, deterministic) 189 | } 190 | func (dst *Close) XXX_Merge(src proto.Message) { 191 | xxx_messageInfo_Close.Merge(dst, src) 192 | } 193 | func (m *Close) XXX_Size() int { 194 | return xxx_messageInfo_Close.Size(m) 195 | } 196 | func (m *Close) XXX_DiscardUnknown() { 197 | xxx_messageInfo_Close.DiscardUnknown(m) 198 | } 199 | 200 | var xxx_messageInfo_Close proto.InternalMessageInfo 201 | 202 | func init() { 203 | proto.RegisterType((*User)(nil), "proto.User") 204 | proto.RegisterType((*Message)(nil), "proto.Message") 205 | proto.RegisterType((*Connect)(nil), "proto.Connect") 206 | proto.RegisterType((*Close)(nil), "proto.Close") 207 | } 208 | 209 | // Reference imports to suppress errors if they are not otherwise used. 210 | var _ context.Context 211 | var _ grpc.ClientConn 212 | 213 | // This is a compile-time assertion to ensure that this generated file 214 | // is compatible with the grpc package it is being compiled against. 215 | const _ = grpc.SupportPackageIsVersion4 216 | 217 | // BroadcastClient is the client API for Broadcast service. 218 | // 219 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 220 | type BroadcastClient interface { 221 | CreateStream(ctx context.Context, in *Connect, opts ...grpc.CallOption) (Broadcast_CreateStreamClient, error) 222 | BroadcastMessage(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Close, error) 223 | } 224 | 225 | type broadcastClient struct { 226 | cc *grpc.ClientConn 227 | } 228 | 229 | func NewBroadcastClient(cc *grpc.ClientConn) BroadcastClient { 230 | return &broadcastClient{cc} 231 | } 232 | 233 | func (c *broadcastClient) CreateStream(ctx context.Context, in *Connect, opts ...grpc.CallOption) (Broadcast_CreateStreamClient, error) { 234 | stream, err := c.cc.NewStream(ctx, &_Broadcast_serviceDesc.Streams[0], "/proto.Broadcast/CreateStream", opts...) 235 | if err != nil { 236 | return nil, err 237 | } 238 | x := &broadcastCreateStreamClient{stream} 239 | if err := x.ClientStream.SendMsg(in); err != nil { 240 | return nil, err 241 | } 242 | if err := x.ClientStream.CloseSend(); err != nil { 243 | return nil, err 244 | } 245 | return x, nil 246 | } 247 | 248 | type Broadcast_CreateStreamClient interface { 249 | Recv() (*Message, error) 250 | grpc.ClientStream 251 | } 252 | 253 | type broadcastCreateStreamClient struct { 254 | grpc.ClientStream 255 | } 256 | 257 | func (x *broadcastCreateStreamClient) Recv() (*Message, error) { 258 | m := new(Message) 259 | if err := x.ClientStream.RecvMsg(m); err != nil { 260 | return nil, err 261 | } 262 | return m, nil 263 | } 264 | 265 | func (c *broadcastClient) BroadcastMessage(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Close, error) { 266 | out := new(Close) 267 | err := c.cc.Invoke(ctx, "/proto.Broadcast/BroadcastMessage", in, out, opts...) 268 | if err != nil { 269 | return nil, err 270 | } 271 | return out, nil 272 | } 273 | 274 | // BroadcastServer is the server API for Broadcast service. 275 | type BroadcastServer interface { 276 | CreateStream(*Connect, Broadcast_CreateStreamServer) error 277 | BroadcastMessage(context.Context, *Message) (*Close, error) 278 | } 279 | 280 | func RegisterBroadcastServer(s *grpc.Server, srv BroadcastServer) { 281 | s.RegisterService(&_Broadcast_serviceDesc, srv) 282 | } 283 | 284 | func _Broadcast_CreateStream_Handler(srv interface{}, stream grpc.ServerStream) error { 285 | m := new(Connect) 286 | if err := stream.RecvMsg(m); err != nil { 287 | return err 288 | } 289 | return srv.(BroadcastServer).CreateStream(m, &broadcastCreateStreamServer{stream}) 290 | } 291 | 292 | type Broadcast_CreateStreamServer interface { 293 | Send(*Message) error 294 | grpc.ServerStream 295 | } 296 | 297 | type broadcastCreateStreamServer struct { 298 | grpc.ServerStream 299 | } 300 | 301 | func (x *broadcastCreateStreamServer) Send(m *Message) error { 302 | return x.ServerStream.SendMsg(m) 303 | } 304 | 305 | func _Broadcast_BroadcastMessage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 306 | in := new(Message) 307 | if err := dec(in); err != nil { 308 | return nil, err 309 | } 310 | if interceptor == nil { 311 | return srv.(BroadcastServer).BroadcastMessage(ctx, in) 312 | } 313 | info := &grpc.UnaryServerInfo{ 314 | Server: srv, 315 | FullMethod: "/proto.Broadcast/BroadcastMessage", 316 | } 317 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 318 | return srv.(BroadcastServer).BroadcastMessage(ctx, req.(*Message)) 319 | } 320 | return interceptor(ctx, in, info, handler) 321 | } 322 | 323 | var _Broadcast_serviceDesc = grpc.ServiceDesc{ 324 | ServiceName: "proto.Broadcast", 325 | HandlerType: (*BroadcastServer)(nil), 326 | Methods: []grpc.MethodDesc{ 327 | { 328 | MethodName: "BroadcastMessage", 329 | Handler: _Broadcast_BroadcastMessage_Handler, 330 | }, 331 | }, 332 | Streams: []grpc.StreamDesc{ 333 | { 334 | StreamName: "CreateStream", 335 | Handler: _Broadcast_CreateStream_Handler, 336 | ServerStreams: true, 337 | }, 338 | }, 339 | Metadata: "service.proto", 340 | } 341 | 342 | func init() { proto.RegisterFile("service.proto", fileDescriptor_service_40d27f16416a858b) } 343 | 344 | var fileDescriptor_service_40d27f16416a858b = []byte{ 345 | // 234 bytes of a gzipped FileDescriptorProto 346 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x8f, 0xcf, 0x4a, 0xc3, 0x40, 347 | 0x10, 0xc6, 0x49, 0x4c, 0x1b, 0x33, 0xad, 0x45, 0xe6, 0x20, 0xa1, 0x08, 0x4a, 0x4e, 0xe2, 0xa1, 348 | 0x84, 0xfa, 0x06, 0xcd, 0xd9, 0x83, 0x11, 0x1f, 0x60, 0xdc, 0x0c, 0xb2, 0x60, 0x76, 0xcb, 0xce, 349 | 0xd8, 0xe7, 0x97, 0x6e, 0x37, 0x15, 0xe9, 0x69, 0xf7, 0x37, 0x7f, 0xbe, 0xf9, 0x3e, 0xb8, 0x11, 350 | 0x0e, 0x07, 0x6b, 0x78, 0xb3, 0x0f, 0x5e, 0x3d, 0xce, 0xe2, 0xd3, 0x3c, 0x43, 0xf1, 0x21, 0x1c, 351 | 0x70, 0x05, 0xb9, 0x1d, 0xea, 0xec, 0x31, 0x7b, 0xaa, 0xfa, 0xdc, 0x0e, 0x88, 0x50, 0x38, 0x1a, 352 | 0xb9, 0xce, 0x63, 0x25, 0xfe, 0x9b, 0x37, 0x28, 0x5f, 0x59, 0x84, 0xbe, 0xf8, 0x62, 0xbc, 0x86, 353 | 0xd2, 0x78, 0xa7, 0xec, 0x34, 0x6d, 0x4c, 0x88, 0xf7, 0x50, 0xa9, 0x1d, 0x59, 0x94, 0xc6, 0x7d, 354 | 0x7d, 0x15, 0x7b, 0x7f, 0x85, 0x66, 0x07, 0x65, 0xe7, 0x9d, 0x63, 0xa3, 0xf8, 0x00, 0xc5, 0x8f, 355 | 0x70, 0x88, 0xa2, 0x8b, 0xed, 0xe2, 0x64, 0x73, 0x73, 0x34, 0xd7, 0xc7, 0x06, 0xde, 0xc1, 0x9c, 356 | 0x8c, 0xda, 0xc3, 0xc9, 0xd4, 0x75, 0x9f, 0xa8, 0x29, 0x61, 0xd6, 0x7d, 0x7b, 0xe1, 0xad, 0x87, 357 | 0x6a, 0x17, 0x3c, 0x0d, 0x86, 0x44, 0xb1, 0x85, 0x65, 0x17, 0x98, 0x94, 0xdf, 0x35, 0x30, 0x8d, 358 | 0xb8, 0x4a, 0x82, 0xe9, 0xdc, 0x7a, 0xe2, 0x94, 0xa8, 0xcd, 0xb0, 0x85, 0xdb, 0xf3, 0xfa, 0x39, 359 | 0xe7, 0xff, 0xa9, 0xf5, 0x72, 0x52, 0x39, 0x1e, 0xfc, 0x9c, 0x47, 0x78, 0xf9, 0x0d, 0x00, 0x00, 360 | 0xff, 0xff, 0x3f, 0x75, 0x4e, 0x77, 0x5b, 0x01, 0x00, 0x00, 361 | } 362 | -------------------------------------------------------------------------------- /proto/service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package proto; 4 | 5 | message User { 6 | string id = 1; 7 | string name = 2; 8 | } 9 | 10 | message Message { 11 | string id = 1; 12 | string content = 2; 13 | string timestamp = 3; 14 | } 15 | 16 | message Connect { 17 | User user = 1; 18 | bool active = 2; 19 | } 20 | 21 | message Close {} 22 | 23 | service Broadcast { 24 | rpc CreateStream(Connect) returns (stream Message); 25 | rpc BroadcastMessage(Message) returns (Close); 26 | } 27 | --------------------------------------------------------------------------------