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.

Reply via email to