github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/talks/2012/chat/both/chat.go (about) 1 // +build OMIT 2 3 package main 4 5 import ( 6 "fmt" 7 "io" 8 "log" 9 "net" 10 "net/http" 11 "time" 12 13 "golang.org/x/net/websocket" 14 ) 15 16 const listenAddr = "localhost:4000" 17 18 func main() { 19 go netListen() // HL 20 http.HandleFunc("/", rootHandler) 21 http.Handle("/socket", websocket.Handler(socketHandler)) 22 err := http.ListenAndServe(listenAddr, nil) 23 if err != nil { 24 log.Fatal(err) 25 } 26 } 27 28 func netListen() { 29 l, err := net.Listen("tcp", "localhost:4001") 30 if err != nil { 31 log.Fatal(err) 32 } 33 for { 34 c, err := l.Accept() 35 if err != nil { 36 log.Fatal(err) 37 } 38 go match(c) 39 } 40 } 41 42 type socket struct { 43 io.Reader 44 io.Writer 45 done chan bool 46 } 47 48 func (s socket) Close() error { 49 s.done <- true 50 return nil 51 } 52 53 var chain = NewChain(2) // 2-word prefixes 54 55 func socketHandler(ws *websocket.Conn) { 56 r, w := io.Pipe() 57 go func() { 58 _, err := io.Copy(io.MultiWriter(w, chain), ws) 59 w.CloseWithError(err) 60 }() 61 s := socket{r, ws, make(chan bool)} 62 go match(s) 63 <-s.done 64 } 65 66 var partner = make(chan io.ReadWriteCloser) 67 68 func match(c io.ReadWriteCloser) { 69 fmt.Fprint(c, "Waiting for a partner...") 70 select { 71 case partner <- c: 72 // now handled by the other goroutine 73 case p := <-partner: 74 chat(p, c) 75 case <-time.After(5 * time.Second): 76 chat(Bot(), c) 77 } 78 } 79 80 func chat(a, b io.ReadWriteCloser) { 81 fmt.Fprintln(a, "Found one! Say hi.") 82 fmt.Fprintln(b, "Found one! Say hi.") 83 errc := make(chan error, 1) 84 go cp(a, b, errc) 85 go cp(b, a, errc) 86 if err := <-errc; err != nil { 87 log.Println(err) 88 } 89 a.Close() 90 b.Close() 91 } 92 93 func cp(w io.Writer, r io.Reader, errc chan<- error) { 94 _, err := io.Copy(w, r) 95 errc <- err 96 } 97 98 // Bot returns an io.ReadWriteCloser that responds to 99 // each incoming write with a generated sentence. 100 func Bot() io.ReadWriteCloser { 101 r, out := io.Pipe() // for outgoing data 102 return bot{r, out} 103 } 104 105 type bot struct { 106 io.ReadCloser 107 out io.Writer 108 } 109 110 func (b bot) Write(buf []byte) (int, error) { 111 go b.speak() 112 return len(buf), nil 113 } 114 115 func (b bot) speak() { 116 time.Sleep(time.Second) 117 msg := chain.Generate(10) // at most 10 words 118 b.out.Write([]byte(msg)) 119 }