UDS:一种进程间通信的手段

原理

Unix Domain Sockets,简称UDS,又叫IPC Socket,可以使同一机器上两个或者多个进程进行数据通信,跟传统的TCP/IP socket有所不同:

维度UDSTCP/IP socket
标识一个文件名,例如:/var/lib/example/example.socketIP:Port,例如:192.168.0.2::9090
包处理过程将应用层数据从一个进程拷贝到另一个进程需要经过网络协议栈,打包拆包、计算校验和、维护序号和应答等
使用场景同一台机器上两个或多个进程间通信,速度更快跨网络通信

UDS通信可以用下面的模型描述:

UDS Model
UDS Model

表达下面几点内容:

  1. 在一台机器上的多个进程进行通信
  2. process 1,或者称为服务器进程:
    1. 监听一个socket文件
    2. 等待连接进入
    3. 处理连接:
      1. 处理消息,可以是:读写、写读、读、写、跳过本步骤,几种中的任意一种
      2. 关闭连接
  3. process 2/3,或者称为客户端进程:
    1. 连接socket文件
    2. 处理连接:
      1. 处理消息,可以是:读写、写读、读、写、跳过本步骤,几种中的任意一种
      2. 关闭连接

代码

我们接下以golang为例来看看代码,代码分服务端和客户端

服务端

设计

  1. 监听socket文件
  2. 等待下一个连接
  3. 处理连接
    1. 生成一个UUID,将该UUID回应给客户端,作为该连接的标识
    2. 接收下一条消息
    3. 将连接标识回应客户端
    4. 重复2、3
    5. 关闭连接
  4. 重复2、3

代码

package main

import (
	"context"
	"fmt"
	"net"
	"os"
	"os/signal"
	"syscall"

	"github.com/google/uuid"
)

func connHandler(ctx context.Context, conn *net.UnixConn, id string) {
	defer conn.Close()

	_, err := conn.Write([]byte(id))
	if err != nil {
		fmt.Println("send id failed: ", err)
		return
	}

	var buf [1024]byte

	for {
		select {
		case <-ctx.Done():
			return
		default:
			n, err := conn.Read(buf[:])
			if err != nil {
				fmt.Println("connection read error: ", err)
				return
			}
			fmt.Printf("%s: %s\n", id, string(buf[:n]))
			_, err = conn.Write([]byte(id))
			if err != nil {
				fmt.Println("response failed: ", err)
				return
			}
		}
	}
}

func main() {
	udsFile := "/tmp/unixdomain"
    // three types
    // unix: SOCK_STREAM
    // unixdomain: SOCK_DGRAM
    // unixpacket: SOCK_SEQPACKET
    // ListenUnix when type is unix or unixpacket
    // ListenUnixgram when type is unixgram
	l, err := net.ListenUnix("unix", &net.UnixAddr{Name: udsFile, Net: "unix"})
	if err != nil {
		panic(err)
	}
	defer os.Remove(udsFile)

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt, os.Kill, syscall.SIGTERM)

	ctx, cancle := context.WithCancel(context.Background())

	for {
		select {
		case sig := <-ch:
			fmt.Println("server stop: ", sig)
			cancle()
			return
		default:
			conn, err := l.AcceptUnix()
			if err != nil {
				panic(err)
			}
			go connHandler(ctx, conn, uuid.New().String())
		}
	}
}

客户端

设计

  1. 连接socket文件
  2. 处理连接
    1. 接收服务端分配的ID
    2. 将ID作为消息内容发送给服务端
    3. 接收服务端的回应
    4. 重复2、3
  3. 关闭连接

代码

package main

import (
	"fmt"
	"net"
	"os"
	"os/signal"
	"syscall"
	"time"
)

func main() {
	t := "unix" // or "unixgram" or "unixpacket"
	conn, err := net.DialUnix(t, nil, &net.UnixAddr{"/tmp/unixdomain", t})
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt, os.Kill, syscall.SIGTERM)

	var buf [1024]byte
	n, err := conn.Read(buf[:])
	if err != nil {
		fmt.Println("read id failed: ", err)
		return
	}
	id := string(buf[:n])
	fmt.Println("server response me with id: ", id)

	for {
		select {
		case sig := <-ch:
			fmt.Println("server stop: ", sig)
			return
		default:
			_, err = conn.Write([]byte(id))
			if err != nil {
				panic(err)
			}

			n, err = conn.Read(buf[:])
			if err != nil {
				fmt.Println("read response failed: ", err)
				return
			}
			fmt.Println("server response: ", string(buf[:n]))

			time.Sleep(time.Second)
		}
	}
}

运行

参考链接

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值