hi, thanks for answers, indeed, at least for the prompt() rl library is useful.
About syscall, i kind of experienced the mess of not closing rl... : ) Despite all, I feel like its hopeless, to make a race free program, close the resources properly before quitting. In a way or another the program always exits with some pending loops waiting for a blocking call. Find both client code, and multi-echo-server code, server package main import ( "flag" "log" "math/rand" "net/http" "time" "github.com/gorilla/websocket" ) var addr = flag.String("addr", "localhost:8080", "http service address") var upgrader = websocket.Upgrader{} // use default options func echo(w http.ResponseWriter, r *http.Request) { c, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Print("upgrade:", err) return } defer c.Close() for { mt, message, err := c.ReadMessage() if err != nil { log.Println("read:", err) break } log.Printf("recv: %s", message) e := rand.Intn(10) + 1 for i := 0; i < e; i++ { err = c.WriteMessage(mt, message) if err != nil { log.Println("write:", err) break } <-time.After(time.Millisecond * time.Duration(rand.Intn(500))) } } } func main() { flag.Parse() log.SetFlags(0) http.HandleFunc("/", echo) log.Fatal(http.ListenAndServe(*addr, nil)) } client package main import ( "flag" "fmt" // "bufio" // "os" "io" "net/http" "time" "github.com/chzyer/readline" ws "github.com/gorilla/websocket" ) func dial(url string, origin string, subprotocol string) (*ws.Conn, error) { var subprotocols []string var header http.Header if subprotocol != "" { subprotocols = []string{subprotocol} } if origin != "" { header = http.Header{"Origin": {origin}} } dialer := ws.Dialer{ Subprotocols: subprotocols, ReadBufferSize: 1024, WriteBufferSize: 1024, } conn, _, err := dialer.Dial(url, header) return conn, err } func writeConn(conn *ws.Conn) (chan<- []byte, chan error) { write := make(chan []byte) errc := make(chan error) go func() { for { data := <-write err := conn.WriteMessage(ws.TextMessage, data) if err != nil { errc <- err } } }() return write, errc } func readLine(rl *readline.Instance) (<-chan []byte, chan<- bool, chan error ) { read := make(chan []byte) errc := make(chan error) want := make(chan bool) go func() { for { select { case <-want: line, err := rl.Readline() if err == io.EOF { fmt.Println("EOF") } else if err != nil { errc <- err } else { read <- []byte(line) } } } }() return read, want, errc } func readConn(conn *ws.Conn) (<-chan []byte, chan error) { read := make(chan []byte) errc := make(chan error) go func() { for { _, data, err := conn.ReadMessage() if err != nil { errc <- err } read <- data } }() return read, errc } func main() { var url = flag.String("url", "ws://localhost:8080/", "url") var origin = flag.String("origin", "", "optional origin") var subprotocol = flag.String("subprotocol", "", "optional subprotocol") flag.Parse() if *url == "" { flag.Usage() return } conn, err := dial(*url, *origin, *subprotocol) if err != nil { panic(err) } rl, err := readline.New("> ") if err != nil { panic(err) } write, writeErr := writeConn(conn) read, readErr := readConn(conn) lines, want, rlErr := readLine(rl) terminate := func() { conn.Close() rl.Close() } want <- true for { select { case line := <-lines: if len(line) == 0 { rl.Write([]byte("Type in a message!\n")) want <- true } else if string(line) == "quit" { rl.Write([]byte("Bye!\n")) terminate() return } else { write <- line want <- true } case res := <-read: msg := fmt.Sprintf("response: %v\n", string(res)) rl.Write([]byte(msg)) case err := <-rlErr: if err == readline.ErrInterrupt { rl.Write([]byte("Interrupted\n")) } else { msg := fmt.Sprintf("error while reading stdin, %v\n", err) rl.Write([]byte(msg)) } terminate() return case err := <-readErr: msg := fmt.Sprintf("error while reading WS, %v\n", err) rl.Write([]byte(msg)) terminate() return case err := <-writeErr: msg := fmt.Sprintf("error while writing WS, %v\n", err) rl.Write([]byte(msg)) terminate() return } } <-time.After(time.Second * 1) } Le dimanche 25 septembre 2016 15:19:53 UTC+2, KwangYul Seo a écrit : > > Hi, > > Thanks for the feedback! Please check the comments below. > > On Sun, Sep 25, 2016 at 7:25 PM, mhhcbon <cpasmabo...@gmail.com > <javascript:>> wrote: > >> Hi, >> >> The code below looks like to make the job, based on the second version i >> posted, but with RL library. >> >> BTW, just wondering, >> >> why don t you use standard bufio+os.Stdin ? >> > > Because I would like to use features provided by readline such as history > and editing mode. > > >> >> Why a syscall.Kill rather than os.Exit ? >> >> syscall.Kill(syscall.Getpid(), syscall.SIGINT) >> >> >> > If you terminate the program by calling os.Exit(), the readline library > can't perform proper terminal cleanup. > > >> the code, >> >> package main >> >> import ( >> "flag" >> "fmt" >> // "bufio" >> // "os" >> "net/http" >> "io" >> >> "github.com/chzyer/readline" >> >> ws "github.com/gorilla/websocket" >> ) >> >> >> func dial(url string, origin string, subprotocol string) (*ws.Conn, error >> ) { >> var subprotocols []string >> var header http.Header >> >> if subprotocol != "" { >> subprotocols = []string{subprotocol} >> } >> if origin != "" { >> header = http.Header{"Origin": {origin}} >> } >> >> dialer := ws.Dialer{ >> Subprotocols: subprotocols, >> ReadBufferSize: 1024, >> WriteBufferSize: 1024, >> } >> >> conn, _, err := dialer.Dial(url, header) >> return conn, err >> } >> >> func writeConn (conn *ws.Conn) (chan<-[]byte, chan bool, chan error) { >> write := make(chan []byte) >> done := make(chan bool) >> errc := make(chan error) >> go func () { >> for { >> select { >> case data := <-write: >> err := conn.WriteMessage(ws.TextMessage, data) >> if err !=nil { >> errc <- err >> } >> case <-done: >> close(write) >> close(done) >> close(errc) >> return >> } >> } >> }() >> return write, done, errc >> } >> >> func readLine () (<-chan []byte, chan<-bool, chan bool, chan error) { >> read := make(chan []byte) >> done := make(chan bool) >> errc := make(chan error) >> want := make(chan bool) >> go func () { >> >> rl, err := readline.New("> ") >> if err!=nil{ >> errc <- err >> return >> } >> // reader := bufio.NewReader(os.Stdin) >> for { >> select { >> case <-want: >> // data, err := reader.ReadBytes('\n') >> // if err != nil { >> // errc <- err >> // } >> line, err := rl.Readline() >> if err == io.EOF { >> fmt.Println("EOF") >> } else if err != nil { >> errc <- err >> // log.Fatalf("ReadLine: %v", err) >> } else { >> read<-[]byte(line) >> } >> // read<-data >> case <-done: >> close(read) >> close(done) >> close(errc) >> rl.Close() >> >> return >> } >> } >> }() >> return read, want, done, errc >> } >> >> func readConn (conn *ws.Conn) (<-chan []byte, chan<-bool, chan bool, >> chan error) { >> read := make(chan []byte) >> done := make(chan bool) >> want := make(chan bool) >> errc := make(chan error) >> go func () { >> for { >> select { >> case <-want: >> _, data, err := conn.ReadMessage() >> if err != nil { >> errc <- err >> } >> read<-data >> case <-done: >> close(read) >> close(done) >> close(errc) >> return >> } >> } >> }() >> return read, want, done, errc >> } >> >> >> func main () { >> >> var url = flag.String("url", "ws://echo.websocket.org", "url") >> var origin = flag.String("origin", "", "optional origin") >> var subprotocol = flag.String("subprotocol", "", "optional >> subprotocol") >> flag.Parse() >> if *url == "" { >> flag.Usage() >> return >> } >> >> conn, err := dial(*url, *origin, *subprotocol) >> if err!=nil { >> panic(err) >> } >> >> write, writeEnd, writeErr := writeConn(conn) >> read, wantRes, readEnd, readErr := readConn(conn) >> rl, wantRl, rlEnd, rlErr := readLine() >> >> terminate := func () { >> rlEnd<-true >> readEnd<-true >> writeEnd<-true >> conn.Close() >> } >> >> fmt.Print("> ") >> for { >> select { >> case wantRl <- true: >> case line := <-rl: >> >> // line = line[:len(line)-1] >> if len(line)==0 { >> fmt.Printf("Type in a message!\n") >> fmt.Print("> ") >> >> } else if string(line)=="quit" { >> fmt.Println("Bye!") >> terminate() >> return >> >> } else { >> write<-line >> wantRes <- true >> } >> case res := <-read: >> fmt.Printf("response: %v\n", string(res)) >> fmt.Print("> ") >> >> case err := <-rlErr: >> if err == readline.ErrInterrupt { >> fmt.Println("Interrupted") >> } else { >> fmt.Printf("error while reading stdin, %v\n", err) >> } >> terminate() >> return >> case err := <-readErr: >> fmt.Printf("error while reading WS, %v\n", err) >> terminate() >> return >> case err := <-writeErr: >> fmt.Printf("error while writing WS, %v\n", err) >> terminate() >> return >> } >> } >> } >> >> >> >> -- >> You received this message because you are subscribed to the Google Groups >> "golang-nuts" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to golang-nuts...@googlegroups.com <javascript:>. >> For more options, visit https://groups.google.com/d/optout. >> > > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.