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

时间:2016-11-11 08:40:14

标签: http ssl 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)

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

谢谢!

1 个答案:

答案 0 :(得分:4)

您可以为自己的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)

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 中尝试此操作,但这是非常的便携式,不安全,不考虑上下文,可能迟早会破裂:

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,
}

编辑2:在Go 1.11中,您可以执行此操作:

@foreach($users as $user)
        <tr>
            <td>{{$user->id}}</td>
            <td>{{$user->username}}</td>
            <td>{{$user->email}}</td>
            <td><a href="delmember/{{$user->id}}">Supprimer le membre</a></td>
            @if($user->admin == 0)
                <td><a href="makeadmin/{{$user->id}}">Passer l'utilisateur Admin</a></td>
            @else
                <td><a href="noadmin/{{$user->id}}">Retirer l'admin à l'utilisateur</a></td>
            @endif
        </tr>
@endforeach