访问网络服务-Go对IPC的支持
文章目录
一、socket与IPC
socket(套接字)是网络编程中最核心内容之一。
1.1 IPC(进程间通信)
IPC(Inter-Process Communication):进程间通信。因此,IPC这个概念,主要定义的是多个进程之间,相互通信的方法。
主要的IPC(进程间通信)的方法有:系统信号(signal)、管道(pipe)、套接字(socket)、文件锁(file lock)、消息队列(message queue)、信号灯(semaphone,也称为信号量)等。
现存的操作系统都对IPC(进程间通信)提供了强有力的支持,尤其是socket。
1.2 socket(套接字)
socket(套接字),是一种IPC(进程间通信)方法。
在众多的IPC方法中,socket是最为通用和灵活的一种:
- 利用socket进行通信的进程,可以不局限在同一台计算机上;
- 通信的双方无论存在于世界上的哪个角落,只要能狗通过计算机的网卡端口以及网络进程互联,就可以使用socket;
支持socket的操作系统,会对外提供一套API。跑在它们之上的应用程序会利用这套API,就可以与互联网上的另一台计算机中的其它程序、甚至同一个程序中的其它线程进行通信。
二、Go语言对IPC的支持
Go语言对IPC提供了一定的支持:
- 在Go的os代码包和os/signal代表包中就有针对系统信号的API;
-
os.Pipe
函数可以创建命名管道,而
os/exec
代码包中对另一类管道(匿名管道)提供了支持; - Go语言在net代码包中有对socket支持的程序实体;
2.1 socket 的系统调用
例如,在Linux操作系统中,用于创建socket实例的API,就是由一个名为socket的系统调用代表的。这个系统调用时Linux内核的一部分。
所谓的系统调用,可以理解为特殊的C语言函数。它们是连接应用程序和操作系统内核的桥梁,也是应用程序使用操作系统功能的唯一渠道。
2.2 Go语言的标准库syscall
在Go语言标准库的syscall代码包中,有一个与这个socket系统调用相对应的函数。这两者的函数签名是基本一致的,它们都会接受三个int类型的参数,并会返回一个可以代表文件描述符的结果。
func Socket(domain, typ, proto int) (fd int, err error) {
if domain == AF_INET6 && SocketDisableIPv6 {
return -1, EAFNOSUPPORT
}
fd, err = socket(domain, typ, proto)
return
}
syscall.Socket
函数本身是平台不想管的。
在其底层,Go语言为它支持的每个操作系统都做了适配,这才是这个函数无论在哪个平台,总是有效的。
syscall.Soeckt
函数接受三个int类型的参数,代表分别要创建的socket实例的通信域、类型以及使用的协议。
(1)socket的通信域
socket的通信域主要有这样几个可选项:IPv4域、IPv6域、和Unix域。
-
IPv4域(AF_INET):对应基于IP协议第四版的网络;
IPv4是互联网协议中的一种地址格式,用于唯一标识网络中的设备。IPv4地址是32位二进制数,通常表示为点分十进制数,其中每个数值的范围为0到255。IPv4地址由网络号和主机号组成,其中网络号用于标识网络,主机号用于标识网络中的设备。IPv4地址的范围是从0.0.0.0到255.255.255.255,其中有一些保留地址用于特定目的,如私有网络和广播地址。IPv4地址空间的不足和瓶颈,使得人们逐渐转向了IPv6。
-
IPv6域(AF_INET6):对应基于IP协议第六版的网路;
IPv6域是一种用于标识IPv6地址的地址族(address family),与IPv4不同,IPv6地址使用128位二进制数表示,通常表示为8组16位的十六进制数,中间以冒号分隔,例如2001:0db8:85a3:0000:0000:8a2e:0370:7334。IPv6地址空间极为广阔,远远超过了IPv4,可以支持更多的设备连接互联网。IPv6协议的地址格式与IPv4不兼容,需要使用特定的协议栈才能实现IPv6的通信。
-
UNIX域套接字(AF_UNIX):指的是一种类Unix操作系统中特有的通信域;
在轧辊有此类操作系统的同一台计算机中,应用程序可以基于此域建立socket连接。
-
IPX/SPX(AF_IPX):用于Novell NetWare网络的通信。
-
AppleTalk(AF_APPLETALK):用于苹果机器之间的通信。
-
Netlink(AF_NETLINK):用于Linux内核和用户空间之间的通信。
-
Bluetooth(AF_BLUETOOTH):用于蓝牙设备之间的通信。
(2)socket的类型
socket的类型一共有四种,分别是:SOCK_DGRAM、SOCK_STREAM、SOCK_SEQPACKET、SOCK_RAW。
-
SOCK_DGRAM(数据报套接字):“DGRAM”代表datagram,即数据报文。它是一种有
消息边界
,但没有
逻辑连接
的非可靠socket类型。数据报套接字提供了无连接的、不可靠的数据传输,每个数据包都是独立的。这种类型的套接字常用于UDP协议的网络编程中。
-
SOCK_STREAM(流式套接字):流式套接字提供了一个面向连接的、可靠的、双向的字节流传输。这种类型的套接字常用于TCP协议的网络编程中,如HTTP、FTP等应用。
以字节流形式传输,没有消息边界,但有逻辑连接,能够保证传输等可靠性和数据的有序性,同时还可以实现数据的双向传输。
这样的网络通信传输数据的形式是字节流,而不是数据报文。字节流式以字节为单位。内核无法感知一段字节中包含了多少消息,以及消息是否完整,需要应用程序自己去把控。此类网络通信中的一端总会按照另一端发送数据是的字节排列顺序接收和缓存它们。应用程序需要根据双方的约定去数据中查找消息边界,并按照边界切割数据。
-
SOCK_SEQPACKET(序列化数据包套接字):序列化数据包套接字提供面向连接的、可靠的、双向的数据传输,每个数据包都有固定的长度。这种类型的套接字常用于按顺序传输的应用,如顺序读取数据流。
-
SOCK_RAW(原始套接字):原始套接字提供对底层协议的直接访问,可以自定义协议头和数据。这种类型的套接字常用于网络诊断和攻击等需要对网络协议进行深度操作对应用中。
消息边界:与socket相关的操作系统内核中的程序(内核程序)在发送或接受数据的时候是以消息为单位的。
可以把消息理解为带有固定边界的一段数据。内核程序可以自动识别和维护这个边界,并在必要的时候,把数据切割成一个一个的消息,或者把多个消息串成连续的数据。因此,应用程序只需要面向消息进程处理就可以了。
逻辑连接:通信双方在收发数据之前必须先建立网络连接。待连接建立好之后,双方就可以一对一地进行数据传输了。
基于UDP协议的网络通信并不需要建立逻辑连接,只要应用程序制定好对方的网络地址,内核程序就立即把数据报文发送出去。
不建立逻辑连接的好处是:发送速度快,不长期占用网络资源。
不建立逻辑连接的劣势是:发送的数据报文更长,无法保证传输的可靠性,不能实现数据的有序性,数据只能单向进行传输。
(3)socket实例所用的协议
syscall.Socket
函数第三个参数用于表示socket实例所使用的协议。
通常,只要明确了前两个参数的值,就无需在确定第三个参数值,一般吧它置为0就可以了。内核程序会自动选择最合适的协议。
例如:
-
当前两个参数分别为:
syscall.AF_INET
和
syscall.SOCK_DGRAM
时,内核程序会选择UDP作为协议; -
当前两个参数分别为:
syscall.AF_INET6
和
syscall.SOCK_STREAM
时,内核程序会选择TCP作为协议;
2.3 Go语言的net代码包
Go0语言的net代码包中的很多程序实体,都会直接或间接的使用
syscall.Sorkcet
函数。
(1)
net.Dial
函数
net.Dial
func Dial(network, address string) (Conn, error) {
var d Dialer
return d.Dial(network, address)
}
第一个参数network:(string类型)决定着Go程序在底层会创建什么样的socket实例,并使用什么样的协议与其它程序通信。
network常用的9个可选值:
-
tcp
:代表TCP协议,其基于的IP协议的版本根据参数address的值自适应; -
tcp4
:代表基于IP协议第四版的TCP协议; -
tcp6
:代表基于IP协议第六版的TCP协议; -
udp
:代表UDP协议,其基于的IP协议的版本根据参数address的值自适应; -
udp4
:代表基于IP协议第四版的UDP协议; -
udp6
:代表基于IP协议第六版的UDP协议; -
unix
:代表Unix通信域下的一种内部socket协议,以SOCK_STREAM为socket类型。 -
unixgram
:代表Unix通信域下的一种内部socket协议,以SOCK_DGRAM 为socket类型;“unixgram” 表示 Unix 域数据报套接字,用于在进程之间传输不连续的数据包(类似于 UDP 协议)。
-
unixpacket
:代表Unix通信域下的一种内部socket协议,以SOCK_SEQPACKET为socket类型;“unixpacket” 表示 Unix 域数据包套接字,与 Unix 域流套接字类似,但是提供原子性的数据包传输,即保证在发送的数据包全部到达接收端之前,接收端不会看到任何数据包。
network不常用的3个可选项:
-
ip
:IP协议,可以用于任何网络类型。 -
ip4
:仅限IPv4的IP协议,可以用于任何网络类型。 -
ip6
:仅限IPv6的IP协议,可以用于任何网络类型。
需要注意的是,““unix”、unixgram” 和 “unixpacket” 这三个可选值只能在 Unix 系统上使用。如果在非 Unix 系统上使用这两个值,Dial 函数将返回错误。
(2)
net.DialTimeout
函数
net.DialTimeout
net.DialTimeout
函数给定的超时时间,代表着函数为网络连接建立完成而等待的最长时间。
这是一个相对时间,由这个函数的参数timeout的值表示。
开始的时间几乎是我们调用
net.DialTimeout
函数的那一刻。在这之后,时间会花费在“解析参数network和address的值”,以及“创建socket实例并建立网络连接”这两件事情上。
不论执行到哪一步,只要在绝对的超时时间达到的那一刻,网络连接还没建立完成,该函数就会返回一个代表了I/O操作超时的错误值。
net.DialTimeout
是利用
net.Dialer
结构体实现超时功能的。