问题描述
我有一个文件服务器和一个代理服务器,通过它我可以通过 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
可能是什么问题?
解决方法
broken pipe
是链接到 errno 32 EPIPE
的 golang 消息。
在处理 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
可能在某些情况下发生,但它是由与文件服务器的连接触发的,而不是由客户端发起的连接触发。
浏览器在接收到mp4文件时,切换到流媒体模式 也就是说,它开始发送带有范围标头的 http 请求,并期望得到状态为 206
的响应我的代理服务器总是以状态 200 响应
copyHeader(w.Header(),resp.Header)
w.WriteHeader(resp.StatusCode)
这会强制浏览器断开连接