draw62188 2019-03-11 01:31
浏览 99

如何将一些事件更新从http处理程序发送到WebSocket处理程序

I'm very new to Go language and trying to wrap my head around channels. To clear my understanding I have watched video tutorials, read some books, but I'm still feeling confused when comes to practical coding and use of channels in web app coded in Go.

What I'm trying to do is to have 2 URLS:

  1. The usual normal GET or POST URL which displays or gets value and process it. In the back-end does some processing and I wish that processing output to be sent out in a websocket update to the same URL, so that no window refresh/reload is required.
  2. A Gorilla package based websockets URL.

Following is the so far test code I tried, which is still a stripped down version of the mess of code I made trying to make a solution:

//file main.go
package main

import (
    "io"
    "net/http"
    "fmt"
    "math/rand"
    "log"
    "time"
    "github.com/gorilla/websocket"
)


func logging(f http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        log.Println(r.URL.Path)
        if r.URL.Path == `/ws` {
            log.Println("WebSocket is accessed from ws://localhost:8080/ws")
        }
        f(w, r)
    }
}

type hotcat int

func (c hotcat) ServeHTTP(res http.ResponseWriter, req *http.Request) {
    io.WriteString(res, "cat cat cat")

    //Some code here who's output I want to pass to websockets url ws://localhost:8080/ws
    n := timeConsumingWork(4)
    fmt.Println("Random Number Print from cat: ", n)
    //Example the value of n I need to pass to ws://localhost:8080/ws, how can I do it?

    // Some other example test code just giving some random output from hotcat http handler
    // Would like to pass it's output to ws://localhost:8080/ws to print in websocckets output in browser
    go func(){
        out := make(chan string)
        go func(){
            for i := 0; ; i++ {
                out <- `foo said something`
                time.Sleep(time.Duration(rand.Intn(2e3)) * time.Millisecond)
            }
            //out <- `foo said something`
        }()
        printer(out)
    }()
}

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin: func(r *http.Request) bool {
        return true
    },
}

// Execute this in browser console to initiate websococket connection and to send ws.send() commands etc.
/*
var ws = new WebSocket("ws://localhost:8080/ws")
ws.addEventListener("message", function(e) {console.log(e);});
ws.onmessage = function (event) {
    console.log(event.data);
}

ws.send("foo")
ws.send(JSON.stringify({username: "Sat"}))

ws.readyState
ws.CLOSED
ws.OPEN
ws.close()
*/
func ws(w http.ResponseWriter, r *http.Request) {
    socket, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        fmt.Println(err)
        return
    }
    for {
        msgType, msg, err := socket.ReadMessage()
        if err != nil {
            fmt.Println(err)
            return
        }
        fmt.Println(string(msg))
        if err = socket.WriteMessage(msgType, msg); err != nil {
            fmt.Println(err)
        }
    }
}

func main() {
    var c hotcat

    http.Handle("/cat", c)
    http.HandleFunc("/ws", logging(ws))

    http.ListenAndServe(":8080", nil)
}


func timeConsumingWork(n int) int {
    time.Sleep(time.Microsecond * time.Duration(rand.Intn(500)))
    return n + rand.Intn(1000)
}


func printer(in <-chan string) {
    //log.Println(<-in)
    go func() {
        for {
            log.Println(<-in)
        }
    }()
}
# command shell output
$ go run main.go 

Random Number Print from cat:  891
2019/03/11 14:15:32 foo said something
2019/03/11 14:15:33 /ws
2019/03/11 14:15:33 WebSocket is accessed from ws://localhost:8080/ws
foo
2019/03/11 14:15:34 foo said something
2019/03/11 14:15:34 foo said something
2019/03/11 14:15:34 foo said something
2019/03/11 14:15:36 foo said something
2019/03/11 14:15:36 foo said something
^Csignal: interrupt
$ 

websocket command input from browser

I want to display the random output string "2019/03/11 14:15:34 foo said something" like outputs in websocket output in browser.

I'd really appreciate some direction or help.

I think the comments in the code, terminal output, and browser screenshot for this question should clear what I'm trying to do, but if this question is still unclear, please let me know, I'll try expanding on it more.

Thanks & Regards,

Satinder

Update 1:

I read, tried the Mat Ryer example of chat application: https://github.com/matryer/goblueprints/tree/master/chapter1/chat

Here's may code copy: https://github.com/satindergrewal/golang-practice/tree/master/chat-examples/mychat02

From the example I understand that if I have a web socket handle I can have the messages channeled from that websocket http handle to other connected web clients. But I'm still confused how I can send a message from a non-websocket http handle to a websocket handle route/address.

I know I can not just use this code for /'s ServeHTTP:

// ServeHTTP handles the HTTP request.
func (t *templateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    t.once.Do(func() {
        t.templ = template.Must(template.ParseFiles(filepath.Join("templates", t.filename)))
    })
    fmt.Println(r.Host)
    t.templ.Execute(w, r)
    var room *room
    socket, _ := upgrader.Upgrade(w, r, nil)
    client := &client{
        socket: socket,
        send:   make(chan []byte, messageBufferSize),
        room:   room,
    }
    room.join <- client
    go func() {
        for i := 0; i < 10; i++ {
            time.Sleep(time.Duration(rand.Intn(8e3)) * time.Millisecond)
            client.socket.WriteMessage(websocket.TextMessage, []byte("Hello from / ServeHTTP Handle"))
            //fmt.Println("Sending automatic hello from root ServeHTTP handle to web page!")
        }
    }()
}

It already gives me the following error:

localhost:8080
2019/03/25 11:19:18 http: superfluous response.WriteHeader call from github.com/gorilla/websocket.(*Upgrader).returnError (server.go:81)
2019/03/25 11:19:18 http: panic serving [::1]:52691: runtime error: invalid memory address or nil pointer dereference
goroutine 39 [running]:
net/http.(*conn).serve.func1(0xc00013e140)
        /usr/local/Cellar/go/1.12.1/libexec/src/net/http/server.go:1769 +0x139
panic(0x13a3e00, 0x172dc20)
        /usr/local/Cellar/go/1.12.1/libexec/src/runtime/panic.go:522 +0x1b5
main.(*templateHandler).ServeHTTP(0xc00008ef60, 0x14954a0, 0xc0001dc380, 0xc000214200)
        /Users/satinder/go/src/golang-practice/chat-examples/mychat02/main.go:40 +0x209
net/http.(*ServeMux).ServeHTTP(0x173cfa0, 0x14954a0, 0xc0001dc380, 0xc000214200)
        /usr/local/Cellar/go/1.12.1/libexec/src/net/http/server.go:2375 +0x1d6
net/http.serverHandler.ServeHTTP(0xc000130000, 0x14954a0, 0xc0001dc380, 0xc000214200)
        /usr/local/Cellar/go/1.12.1/libexec/src/net/http/server.go:2774 +0xa8
net/http.(*conn).serve(0xc00013e140, 0x1495ba0, 0xc0000a0380)
        /usr/local/Cellar/go/1.12.1/libexec/src/net/http/server.go:1878 +0x851
created by net/http.(*Server).Serve
        /usr/local/Cellar/go/1.12.1/libexec/src/net/http/server.go:2884 +0x2f4

Update 2: Tried different, after re-reading the first reply from comments again.

// ServeHTTP handles the HTTP request.
func (t *templateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    t.once.Do(func() {
        t.templ = template.Must(template.ParseFiles(filepath.Join("templates", t.filename)))
    })
    fmt.Println(r.Host)
    t.templ.Execute(w, r)

    room := newRoom()
    go func() {
        for i := 0; i < 10; i++ {
            time.Sleep(time.Duration(rand.Intn(8e3)) * time.Millisecond)
            room.forward <- []byte("Hello from / ServeHTTP Handle")
            //client.socket.WriteMessage(websocket.TextMessage, []byte("Hello from / ServeHTTP Handle"))
            fmt.Println("Sending automatic hello from root ServeHTTP handle to web page!")
        }
    }()
}

Now it doesn't give error, but I don't see the console showing the second client added which I was expecting that / added via command line.

GoldenBook:mychat02 satinder$ go build -o chat 
GoldenBook:mychat02 satinder$ ./chat 
2019/03/25 11:37:49 Starting web server on :8080
localhost:8080
New client joined
^C

Tried again with previous and new code mix and this is the result:

// ServeHTTP handles the HTTP request.
func (t *templateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    t.once.Do(func() {
        t.templ = template.Must(template.ParseFiles(filepath.Join("templates", t.filename)))
    })
    fmt.Println(r.Host)
    t.templ.Execute(w, r)

    room := newRoom()
    socket, _ := upgrader.Upgrade(w, r, nil)
    client := &client{
        socket: socket,
        send:   make(chan []byte, messageBufferSize),
        room:   room,
    }
    room.join <- client
    go func() {
        for i := 0; i < 10; i++ {
            time.Sleep(time.Duration(rand.Intn(8e3)) * time.Millisecond)
            room.forward <- []byte("Hello from / ServeHTTP Handle")
            //client.socket.WriteMessage(websocket.TextMessage, []byte("Hello from / ServeHTTP Handle"))
            //fmt.Println("Sending automatic hello from root ServeHTTP handle to web page!")
        }
    }()
}
GoldenBook:mychat02 satinder$ go build -o chat 
GoldenBook:mychat02 satinder$ ./chat 
2019/03/25 11:40:44 Starting web server on :8080
localhost:8080
2019/03/25 11:40:50 http: superfluous response.WriteHeader call from github.com/gorilla/websocket.(*Upgrader).returnError (server.go:81)
^C

Still feeling confused...

Can someone please give me a solution to this? Would really really appreciate your help.

Thanks in advance.

Satinder

  • 写回答

1条回答 默认 最新

  • donglikuang8145 2019-03-30 15:04
    关注

    Think I solved it. At least it is doing what I imagined the code to do what I wanted it to do, even in case it might not be correct way of doing things. Correct way of doing things or efficient way of doing this exact thing, I think many of you can probably correct me and help me to do so, I hope so, so please comment and correct me if you think it's wrong or inefficient.

    This is how I solved it:

    I looked at the echo example (https://github.com/gorilla/websocket/blob/master/examples/echo/client.go) of gorilla's websocket examples, and just took the basic bit of code from clients.go file, which is makes a connection to websocket as a client.

    My end goal was send the event update from another http Handle to the websocket, so for that I was simulating the print log example of some string in console output.

    This is the change I did in the main.go file of my Mat Ryer chat example code:

    // ServeHTTP handles the HTTP request.
    func (t *templateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        t.once.Do(func() {
            t.templ = template.Must(template.ParseFiles(filepath.Join("templates", t.filename)))
        })
        fmt.Println(r.Host)
        t.templ.Execute(w, r)
    
        // Creating the URL scheme to use with websocket Dialer
        // to connnect to ws://localhost:8080/room
        u := url.URL{Scheme: "ws", Host: "localhost:8080", Path: "/room"}
        log.Printf("connecting to %s", u.String())
    
        // Initiate the websocket connection from the go code **as a client** to connect to the chat room
        c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
        if err != nil {
            log.Fatal("dial:", err)
        }
    
        go func() {
            for i := 0; i < 10; i++ {
                time.Sleep(time.Duration(rand.Intn(8e3)) * time.Millisecond)
                // Just printing the log of the same message in command line. Might better to ignore it.
                // log.Println("Sending automatic hello from root ServeHTTP handle to web page!")
    
                // Write the Message as Text message to the web socket connection
                // which will show up in the chat box
                err := c.WriteMessage(websocket.TextMessage, []byte("Sending automatic hello from root ServeHTTP handle to web page!"))
                if err != nil {
                    log.Println("write:", err)
                    return
                }
            }
        }()
    }
    

    Running this code after building the binary prints the following example output in console:

    GoldenBook:mychat02 satinder$ ./chat 
    2019/03/31 03:44:27 Starting web server on :8080
    localhost:8080
    2019/03/31 03:44:31 connecting to ws://localhost:8080/room
    New client joined
    New client joined
    2019/03/31 03:44:33 Sending automatic hello from root ServeHTTP handle to web page!
    Message received: Sending automatic hello from root ServeHTTP handle to web page!
    -- sent to client
    -- sent to client
    2019/03/31 03:44:36 Sending automatic hello from root ServeHTTP handle to web page!
    Message received: Sending automatic hello from root ServeHTTP handle to web page!
    -- sent to client
    -- sent to client
    2019/03/31 03:44:43 Sending automatic hello from root ServeHTTP handle to web page!
    Message received: Sending automatic hello from root ServeHTTP handle to web page!
    -- sent to client
    -- sent to client
    2019/03/31 03:44:45 Sending automatic hello from root ServeHTTP handle to web page!
    Message received: Sending automatic hello from root ServeHTTP handle to web page!
    -- sent to client
    -- sent to client
    2019/03/31 03:44:45 Sending automatic hello from root ServeHTTP handle to web page!
    Message received: Sending automatic hello from root ServeHTTP handle to web page!
    -- sent to client
    -- sent to client
    Message received: HELLO
    -- sent to client
    -- sent to client
    2019/03/31 03:44:48 Sending automatic hello from root ServeHTTP handle to web page!
    Message received: Sending automatic hello from root ServeHTTP handle to web page!
    -- sent to client
    -- sent to client
    2019/03/31 03:44:48 Sending automatic hello from root ServeHTTP handle to web page!
    Message received: Sending automatic hello from root ServeHTTP handle to web page!
    -- sent to client
    -- sent to client
    2019/03/31 03:44:49 Sending automatic hello from root ServeHTTP handle to web page!
    Message received: Sending automatic hello from root ServeHTTP handle to web page!
    -- sent to client
    -- sent to client
    2019/03/31 03:44:52 Sending automatic hello from root ServeHTTP handle to web page!
    Message received: Sending automatic hello from root ServeHTTP handle to web page!
    -- sent to client
    -- sent to client
    ^C
    

    And following is the output which shows in the web page at http://localhost:8080/

    chat window screenshot

    Here's the full code update: https://github.com/satindergrewal/golang-practice/tree/master/chat-examples/mychat02

    Since this is the example code, which I wanted to solve for my actual application, which is a WebTTY based app, I'll be able to send the WebTTY session end event update from some other http Handle to websocket using this code bit.

    I can still use some help from go masters online.

    Do I need to add the channel close to this Dialer somewhere? I guess I do, but would appreciate if someone can fix any errors or expected inefficiencies in it.

    Thanks a lot to @cerise-limón who helped me so far. :-)

    评论

报告相同问题?

悬赏问题

  • ¥15 delta降尺度计算的一些细节,有偿
  • ¥15 Arduino红外遥控代码有问题
  • ¥15 数值计算离散正交多项式
  • ¥30 数值计算均差系数编程
  • ¥15 redis-full-check比较 两个集群的数据出错
  • ¥15 Matlab编程问题
  • ¥15 训练的多模态特征融合模型准确度很低怎么办
  • ¥15 kylin启动报错log4j类冲突
  • ¥15 超声波模块测距控制点灯,灯的闪烁很不稳定,经过调试发现测的距离偏大
  • ¥15 import arcpy出现importing _arcgisscripting 找不到相关程序