开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情
UDS是什么?
IPC(InterProcess Communication)是Unix系统进程之间相互通信的技术,有多种选择类型如下图:管道、FIFO、消息队列、信号量、共享存储、套接字等。套接字即UDS(Unix Domain Socket)是IPC的类型之一:
上图前7种IPC通常用于同一台主机的各个进程间的IPC,最后两种,即套接字和STREAMS,是仅有的两种支持不同主机上各个进程间的IPC的类型。
“无连接”:无需先调用某种形式打开函数就能发送消息的能力。“可靠”:所有这些形式的IPC都限制在单主机上,所以都是可靠的。当消息通过网络传达时,丢失消息消息的可能性就要加以考虑。“流控制“:如果系统资源(缓冲区)短缺或者如果接收进程不能再接收更多消息,则发送进程就要休眠。当流控制条件消失时,发送进程应自动地被唤醒。
套接字网络IPC接口,进程能够使用该接口和其他进程通信。通过该接口,其他进程运行位置是透明的,进程可以在同个计算机上或者不同计算机上,UDS在同一主机操作系统上执行进程之间数据交换。
创建和销毁:
套接字是通信端点的抽象,访问套接字需要用套接字描述符。套接字描述符在Unix系统是用文件描述符实现。许多处理文件描述符的函数(如read和write)都可以直接处理套接字描述符,创建套接字,需要调用socket函数:
参数domain(域)确定通信特性,取值域(以AF_开头)如下:
参数type确定套接字类型,取值如下:
AF_INET通信域中套接字类型为SOCK_STREAM的默认协议是有连接的TCP(传输控制协议),是有序、可靠双向的面向连接的字节流;在AF_INET通信域中套接字类型SOCK_DGRAM的默认协议是无连接的UDP(用户数据报协议),是长度固定、不保序、不可靠报文传递。SOCK_SEQPACKET与SOCK_STREAM类似有序、可靠、面向连接的长度固定的报文传递,流传输控制协议SCTP提供顺序数据包服务。SOCK_RAW套接字提供一个数据报接口用于直接访问下面的网络层(在因特网域中为IP),绕过传输协议(TCP和UDP等),应用程序使用此接口构造自己的协议首部。
操作套接字函数有哪些?
如何禁止套接字上的输入/输出:
shutdown允许套接字不直接关闭,以某种不活动状态存在。当参数how=SHUT_RD,无法从套接字读取数据;当how=SHUT_WR时,无法使用套接字发送数据;当how=SHUT_RDWR时,将同时无法读取和发送数据。当关闭了套接字最后一个活动引用时,才调用close函数关闭套接字,释放网络端点。
golang example
Server:
package main
import (
"fmt"
"io"
"log"
"net"
"os"
"os/signal"
"strings"
"github.com/devlights/go-unix-domain-socket-example/pkg/message"
)
const (
protocol = "unix"
sockAddr = "/tmp/echo.sock"
)
func main() {
cleanup := func() {
if _, err := os.Stat(sockAddr); err == nil {
if err := os.RemoveAll(sockAddr); err != nil {
log.Fatal(err)
}
}
}
cleanup()
listener, err := net.Listen(protocol, sockAddr)
if err != nil {
log.Fatal(err)
}
quit := make(chan os.Signal)
signal.Notify(quit, os.Interrupt)
go func() {
<-quit
fmt.Println("ctrl-c pressed!")
close(quit)
cleanup()
os.Exit(0)
}()
fmt.Println("> Server launched")
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
}
fmt.Println(">>> accepted: ", conn.RemoteAddr().Network())
go echo(conn)
}
}
func echo(conn net.Conn) {
defer conn.Close()
for {
m := &message.Echo{}
err := m.Read(conn)
if err != nil {
if err == io.EOF {
fmt.Println("=== closed by client")
break
}
log.Println(err)
break
}
fmt.Println("[READ ] ", m)
s := strings.ToUpper(string(m.Data))
m.Length = len(s)
m.Data = []byte(s)
err = m.Write(conn)
if err != nil {
log.Println(err)
break
}
fmt.Println("[WRITE] ", m)
}
}
Client:
package main
import (
"fmt"
"log"
"net"
"time"
"github.com/devlights/go-unix-domain-socket-example/pkg/message"
)
const (
protocol = "unix"
sockAddr = "/tmp/echo.sock"
)
func main() {
values := []string{
"hello world",
"golang",
"goroutine",
"this program runs on crostini",
}
conn, err := net.Dial(protocol, sockAddr)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
for _, v := range values {
time.Sleep(1 * time.Second)
m := &message.Echo{
Length: len(v),
Data: []byte(v),
}
err = m.Write(conn)
if err != nil {
log.Fatal(err)
}
fmt.Println("[WRITE] ", m)
err = m.Read(conn)
if err != nil {
log.Fatal(err)
}
fmt.Println("[READ ] ", m)
}
}