|
@@ -0,0 +1,160 @@
|
|
|
+// Skeleton to part 7 of the Whispering Gophers code lab.
|
|
|
+//
|
|
|
+// This program extends part 6 by adding a Peers type.
|
|
|
+// The rest of the code is left as-is, so functionally there is no change.
|
|
|
+//
|
|
|
+// However we have added a peers_test.go file, so that running
|
|
|
+// go test
|
|
|
+// from the package directory will test your implementation of the Peers type.
|
|
|
+//
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "bufio"
|
|
|
+ "encoding/json"
|
|
|
+ "flag"
|
|
|
+ "fmt"
|
|
|
+ "log"
|
|
|
+ "net"
|
|
|
+ "os"
|
|
|
+ "sync"
|
|
|
+
|
|
|
+ "code.google.com/p/whispering-gophers/util"
|
|
|
+)
|
|
|
+
|
|
|
+var (
|
|
|
+ peerAddr = flag.String("peer", "", "peer host:port")
|
|
|
+ self string
|
|
|
+)
|
|
|
+
|
|
|
+type Message struct {
|
|
|
+ Addr string
|
|
|
+ Body string
|
|
|
+}
|
|
|
+
|
|
|
+func main() {
|
|
|
+ flag.Parse()
|
|
|
+
|
|
|
+ l, err := util.Listen()
|
|
|
+ if err != nil {
|
|
|
+ log.Fatal(err)
|
|
|
+ }
|
|
|
+ self = l.Addr().String()
|
|
|
+ log.Println("Listening on", self)
|
|
|
+
|
|
|
+ go dial(*peerAddr)
|
|
|
+ go readInput()
|
|
|
+
|
|
|
+ for {
|
|
|
+ c, err := l.Accept()
|
|
|
+ if err != nil {
|
|
|
+ log.Fatal(err)
|
|
|
+ }
|
|
|
+ go serve(c)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+type Peers struct {
|
|
|
+ m map[string]chan<- Message
|
|
|
+ mu sync.RWMutex
|
|
|
+}
|
|
|
+
|
|
|
+// Add creates and returns a new channel for the given peer address.
|
|
|
+// If an address already exists in the registry, it returns nil.
|
|
|
+func (p *Peers) Add(addr string) <-chan Message {
|
|
|
+ // TODO: Take the write lock on p.mu. Unlock it before returning (using defer).
|
|
|
+ defer p.mu.Unlock()
|
|
|
+ p.mu.Lock()
|
|
|
+
|
|
|
+ // TODO: Check if the address is already in the peers map under the key addr.
|
|
|
+ if _, ok := p.m[addr] ; ok {
|
|
|
+ // TODO: If it is, return nil.
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO: Make a new channel of messages
|
|
|
+ messages := make(chan Message)
|
|
|
+
|
|
|
+ // TODO: Add it to the peers map
|
|
|
+ p.m[addr] = messages
|
|
|
+
|
|
|
+ // TODO: Return the newly created channel.
|
|
|
+ return messages
|
|
|
+}
|
|
|
+
|
|
|
+// Remove deletes the specified peer from the registry.
|
|
|
+func (p *Peers) Remove(addr string) {
|
|
|
+ // TODO: Take the write lock on p.mu. Unlock it before returning (using defer).
|
|
|
+ defer p.mu.Unlock()
|
|
|
+ p.mu.Lock()
|
|
|
+
|
|
|
+ // TODO: Delete the peer from the peers map.
|
|
|
+ delete(p.m, addr)
|
|
|
+}
|
|
|
+
|
|
|
+// List returns a slice of all active peer channels.
|
|
|
+func (p *Peers) List() []chan<- Message {
|
|
|
+ // TODO: Take the read lock on p.mu. Unlock it before returning (using defer).
|
|
|
+ defer p.mu.RUnlock()
|
|
|
+ p.mu.RLock()
|
|
|
+
|
|
|
+ // TODO: Declare a slice of chan<- Message.
|
|
|
+ s := make([]chan <- Message, 0)
|
|
|
+
|
|
|
+ /* TODO: Iterate over the map using range */
|
|
|
+ for _, v := range p.m {
|
|
|
+ // TODO: Append each channel into the slice.
|
|
|
+ s = append(s, v)
|
|
|
+ }
|
|
|
+ // TODO: Return the slice.
|
|
|
+ return s
|
|
|
+}
|
|
|
+
|
|
|
+func serve(c net.Conn) {
|
|
|
+ defer c.Close()
|
|
|
+ d := json.NewDecoder(c)
|
|
|
+ for {
|
|
|
+ var m Message
|
|
|
+ err := d.Decode(&m)
|
|
|
+ if err != nil {
|
|
|
+ log.Println(err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ fmt.Printf("%#v\n", m)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+var peer = make(chan Message)
|
|
|
+
|
|
|
+func readInput() {
|
|
|
+ s := bufio.NewScanner(os.Stdin)
|
|
|
+ for s.Scan() {
|
|
|
+ m := Message{
|
|
|
+ Addr: self,
|
|
|
+ Body: s.Text(),
|
|
|
+ }
|
|
|
+ peer <- m
|
|
|
+ }
|
|
|
+ if err := s.Err(); err != nil {
|
|
|
+ log.Fatal(err)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func dial(addr string) {
|
|
|
+ c, err := net.Dial("tcp", addr)
|
|
|
+ if err != nil {
|
|
|
+ log.Println(addr, err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ defer c.Close()
|
|
|
+
|
|
|
+ e := json.NewEncoder(c)
|
|
|
+
|
|
|
+ for m := range peer {
|
|
|
+ err := e.Encode(m)
|
|
|
+ if err != nil {
|
|
|
+ log.Println(addr, err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|