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

问题描述:

我是一个新手去语言。我打算使用go语言来开发一个http客户机/服务器。 在浏览http客户端软件包中支持的功能列表时,我找不到在套餐中设置套接字选项的方法 (可能我只是不知道如何使用它)。在调用http客户端连接之前,我需要在fd中设置DSCP选项(IP_TOS)。 (尽管我发现设置套接字选项的系统调用选项,但我没有找到从http包获取fd的方法)。如何使用go语言为http客户端设置套接​​字选项(IP_TOS)?

在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) 

在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) 

谢谢!

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

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.SOL_SOCKET, 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) 

编辑:如果您需要连接实际发生之前选项设置,你可以尝试在您的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.SOL_SOCKET, 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 
+0

感谢您的详细回复。我已经通过示例程序测试了上面的代码,但socket客户端IP_TOS值无法在从http客户端发起的syn分组中设置。在这里,我有一个疑问,同时将参数传递给DialContext(ctx,network,addr),如何创建上下文以ctx的形式传递。目前我使用** DialContext(context.Background()网络,地址)**? –

+0

@ManojSaha请参阅编辑并尝试。无论它是否有效,您都可能想要在Go问题跟踪器上请求一项功能,以某种方式允许FD上的设置选项。 –

+0

再次感谢您的努力!我在示例程序中进行了测试,为api设置了dscp设置是成功的,我可以通过获取dscp的api来读取dscp值,但它并不反映在syn包中的tcp连接请求中。我仍然看到dscp是0。 –