《网络编程-Go语言》-5-UNIX域套接字1

到目前为止,在这本书中,我们已经讨论了网络上节点之间的通信。但并不是所有的网络编程都只在不同的节点之间进行。应用程序有时可能需要与在同一节点上托管的数据库等服务进行通信.
除了指定IP和端口访问数据库外,还有一种方法:Unix域套接字。其使用文件系统来决定数据包目标地址,允许在同个节点的进程和另外的进程交换数据,使用IPC(inter-process communication)。
本章首先确切地定义了什么是Unix域套接字,以及如何控制对它们的读写访问。接着,您通过Go的net包探索其中三种类型,并各自编写echo服务。最后,您将编写一个使用Unix域套接字根据用户和组ID信息对客户端进行身份验证的服务。

什么是Unix域套接字

Unix域套接字将套接字寻址原则应用到文件系统中,每个Unix域套接字在文件系统上都有一个关联的文件,它对应于网络套接字的IP地址和端口号.您可以通过读写此文件来与侦听套接字的服务进行通信。同样,您也可以利用文件系统的所有权和权限来控制对套接字的读写访问。Unix域套接字通过绕过操作系统的网络堆栈来提高效率,消除了流量路由的开销。出于同样的原因,在使用Unix域套接字时不需要担心分片或数据包排序。如果您选择放弃Unix域套接字,并在与本地服务通信时专门使用网络套接字(例如,将应用程序连接到本地数据库、内存缓存等),那么您将忽略显著的安全优势和性能提升。
虽然这个系统带来了明显的优势,但它有一个注意事项:Unix域套接字是使用本地节点,因此您不能使用它们与其他远程节点通信,就像与网络套接字一样。因此,如果您希望将服务移动到另一个节点或需要应用程序的最大可移植性,则Unix域套接字可能并不太适合。要保持通信,需首先迁移到网络套接字。

绑定到Unix域套接字文件

当代码使用 net.Listen, net.ListenUnix或net.ListenPacket 函数尝试绑定到未使用的Unix域套接字地址时,将创建Unix域套接字文件。如果该地址的套接字文件已经存在,则操作系统将返回一个错误指示该地址正在使用中。在大多数情况下,只需删除现有的Unix域套接字文件可以解决这个错误。

更改套接字文件的所有权和权限

一旦服务绑定到套接字文件,您就可以使用GO的os包来修改文件的所有权和读/写权限。可以使用os.Chown函数来修改。

err := os.Chown("/path/to/socket/file", -1, 100)

其中-1是拥有者的ID,100是拥有者所在组的ID。

grp, err := user.LookupGroup("users")

上面是查看users这个组的group ID.

err := os.Chmod("/path/to/socket/file", os.ModeSocket|0660)

了解unix套接字类型

有3种:streaming sockets(类似TCP)/datagram sockets(类似UDP)/sequence packet sockets(类似TCP和UDP两者的结合),Go使用unix,unixgram和unixpacket来标明。
(1)unix streaming socket
下列代码不论Linux,macOS和Windows都支持。
先是echo.go

package echo

import (
    "context"
    "net"
)

func streamingEchoServer(ctx context.Context, network string,
    addr string) (net.Addr, error) {
    s, err := net.Listen(network, addr)
    if err != nil {
        return nil, err
    }
    go func() {
        go func() {
            <-ctx.Done()
            _ = s.Close()
        }()
        for {
            conn, err := s.Accept()
            if err != nil {
                return
            }
            go func() {
                defer func() { _ = conn.Close() }()
                for {
                    buf := make([]byte, 1024)
                    n, err := conn.Read(buf)
                    if err != nil {
                        return
                    }
                    _, err = conn.Write(buf[:n])
                    if err != nil {
                        return
                    }
                }
            }()
        }
    }()
    return s.Addr(), nil
}

下列是echo_test.go

package echo

import (
    "bytes"
    "context"
    "fmt"
    "io/ioutil"
    "net"
    "os"
    "path/filepath"
    "testing"
)

func TestEchoServerUnix(t *testing.T) {
    dir, err := ioutil.TempDir("", "echo_unix")
    if err != nil {
        t.Fatal(err)
    }
    defer func() {
        if rErr := os.RemoveAll(dir); rErr != nil {
            t.Error(rErr)
        }
    }()
    ctx, cancel := context.WithCancel(context.Background())
    socket := filepath.Join(dir, fmt.Sprintf("%d.sock", os.Getpid()))
    rAddr, err := streamingEchoServer(ctx, "unix", socket)
    if err != nil {
        t.Fatal(err)
    }
    err = os.Chmod(socket, os.ModeSocket|0666)
    if err != nil {
        t.Fatal(err)
    }
    conn, err := net.Dial("unix", rAddr.String())
    if err != nil {
        t.Fatal(err)
    }
    defer func() { _ = conn.Close() }()
    msg := []byte("ping")
    for i := 0; i < 3; i++ { // write 3 "ping" messages
        _, err = conn.Write(msg)
        if err != nil {
            t.Fatal(err)
        }
    }
    buf := make([]byte, 1024)
    n, err := conn.Read(buf) // read once from the server
    if err != nil {
        t.Fatal(err)
    }
    expected := bytes.Repeat(msg, 3)
    if !bytes.Equal(expected, buf[:n]) {
        t.Fatalf("expected reply %q; actual reply %q", expected,
            buf[:n])
    }
    _ = closer.Close()
    <-done
}

另外两个socket由于windows不支持,所以暂时偷懒跳过。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容