如何在服务器开始监听后启动浏览器?

20

在Go中,我如何在服务器开始监听后启动浏览器?

最好是尽可能简单的方式。

到目前为止,我的代码已经非常简化了:

package main

import (  
    // Standard library packages
    "fmt"
    "net/http"
    "github.com/skratchdot/open-golang/open"
    // Third party packages
    "github.com/julienschmidt/httprouter"
)


// go get github.com/toqueteos/webbrowser

func main() {  
    // Instantiate a new router
    r := httprouter.New()

    // Add a handler on /test
    r.GET("/test", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
        // Simply write some test data for now
        fmt.Fprint(w, "Welcome!\n")
    })
    
    //open.Run("https://google.com/")
     
    // open.Start("https://google.com")

    // http://127.0.0.1:3000/test
    // Fire up the server
    http.ListenAndServe("localhost:3000", r)
    fmt.Println("ListenAndServe is blocking")  
    open.RunWith("http://localhost:3000/test", "firefox")  
    fmt.Println("Done")
}

4
在 Go 语言中,将 "阻塞式" 的 http.ListenAndServe 改为 "非阻塞式" 并不是很复杂:只需使用 go http.ListenAndServe(...) ,加上适当的错误处理即可。那么问题究竟在哪里呢? - Volker
@Volker:问题是,这是我在Go中迈出的第一步,所以我对它不太熟悉,否则我会创建一个新线程并在打开浏览器前睡眠几毫秒;而且目前我有其他事情要做;) 另外,如果ListenAndServer是非阻塞的,那么它将在主程序退出时立即退出(据我所知)。 - Stefan Steiger
只需在单独的Go协程中运行http.ListenAndServe(),然后休眠一段时间(例如200毫秒),最后在浏览器中打开即可。 - Eric
3个回答

32

打开监听器,启动浏览器,然后进入服务器循环:

l, err := net.Listen("tcp", "localhost:3000")
if err != nil {
    log.Fatal(err)
}

// The browser can connect now because the listening socket is open.

err := open.Start("http://localhost:3000/test")
if err != nil {
     log.Println(err)
}

// Start the blocking server loop.

log.Fatal(http.Serve(l, r)) 

不需要像另一个答案所示那样进行轮询。如果浏览器在启动之前听取套接字打开,则浏览器将连接。

ListenAndServe是一个便利函数,它打开一个套接字并调用Serve。此答案中的代码将这些步骤拆分,以便在监听开始后但在阻塞Serve调用之前打开浏览器。


有趣,我喜欢!甚至不需要线程。非常好。 - Stefan Steiger
2
这确实是正确的答案。监听套接字具有内部固定大小的挂起队列,即使在给定时间没有“接受”正在积极等待,它也可以接受连接。这就是为什么它能够正常工作的原因。 - tomasz
3
server.ListenAndServe 似乎也使用了 tcpKeepAliveListener。它似乎将套接字的 TCP keepalive 设置为 3 分钟。你也应该这样做。 - 0xcaff

14

如果没有错误,则 http.ListenAndServe() 将永远不会返回。因此,您不应该在此之后添加除处理失败的代码以外的代码。

您需要启动一个新的 goroutine,因此在一个 goroutine 中调用 ListenAndServe(),并且检查其是否正常工作的代码应该在另一个 goroutine 中运行。

您可以通过向它发出简单的 HTTP GET 调用来检查服务器是否已经运行,例如使用 http.Get()

以下示例故意延迟 7 秒钟启动时间。新的 goroutine 启动一个无限循环,检查服务器是否正在运行,在尝试之间暂停 1 秒钟。

示例:

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hi!"))
})

go func() {
    for {
        time.Sleep(time.Second)

        log.Println("Checking if started...")
        resp, err := http.Get("http://localhost:8081")
        if err != nil {
            log.Println("Failed:", err)
            continue
        }
        resp.Body.Close()
        if resp.StatusCode != http.StatusOK {
            log.Println("Not OK:", resp.StatusCode)
            continue
        }

        // Reached this point: server is up and running!
        break
    }
    log.Println("SERVER UP AND RUNNING!")
}()

log.Println("Starting server...")
time.Sleep(time.Second * 7)
log.Fatal(http.ListenAndServe(":8081", nil))

示例输出:

2015/09/23 13:53:03 Starting server...
2015/09/23 13:53:04 Checking if started...
2015/09/23 13:53:06 Failed: Get http://localhost:8081: dial tcp [::1]:8081: connectex: No connection could be made because the target machine actively refused it.
2015/09/23 13:53:07 Checking if started...
2015/09/23 13:53:09 Failed: Get http://localhost:8081: dial tcp [::1]:8081: connectex: No connection could be made because the target machine actively refused it.
2015/09/23 13:53:10 Checking if started...
2015/09/23 13:53:10 SERVER UP AND RUNNING!

0

API并不是绝对糟糕的,但我们可以说“需要一些时间来适应”。以下是如何在Server结构上使用自定义属性:

s := &http.Server{
    Addr:           cnf.API_SERVER_ADDRESS,
    Handler:        h,
    ReadTimeout:    0, // 1 * time.Minute,
    WriteTimeout:   30 * time.Minute,
    MaxHeaderBytes: 1 << 20,
}

go func() {

    l, err := net.Listen("tcp", cnf.API_SERVER_ADDRESS)

    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(`{"server_state":"listening"}`)
    log.Fatal(s.Serve(l));
}()

因为如果你使用以下代码:

http.Serve(l, handler)

那么你就不能在服务器上定义自定义属性了


我该如何关闭服务器? - yoka791

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接