将时间推送到浏览器使用通道

huangapple go评论65阅读模式
英文:

Pushing time to browser using channel

问题

我正在尝试使用通道将时间推送到浏览器,所以我写了以下代码:

package main

import (
	"net/http"
	"time"
)

type DataPasser struct {
	logs chan string
}

func main() {
	passer := &DataPasser{logs: make(chan string)}
	go func() {
		for {
			passer.logs <- time.Now().String()
		}
	}()

	http.HandleFunc("/", passer.handleHello)
	http.ListenAndServe(":9999", nil)

}

func (p *DataPasser) handleHello(w http.ResponseWriter, r *http.Request) {
	for {
		w.Write([]byte(<-p.logs))
	}
	/*	for {
			io.WriteString(w, <-p.logs)
		}
	*/
}

它的工作原理是每次有新的时间时,都会添加新的行,如下所示:
将时间推送到浏览器使用通道

我需要的是获取单行,每次服务器发送时间时都会清除并替换为新的时间。有什么帮助吗?

更新

我尝试使用SSE(服务器发送事件),但没有成功:

package main

import (
	"net/http"
	"time"
)

type DataPasser struct {
	logs chan string
}

func main() {
	passer := &DataPasser{logs: make(chan string)}
	t := time.NewTicker(time.Second)
	defer t.Stop()

	go func() {
		for range t.C {
			passer.logs <- time.Now().String()
		}
	}()

	http.HandleFunc("/", passer.handleHello)
	http.ListenAndServe(":9999", nil)

}

func (p *DataPasser) handleHello(w http.ResponseWriter, r *http.Request) {
	setupCORS(&w, r)
	w.Header().Set("Content-Type", "text/event-stream")
	w.Header().Set("Cache-Control", "no-cache")
	w.Header().Set("Connection", "keep-alive")
	flusher, ok := w.(http.Flusher)
	if !ok {
		http.Error(w, "Internal error", 500)
		return
	}
	for {
		w.Write([]byte(<-p.logs))
		//	c := []byte(<-p.logs)
		//	fmt.Fprint(w, c)
		flusher.Flush()
	}
	/*	for {
			io.WriteString(w, <-p.logs)
		}
	*/
	//  w.Write([]byte("Hi, from Service: " + ws.name))

}

func setupCORS(w *http.ResponseWriter, req *http.Request) {
	(*w).Header().Set("Cache-Control", "no-cache")
	(*w).Header().Set("Access-Control-Allow-Origin", "*")
	(*w).Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
	(*w).Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}

HTML 文件如下:

<html>
<head></head>
<body>
    <div id="counter"  width="500" height="600">
</body>
    <script>
        var source = new EventSource("http://localhost:9999/");
        source.onmessage = function (event) {
            console.log(event)
            var counter = event.data; // JSON.parse(event.data);
            document.getElementById("counter").innerHTML = counter;
        }
    </script>
</html>
英文:

I'm trying to push the time to the browser using channels, so I wrote the below:

package main

import (
	&quot;net/http&quot;
	&quot;time&quot;
)

type DataPasser struct {
	logs chan string
}

func main() {
	passer := &amp;DataPasser{logs: make(chan string)}
	go func() {
		for {
			passer.logs &lt;- time.Now().String()
		}
	}()

	http.HandleFunc(&quot;/&quot;, passer.handleHello)
	http.ListenAndServe(&quot;:9999&quot;, nil)

}

func (p *DataPasser) handleHello(w http.ResponseWriter, r *http.Request) {
	for {
		w.Write([]byte(&lt;-p.logs))
	}
	/*	for {
			io.WriteString(w, &lt;-p.logs)
		}
	*/
}

It worked by kept adding new lines with each new time, as below:
将时间推送到浏览器使用通道

What I need is to get single line, that is cleared and replaced with the new time everytime the server sending time to it? any help?

UPDATE

I tried using SSE server sent event, as below but did not work:

package main

import (
	&quot;net/http&quot;
	&quot;time&quot;
)

type DataPasser struct {
	logs chan string
}

func main() {
	passer := &amp;DataPasser{logs: make(chan string)}
	t := time.NewTicker(time.Second)
	defer t.Stop()

	go func() {
		for range t.C {
			passer.logs &lt;- time.Now().String()
		}
	}()

	http.HandleFunc(&quot;/&quot;, passer.handleHello)
	http.ListenAndServe(&quot;:9999&quot;, nil)

}

func (p *DataPasser) handleHello(w http.ResponseWriter, r *http.Request) {
	setupCORS(&amp;w, r)
	w.Header().Set(&quot;Content-Type&quot;, &quot;text/event-stream&quot;)
	w.Header().Set(&quot;Cache-Control&quot;, &quot;no-cache&quot;)
	w.Header().Set(&quot;Connection&quot;, &quot;keep-alive&quot;)
	flusher, ok := w.(http.Flusher)
	if !ok {
		http.Error(w, &quot;Internal error&quot;, 500)
		return
	}
	for {
		w.Write([]byte(&lt;-p.logs))
		//	c := []byte(&lt;-p.logs)
		//	fmt.Fprint(w, c)
		flusher.Flush()
	}
	/*	for {
			io.WriteString(w, &lt;-p.logs)
		}
	*/
	//  w.Write([]byte(&quot;Hi, from Service: &quot; + ws.name))

}

func setupCORS(w *http.ResponseWriter, req *http.Request) {
	(*w).Header().Set(&quot;Cache-Control&quot;, &quot;no-cache&quot;)
	(*w).Header().Set(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;)
	(*w).Header().Set(&quot;Access-Control-Allow-Methods&quot;, &quot;POST, GET, OPTIONS, PUT, DELETE&quot;)
	(*w).Header().Set(&quot;Access-Control-Allow-Headers&quot;, &quot;Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization&quot;)
}

And the html file as:

&lt;html&gt;
&lt;head&gt;&lt;/head&gt;
&lt;body&gt;
    &lt;div id=&quot;counter&quot;  width=&quot;500&quot; height=&quot;600&quot;&gt;
&lt;/body&gt;
    &lt;script&gt;
        var source = new EventSource(&quot;http://localhost:9999/&quot;);
        source.onmessage = function (event) {
            console.log(event)
            var counter = event.data; // JSON.parse(event.data);
            document.getElementById(&quot;counter&quot;).innerHTML = counter;
        }
    &lt;/script&gt;
&lt;/html&gt;

答案1

得分: 0

应用程序必须以text/event-stream格式编写响应:

fmt.Fprintf(w, "data: %s\n\n", <-p.logs)

还有其他问题。当客户端断开连接或写入响应时出现错误时,处理程序应该退出。处理程序应该在等待第一个事件之前刷新标头。以下是更新后的代码:

func (p *DataPasser) handleHello(w http.ResponseWriter, r *http.Request) {
    setupCORS(w, r)
    w.Header().Set("Content-Type", "text/event-stream")
    flusher, ok := w.(http.Flusher)
    if !ok {
        http.Error(w, "Internal error", 500)
        return
    }
    flusher.Flush()
    done := r.Context().Done()
    defer fmt.Println("EXIT")
    for {
        select {
        case <-done:
            // 客户端断开连接
            return
        case m := <-p.logs:
            if _, err := fmt.Fprintf(w, "data: %s\n\n", m); err != nil {
                // 写入连接失败。后续写入可能会失败。
                return
            }
            flusher.Flush()
        }
    }
}
英文:

The application must write the response in the text/event-stream format:

fmt.Fprintf(w, &quot;data: %s\n\n&quot;, &lt;-p.logs)

There are other problems. The handler should exit when the client disconnects or when there's an error writing to the response. The handler should flush the headers before waiting for the first event. Here's the updated code:

func (p *DataPasser) handleHello(w http.ResponseWriter, r *http.Request) {
setupCORS(w, r)
w.Header().Set(&quot;Content-Type&quot;, &quot;text/event-stream&quot;)
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, &quot;Internal error&quot;, 500)
return
}
flusher.Flush()
done := r.Context().Done()
defer fmt.Println(&quot;EXIT&quot;)
for {
select {
case &lt;-done:
// the client disconnected
return
case m := &lt;-p.logs:
if _, err := fmt.Fprintf(w, &quot;data: %s\n\n&quot;, m); err != nil {
// Write to connection failed. Subsequent writes will probably fail.
return
}
flusher.Flush()
}
}
}

huangapple
  • 本文由 发表于 2022年6月12日 23:21:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/72593518.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定