2

我正在尝试实现一个全局按钮计数器,该计数器会随着任何/不同用户单击它而更新。所以这个想法是,如果一个人点击按钮,我会在我的页面实例上看到计数器更新。

我目前正在使用长轮询技术,或者我认为是这样,但经过审查,我相信我在将更新“广播”到所有浏览器时出错。

当前的错误是,例如,如果我打开了两个浏览器,并且我不断单击一个浏览器,那么我单击按钮的那个浏览器只会更新一半时间。它将得到 1 3 5 等,而其他浏览器显示 2 4 6 等。

在线查看后,我认为这可能与频道和网站上所有浏览器的广播有关。如果有人可以帮助我举例说明我如何每次将更新发送到所有浏览器,我将不胜感激。

客户:

<html>
<script language=javascript>

function longpoll(url, callback) {

    var req = new XMLHttpRequest (); 
    req.open ('GET', url, true); 

    req.onreadystatechange = function (aEvt) {
        if (req.readyState == 4) { 
            if (req.status == 200) {
                callback(req.responseText);
                longpoll(url, callback);
            } else {
                alert ("long-poll connection lost");
            }
        }
    };

    req.send(null);
}

function recv(msg) {

    var box = document.getElementById("counter");

    box.innerHTML += msg + "\n";
}
function send() {


    var box = document.getElementById("counter");

  var req = new XMLHttpRequest (); 
    req.open ('POST', "/push?rcpt=", true); 

    req.onreadystatechange = function (aEvt) {
        if (req.readyState == 4) { 
            if (req.status == 200) {
            } else {
                alert ("failed to send!");
            }
        }
  };
  req.send("hi")

  //box.innerHTML += "test" ;  
}
</script>
<body onload="longpoll('/poll', recv);">

<h1> Long-Poll Chat Demo </h1>

<p id="counter"></p>
<button onclick="send()" id="test">Test Button</button>
</body>
</html>

服务器:

package main

import (
    "net/http"
    "log"
    "io"
//  "io/ioutil"
  "strconv"
)

var messages chan string = make(chan string, 100)

var counter = 0

func PushHandler(w http.ResponseWriter, req *http.Request) {

    //body, err := ioutil.ReadAll(req.Body)

    /*if err != nil {
        w.WriteHeader(400)
    }*/
    counter += 1
    messages <- strconv.Itoa(counter)
}


func PollResponse(w http.ResponseWriter, req *http.Request) {

    io.WriteString(w, <-messages)
}

func main() {
    http.Handle("/", http.FileServer(http.Dir("./")))
    http.HandleFunc("/poll", PollResponse)
    http.HandleFunc("/push", PushHandler)
    err := http.ListenAndServe(":8010", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}
4

2 回答 2

8

问题不在于 Go 代码(单独;参见 PS PS),而是浏览器(Chrome)。向同一个 URL 发出 2 个请求是按顺序发生的,而不是并行发生的。

解决方案 您需要在 longpoll URL 中添加一个唯一的时间戳来欺骗浏览器:

req.open ('GET', url+"?"+(new Date().getTime()), true); 

PS - 通过这个问题,我学到了很多关于 Go 通道和互斥锁的知识。谢谢 :)

PS PS - James 的回答(https://stackoverflow.com/a/19803051/143225)是让服务器端 Go 代码一次处理多个请求的关键,因为 Go 通道被阻塞,这意味着只有 1 个 goroutine可以一次收到。所以OP的问题的解决方案是前端和后端代码更改的组合。

于 2013-11-06T07:00:22.583 回答
6

Go 频道不是多播的。也就是说,如果你有多个从通道读取的 goroutine,当你向通道写入值而不是广播给所有读者时,只有一个会唤醒。

一种替代方法是使用条件变量:

var (
    lock sync.Mutex
    cond = sync.NewCond(&lock)
)

在您的PollResponse处理程序中,您可以等待条件:

lock.Lock()
cond.Wait()
message := strconv.Itoa(counter)
lock.Unlock()
// write message to response

在您的PushHandler处理程序中,您可以使用以下命令广播更改:

lock.Lock()
counter += 1
cond.Broadcast()
lock.Unlock()
于 2013-11-06T02:37:08.017 回答