io.Copy write:大文件的管道损坏

Posted

技术标签:

【中文标题】io.Copy write:大文件的管道损坏【英文标题】:io.Copy write: broken pipe for big file 【发布时间】:2021-09-22 09:46:03 【问题描述】:

我有一个文件服务器和一个代理服务器,通过它我可以通过 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 := &Serverconn
    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

可能是什么问题?

【问题讨论】:

【参考方案1】:

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

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


根据"Client closed the connection" 的意思,如果您的意图是寻找一个指示连接到您的代理的外部客户端 提前关闭了它的连接,那么您应该寻找@ 987654324@ 错误,不适用于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 可能在某些情况下发生,但它会由与您的文件服务器的连接触发,而不是由客户端发起的连接触发。

【讨论】:

感谢回答,为什么客户端可以关闭连接,如果浏览器一直发送带有范围标头的请求 为什么不呢? :) 可能有很多原因:如果你的 go 服务器前面有自己的反向代理(比如 nginx 或 apache),请检查是否没有最大响应大小参数。否则:从您正在使用的客户端(网络浏览器?)进行调试。【参考方案2】:

浏览器在接收到 mp4 文件时,切换到流媒体模式 也就是说,它开始发送带有 range 标头的 http 请求,并期望状态为 206 的响应

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

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

这会强制浏览器断开连接

【讨论】:

以上是关于io.Copy write:大文件的管道损坏的主要内容,如果未能解决你的问题,请参考以下文章

pipe.stdin.write("stop\n") 中的管道损坏

ftp_get() 损坏的大文件下载

如何在 C# 中使用 Ionic-zip 下载大文件时修复 zip 文件损坏错误

QTcpSocket::write - 如何写入大文件?

本地xshell损坏了着急拷贝服务器上的大文件怎么办?有办法lrzsz来帮忙

boost::async_write 大文件和内存消耗