2

我有一个文件服务器和一个代理服务器,通过它我可以通过 http 访问文件服务器

简单的文件服务器:


    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        http.ServeFile(w, r, "tmp/1.mp4")
    })

    if err := http.ListenAndServe("localhost:3000", nil); err != http.ErrServerClosed {
        log.Fatalf("HTTP server ListenAndServe: %v", err)
    }

和代理服务器:


type Server struct {
    conn net.Conn
}

func (s *Server) Handle(w http.ResponseWriter, r *http.Request) error {
    if err := r.Write(s.conn); err != nil {
        log.Fatal("req.Write ", err)
    }

    resp, err := http.ReadResponse(bufio.NewReader(s.conn), r)
    if err != nil {
        return fmt.Errorf("http.ReadResponse: %s", err)
    }

    defer func() {
        if resp.Body != nil {
            if err := resp.Body.Close(); err != nil && err != io.ErrUnexpectedEOF {
                fmt.Printf("resp.Body Close error: %s", err)
            }
        }
    }()

    copyHeader(w.Header(), resp.Header)
    w.WriteHeader(resp.StatusCode)

    if _, err := io.Copy(w, resp.Body); err != nil {
        if err == io.ErrUnexpectedEOF {
            fmt.Println("Client closed the connection, couldn't copy response")
        } else {
            fmt.Printf("copy err: %s\n", err)
        }
    }
    return nil
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if err := s.Handle(w, r); err != nil {
        http.Error(w, err.Error(), http.StatusBadGateway)
    }
}

func copyHeader(dst, src http.Header) {
    for k, v := range src {
        vv := make([]string, len(v))
        copy(vv, v)
        dst[k] = vv
    }
}

func main() {

    conn, err := net.Dial("tcp", "localhost:3000")

    if err != nil {
        fmt.Errorf("net.Dial(tcp, localhost:3000) %s", err)
    }
    server := &Server{conn}
    log.Fatal(http.ListenAndServe("localhost:8000", server))

}

如果文件服务器分发小图片,那么如果文件服务器正在提供大视频文件(1.mp4),则一切正常,然后我收到错误:

copy err: readfrom tcp 127.0.0.1:8000->127.0.0.1:58964: write tcp 127.0.0.1:8000->127.0.0.1:58964: write: broken pipe

可能是什么问题呢 ?

4

2 回答 2

2

broken pipe是链接到 errno 32 的 golang 消息EPIPE

在处理 TCP 连接时,当您的进程在另一端已经关闭连接时写入连接时会触发它。


根据您的意思"Client closed the connection",如果您的意图是寻找连接到您的代理的外部客户端提前关闭其连接的指标,那么您应该寻找EPIPE错误,而不是io.ErrUnexpectedEOF.

就像是 :

func isEPIPE(err error) bool {
    var syscallErr syscall.Errno
    b := errors.As(err, &syscallErr) && syscallErr == syscall.EPIPE

    return b
}


...

if isEPIPE(err) {
    fmt.Println("Client closed the connection, couldn't copy response")
} else {
    fmt.Printf("copy err: %s\n", err)
}

io.ErrUnexpectedEOF在某些情况下可能会发生,但它会由与您的文件服务器的连接触发,而不是由客户端发起的连接触发。

于 2021-07-13T12:21:52.067 回答
0

浏览器在接收到一个mp4文件时,切换到流模式,即开始发送带有range header的http请求,并期望得到一个状态为206的响应

我的代理服务器总是以状态 200 响应

 copyHeader(w.Header(), resp.Header)
 w.WriteHeader(resp.StatusCode)

这会强制浏览器断开连接

于 2021-07-14T20:40:13.187 回答