如何在go语言中为http客户端设置套接​​字选项(IP_TOS)?

Man*_*aha 4 ssl http go

我是去语言的新手。我计划使用 go 语言来开发 http 客户端/服务器。在浏览 http 客户端包中支持的功能列表时,我没有找到在包中设置套接字选项的方法(可能是我只是不知道如何使用它)。我需要在调用 http 客户端连接之前在 fd 中设置 DSCP 选项(IP_TOS)。(虽然我找到了 syscall 选项来设置套接字选项,但我没有找到从 http 包中获取 fd 的方法)。

在 http 服务器端,可以设置套接字选项 (IP_TOS)。代码摘录:

       tcpListener,err := net.ListenTCP("tcp4", addr)
        if  err != nil {
             //fmt.Println("error in listen", err.error())
             log.Fatal("net.ListenTCP()", err)
        }
        //get lisenet socket fd
        f, _ := tcpListener.File()
        err = syscall.SetsockoptInt(int(f.Fd()), syscall.SOL_SOCKET, syscall.IP_TOS, 128)
Run Code Online (Sandbox Code Playgroud)

在http客户端, 无法获取套接字fd并设置套接字选项(IP_TOS):(我想在调用NewRequest之前设置IP_TOS)

     client := &http.Client{
                         Transport : tr,
                         //Timeout: time.Duration(10) * time.Second,
       }

     request, err := http.NewRequest("POST", url, body)
       if err != nil {
           panic(err)
       }

      response, err := client.Do(request)
Run Code Online (Sandbox Code Playgroud)

谢谢 !!

Ain*_*r-G 5

您可以DialContext为自己http.RoundTripper的自己创建自己的*http.Client

dial := func(ctx context.Context, network, addr string) (net.Conn, error) {
    conn, err := (&net.Dialer{
        Timeout:   30 * time.Second,
        KeepAlive: 30 * time.Second,
    }).DialContext(ctx, network, addr)
    if err != nil {
        return nil, err
    }

    tcpConn, ok := conn.(*net.TCPConn)
    if !ok {
        err = errors.New("conn is not tcp")
        return nil, err
    }

    f, err := tcpConn.File()
    if err != nil {
        return nil, err
    }

    err = syscall.SetsockoptInt(int(f.Fd()), syscall.IPPROTO_IP, syscall.IP_TOS, 128)
    if err != nil {
        return nil, err
    }

    return conn, nil
}
tr := &http.Transport{
    Proxy:                 http.ProxyFromEnvironment,
    DialContext:           dial,
    MaxIdleConns:          100,
    IdleConnTimeout:       90 * time.Second,
    TLSHandshakeTimeout:   10 * time.Second,
    ExpectContinueTimeout: 1 * time.Second,
}
c := &http.Client{
    Transport: tr,
}
resp, err := c.Get("https://google.com/")
if err != nil {
    log.Fatalf("GET error: %v", err)
}

log.Printf("got %q", resp.Status)
Run Code Online (Sandbox Code Playgroud)

编辑:如果您需要在连接实际发生之前设置选项,您可以在您的DialContext.

tcpAddr, err := net.ResolveTCPAddr(network, addr)
if err != nil {
    return nil, err
}

sa := &syscall.SockaddrInet4{
    Port: tcpAddr.Port,
    Addr: [4]byte{tcpAddr.IP[0], tcpAddr.IP[1], tcpAddr.IP[2], tcpAddr.IP[3]},
}

fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
if err != nil {
    return nil, err
}

err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_TOS, 128)
if err != nil {
    return nil, err
}

err = syscall.Connect(fd, sa)
if err != nil {
    return nil, err
}

file := os.NewFile(uintptr(fd), "")
conn, err := net.FileConn(file)
if err != nil {
    return nil, err
}

return conn, nil
Run Code Online (Sandbox Code Playgroud)

编辑 2:在 Go 1.11 中,您将能够执行以下操作:

dialer := &net.Dialer{
    Control: func(network, address string, c syscall.RawConn) error {
        return c.Control(func(fd uintptr) {
            err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, 128)
            if err != nil {
                log.Printf("control: %s", err)
                return
            }
        })
    },
}
// ...
tr := &http.Transport{
    Proxy:                 http.ProxyFromEnvironment,
    DialContext:           dialer.DialContext,
    MaxIdleConns:          100,
    IdleConnTimeout:       90 * time.Second,
    TLSHandshakeTimeout:   10 * time.Second,
    ExpectContinueTimeout: 1 * time.Second,
}
Run Code Online (Sandbox Code Playgroud)