2016-11-11 124 views
2

我是一個新手去語言。我打算使用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) 

謝謝!

回答

2

您可以爲自己的自己*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。 –