http.FileServer的坑

在windows上起了个文件服务器,放了各种常用的镜像、软件工具在上面供大家下载,之前都还好,这两天有同事反馈一个5.01G的包一直卡在99%下载不了,看了好久才发现go在windows上有如下限制

line22: // Note that sendfile for windows does not support >2GB file.

filename :sendfile_windows.go

花了我20分钟时间排查。。。

暂时换了个ubuntu系统把文件盘映射过去了,如哪位大侠有其他方法,烦请留言指导一下哈,谢谢啦。

已邀请:

chenqinghe

赞同来自: huhuyou2

我看了下,windows上有2GB限制,主要是因为调用了windows的系统调用TransmitFile。这个系统调用能够在内核层面将数据写入socket而不用经过用户层,提高了效率。TransmitFile的函数原型:

BOOL TransmitFile(
  SOCKET                  hSocket,
  HANDLE                  hFile,
  DWORD                   nNumberOfBytesToWrite,
  DWORD                   nNumberOfBytesPerSend,
  LPOVERLAPPED            lpOverlapped,
  LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,
  DWORD                   dwReserved
);

nNumberOfBytesToWrite是个DWORD,用go的话来说就是个uint32,那为什么文件大小限制不是4G呢? 因为此系统调用可能还会调用其他函数,在有些函数中,负数是有特殊含义的,因此最大值应该为int32,也就是2G。(此条待考证,不保证正确性及准确性)

下面说下怎么解决:(只是个思路,没有具体尝试)

查看sendfile调用链,可以看到只有net.TCPConn在ReadFrom函数中在调用,那么可以把ReadFrom函数重写,不要调用那个系统调用就好了,虽然效率肯定会差,但瓶颈不在这的话就无所谓了。具体大致实现如下:

package main

import (
    "net"
    "net/http"
)

func main() {
    var addr *net.TCPAddr
    l, err := net.ListenTCP("tcp", addr)
    checkerr(err)
    http.Serve(&listener{l}, nil)
}

type listener struct {
    *net.TCPListener
}

func (l *listener) Accept() (net.Conn, error) {
    conn, err := l.TCPListener.Accept()
    checkerr(err)
    return &tcpconn{conn}, nil
}

type tcpconn struct {
    net.Conn
}

func (c *tcpconn) ReadFrom() {
    // .....
}

func checkerr(e error) {
    // ...
}

要回复问题请先登录注册