英文:
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 (
"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)
}
*/
}
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 (
"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")
}
And the html file as:
<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>
答案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, "data: %s\n\n", <-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("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:
// the client disconnected
return
case m := <-p.logs:
if _, err := fmt.Fprintf(w, "data: %s\n\n", m); err != nil {
// Write to connection failed. Subsequent writes will probably fail.
return
}
flusher.Flush()
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论