http.Server的奇怪行为 - 为每个客户端建立三个连接?

时间:2017-11-19 15:10:37

标签: http go

我是一个菜鸟Go程序员。我目前正在编写一个将数据流式传输到客户端的Web应用程序。为了测试它是否运行良好,我编写了这段代码(streaming.go):

package main

import (
    "fmt"
    "net"
    "net/http"
    "time"
)

func handler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("X-Content-Type-Options", "nosniff")

    fmt.Println("Client connected!")

    flusher, ok := w.(http.Flusher)
    if !ok {
        fmt.Println("ResponseWriter doesn't implement Flusher interface")
        return
    }

    closeNotifier, ok := w.(http.CloseNotifier)
    if !ok {
        fmt.Println("ResponseWriter doesn't implement CloseNotifier interface")
        return
    }
    closeNotifyChannel := closeNotifier.CloseNotify()

    for {
        fmt.Println("Sending data chunk...")
        fmt.Fprintf(w, "Chunk.")
        flusher.Flush()

        select {
        case <-closeNotifyChannel:
            goto closed
        default:
            time.Sleep(500 * time.Millisecond)
        }
    }

closed:
    fmt.Println("Client disconnected")
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", handler)

    server := &http.Server{
        Addr:    "localhost:8000",
        Handler: mux,
        ConnState: func(conn net.Conn, state http.ConnState) {
            fmt.Printf("[ConnState] %v: ", conn.RemoteAddr())

            switch state {
            case http.StateNew:
                fmt.Println("StateNew")
            case http.StateActive:
                fmt.Println("StateActive")
            case http.StateIdle:
                fmt.Println("StateIdle")
            case http.StateHijacked:
                fmt.Println("StateHijacked")
            case http.StateClosed:
                fmt.Println("StateClosed")
            }
        },
    }

    server.ListenAndServe()
}

它的作用是: 1.当客户端已连接时,获取http.Flusherhttp.CloseNofier实例http.ResponseWriter 2.发送少量数据(块)直到客户端断开连接(在本例中我使用了简单的字符串,"Chunk."

当我在Chrome浏览器中输入127.0.0.1:8000时,以下是此计划的示例输出:

[ConnState] 127.0.0.1:57226: StateNew
[ConnState] 127.0.0.1:57227: StateNew
[ConnState] 127.0.0.1:57228: StateNew
[ConnState] 127.0.0.1:57226: StateActive
Client connected!
Sending data chunk...
Sending data chunk...
Sending data chunk...
Sending data chunk...
Sending data chunk...
Sending data chunk...
Sending data chunk...
[ConnState] 127.0.0.1:57227: StateActive
Client connected!
Sending data chunk...
Sending data chunk...
Client disconnected
[ConnState] 127.0.0.1:57226: StateIdle
[ConnState] 127.0.0.1:57226: StateClosed
Sending data chunk...
Sending data chunk...
Sending data chunk...
Sending data chunk...
...(forever)

那些127.0.0.1:57227127.0.0.1:57228的黑客是什么?它们不会在客户端连接后立即激活,但是当我在浏览器中按ESC键时,会激活一个。我的处理程序将数据发送到哪里?

我想知道Connection: keep-alive(似乎Chrome会自动为请求添加此标头)会让这种情况发生,请告诉我我缺少的内容。感谢。

1 个答案:

答案 0 :(得分:3)

我不认为你在这里遗漏任何东西。 Chrome可能已经打开了与您的服务器的多个连接,可能希望可能有许多并发请求到远程服务器。 Chrome可以向服务器开放最多6个连接,以便与该服务器并行发出请求。但是由于测试中只有一个来自浏览器的GET调用,因此数据传输只发生在打开的那些套接字(或连接)中。由于2个连接(套接字)没有获取数据,它们可能会因客户端或服务器端的空闲超时而关闭。 如果从Linux / Mac等运行该程序,您可以使用Tcpdump检查数据如何通过哪个套接字传输。或者,您也可以使用Wireshark检查HTTP或TCP流量,这将使您清楚地了解这些套接字上发生的情况。 可能是一些tcpdump命令,如下所示,将有助于:

  $ sudo tcpdump -i lo0 port 8000  -s 65534 -A
    Eg. tcpdump -i <interface> port <some port> -s <max-packet-capture-size>  -A