通过unix socket建立http连接

除了我们熟知的tcp连接以外还可以通过本地socket建立通信,Unix Domain Socket的英文简称是UDS,就是一种常用的方式,但它只支持建立本地连接,它的好处是通过路径而非端口建立连接,好处是可以避免端口占用。

首先看服务端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main

import (
    "fmt"
    "net"
    "net/http"
    "os"
)

func main() {<!-- -->
    if len(os.Args) < 2 {<!-- -->
        fmt.Fprintln(os.Stderr, "usage:", os.Args[0], "/path.sock [wwwroot]")
        return
    }

    fmt.Println("Unix HTTP server")

    root := "."
    if len(os.Args) > 2 {<!-- -->
        root = os.Args[2]
    }

    os.Remove(os.Args[1])

    server := http.Server{<!-- -->
        Handler: http.FileServer(http.Dir(root)),
    }
    //如果是tcp连接,参数是tcp 以及端口
    //如果是uds连接,参数是unix以及路径
    unixListener, err := net.Listen("unix", os.Args[1])
    if err != nil {<!-- -->
        panic(err)
    }
    server.Serve(unixListener)
}

启动服务端

1
2
echo "kkk"> aa
go run s.go /var/abc

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package main

import (
    "context"
    "flag"
    "fmt"
    "io"
    "net"
    "net/http"
    "os"
    "strings"
)

func main() {<!-- -->
    post := flag.String("d", "", "data to POST")
    help := flag.Bool("h", false, "usage help")
    flag.Parse()

    if *help || len(flag.Args()) != 2 {<!-- -->
        fmt.Fprintln(os.Stderr, "usage:", os.Args[0], "[-d data] /path.socket /uri")
        flag.PrintDefaults()
        os.Exit(0)
    }

    fmt.Println("Unix HTTP client")

    httpc := http.Client{<!-- -->
        Transport: &http.Transport{<!-- -->
            DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {<!-- -->
                return net.Dial("unix", flag.Args()[0]) //uds 路径
            },
        },
    }

    var response *http.Response
    var err error
    if len(*post) == 0 {<!-- -->
        response, err = httpc.Get("http://unix" + flag.Args()[1])//url路径
    } else {<!-- -->
        response, err = httpc.Post("http://unix"+flag.Args()[1], "application/octet-stream", strings.NewReader(*post))
    }

    if err != nil {<!-- -->
        panic(err)
    }
    io.Copy(os.Stdout, response.Body)
}
1
2
3
4
# go run c.go -d "bb"  /var/abc /aa

Unix HTTP client
kkk

可以正确的获取server端文件内容!大功告成!