Как отправить обновление какого-либо события из http-обработчика в обработчик WebSocket - PullRequest
0 голосов
/ 11 марта 2019

Я очень плохо знаком с языком Go и пытаюсь обернуть голову вокруг каналов. Чтобы очистить свое понимание, я просмотрел видеоуроки, прочитал несколько книг, но я все еще чувствую растерянность, когда дело доходит до практического кодирования и использования каналов в веб-приложении, написанном на Go.

Я пытаюсь создать 2 URL-адреса:

  1. Обычный обычный URL GET или POST, который отображает или получает значение и обработайте это. В бэк-энде происходит некоторая обработка, и я хочу, чтобы обработка вывода для отправки в обновлении веб-сокета к тому же URL, поэтому обновление / перезагрузка окна не требуется.
  2. URL-адрес веб-сокетов на основе пакетов Gorilla.

Ниже приведен тестовый код, который я пробовал, который все еще является урезанной версией кода, который я сделал, пытаясь найти решение:

//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

Я хочу отобразить случайную строку вывода "2019/03/11 14:15:34 foo сказал что-то", как выходные данные в выводе websocket в браузере.

Я бы очень признателен за руководство или помощь.

Я думаю, что комментарии в коде, выводе терминала и скриншоте браузера для этого вопроса должны прояснить, что я пытаюсь сделать, но если этот вопрос все еще неясен, пожалуйста, дайте мне знать, я постараюсь расширить его больше.

Спасибо и всего наилучшего,

Сатиндер

Обновление 1:

Я прочитал, попробовал пример приложения чата Mat Ryer: https://github.com/matryer/goblueprints/tree/master/chapter1/chat

Вот код для копирования: https://github.com/satindergrewal/golang-practice/tree/master/chat-examples/mychat02

Из примера я понимаю, что, если у меня есть дескриптор веб-сокета, я могу направлять сообщения из этого дескриптора http веб-сокета в другие подключенные веб-клиенты. Но я до сих пор не понимаю, как отправить сообщение с http-дескриптора, не являющегося веб-сокетом, на маршрут / адрес дескриптора веб-сокета.

Я знаю, что не могу просто использовать этот код для / 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!")
        }
    }()
}

Это уже дает мне следующую ошибку:

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

Обновление 2: Попробовал иначе, после перечитывания первого ответа из комментариев снова.

// 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!")
        }
    }()
}

Теперь это не выдает ошибку, но я не вижу консоли, показывающей добавленный второй клиент, которого я ожидал, что / добавлен через командную строку.

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

Попробовал еще раз с предыдущим и новым микшированием кода, и вот результат:

// 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

Все еще чувствуя себя смущенным ...

Может кто-нибудь дать мне решение этой проблемы? Буду очень признателен за вашу помощь.

Заранее спасибо.

Сатиндер

1 Ответ

0 голосов
/ 30 марта 2019

Думаю, я решил это.По крайней мере, он делает то, что я себе представлял, чтобы код делал то, что я хотел, даже в том случае, если это может быть неправильный способ делать вещи.Правильный способ сделать что-то или эффективный способ сделать это точно, я думаю, что многие из вас, вероятно, могут исправить меня и помочь мне сделать это, я надеюсь на это, поэтому, пожалуйста, прокомментируйте и исправьте меня, если считаете, что это неправильно или неэффективно.

Вот как я решил это:

Я посмотрел на echo пример (https://github.com/gorilla/websocket/blob/master/examples/echo/client.go) примеров веб-сокетов гориллы и просто взял основной бит кода из файла clients.go, который устанавливает соединение с websocket в качестве клиента.

Моей конечной целью было отправить обновление события из другого http-дескриптора в websocket, поэтому для этого я смоделировал пример журнала печати некоторой строки в выводе консоли.

Это изменение, которое я сделал в файле main.go моего примера кода чата Mat Ryer:

// 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
            }
        }
    }()
}

Запуск этого кода после сборки двоичного файла выводит следующий пример вывода в консоль.:

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

Ниже приведен вывод, который отображается на веб-странице по адресу http://localhost:8080/

chat window screenshot

Вот полный кодобновление: https://github.com/satindergrewal/golang-practice/tree/master/chat-examples/mychat02

Так как это пример кода, который я хотел решить для моего реального приложения, которое является приложением на основе WebTTY, я смогу отправлять обновление события окончания сеанса WebTTY из какого-либо другого http-дескриптора в websocket, используябит этого кода.

Я все еще могу использовать некоторую помощь от go masters онлайн.

Нужно ли где-нибудь добавить канал рядом с этой Dialer?Я предполагаю, что знаю, но был бы признателен, если бы кто-то мог исправить какие-либо ошибки или ожидаемую неэффективность в этом.

Большое спасибо @ cerise-limón, который помог мне до сих пор.: -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...