十二、信号
-
操作系统的信号(signal)是ipc进程通信唯一一种异步的通信方式,本质 是用软件来模拟硬件的中断机制。
-
unix 系统 使用 kill -l 查看支持的信号量。有62种,其中 32,33没有编号。1-31属于标准信号。34-62属于实时信号。
-
信号来源有键盘(ctrl +c),硬件故障,系统函数调用,软件非法运算。
-
进程响应信号方式,忽略,捕捉,执行默认操作(终止进程,忽略该信号量,终止进程并保存内存,停止进程,)
-
os/signal中的Notify方法 ,把操作系统发给当前进程的指定信号 通知给该函数的调用方法。
//1.注册自定义处理的信号量 signs := make(chan os.Signal) signal.Notify(signs, syscall.SIGINT, syscall.SIGQUIT) //2.处理定义的信号事件 for sig := range signs { log.Printf("signal:%s",sig.) } //3.如果收到了没有自定义的信号,会执行操作系统默认操作
-
unix 系统中 sigkill ,sigstop不能被自行处理也不会被忽略,只能是执行系统默认操作。
-
通用信号,处理可以自定义处理,还可以在之后恢复 系统默认操作。
sigRecv1 := make(chan os.Signal, 1) signal.Notify(sigRecv1, syscall.SIGINT, syscall.SIGQUIT) sigRecv2 := make(chan os.Signal, 1) signal.Notify(sigRecv2, syscall.SIGQUIT) var wg sync.WaitGroup wg.Add(2) go func() { for sig := range sigRecv2 { log.Printf("received a signal sigRecv2:%s\n", sig) } log.Printf("End.[sigRecv2]\n") wg.Done() }() go func() { for sig := range sigRecv1 { log.Printf("received a signal sigRecv1:%s\n", sig) } log.Printf("End.[sigRecv1]\n") wg.Done() }() wg.Wait() /** ctrl + \ 2022/09/17 22:42:04 received a signal sigRecv2:quit 2022/09/17 22:42:04 received a signal sigRecv1:quit */
十三、socket
-
socket 是ipc进程通信的方式之一,它通过网络连接来使俩个以上的进程建立通信并传递数据。建立了跨主机的进程间通信基础。
-
linux 操作系统中,存在名为socket的系统调用:int socket(int domain,int type,int protocol) .三个参数分别是通讯域,类型,协议。AF = address family
-
通讯域 含义 地址形式 通讯范围 AF_INET ipv4域 ipv4地址(4个字节),端口号(2个字节 跨主机的应用程序通信 AF_INET6 ipv6域 ipv6地址(16个字节),端口号(2个字节) 跨主机的应用程序通信 AF_UNIX Unix域 路径名称 同台主机的应用程序通信 特性 socket类型 SOCK_DGRAM SOCK_RAW SOCK_SEQPACKET SOCK_STREAM 数据形式 数据报 数据报 字节流 字节流 数据边界 有 有 有 没有 逻辑连接 没有 没有 有 有 数据有序性 不保证 不保证 保证 保证 传输可靠性 不具备 不具备 具备 具备 -
数据形式,数据报和字节流。数据报有明确的边界。字节流是一个字节接着一个字节的串,没有明确的边界,需要应用程序来处理数据包的边界。
-
面向连接的socket 进行数据传输之前必须先建立逻辑连接,在连接被建立后,连接已经暗含了双方的地址,所以传输数据的时候不必再指定目标地址。
-
面向无连接的socket通信无语建立连接,传输的每个数据报都是独立的,数据包都含有目标地址,每个数据包可能被传输到不同的目的地址。
-
传输可靠性和有序性 与socket 是否面向连接有很大关系,SOCK_RAW 提供网络层 的传输方式。
-
通常第三个参数为0,让操作系统根据第一个,第二个参数的值自行决定使用的协议。
决定因素 SOCK_DGRAM SOCK_RAW SOCK_SEQPACKET SOCK_STREAM AF_INET UDP IPV4 SCTP TCP或SCTP AF_INET6 UDPIPV IPV6 SCTP TCP或SCTP AF_UNIX 有效 无效 有效 有效 -
TCP(transmission control protocol),传输控制协议,UDP(user datagram protocol)数据报协议,SCTP(stream control transmission protocol)流传输控制协议。都是tcp/IP协议栈中的传输控制协议。IPV4和IPV6 是网络层协议。
-
基于TCP/IP 协议栈的socket通讯
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1uRH7CeG-1666106762425)(C:\Users\xpc\AppData\Roaming\Typora\typora-user-images\image-20220918002351265.png)]
-
通过基于tcp/ip 协议栈socket 接口,可以建立和监听tcp连接和udp连接,以及网路层IP协议通信
-
golang中标准库中提供 listener,err := net.listen(net,laddr string) (Listener,err)
-
golang, conn,err := listerner.Accept() 阻塞中直到 客户端 与当前程序建立TCP连接。
-
通过案例说明 conn客户都与服务端双方的读写操作
-
客户端 发送给服务端的每块请求数据都带上数据边界。并切割成数据块。
-
客户端获得期望的响应数据后,应该及时关闭连接。
-
严格限制耗时,超过5秒超时后 应该报告超时错误并关闭连接。
-
服务端 根据事先约定好的数据边界把收到的请求数据切割成数据块
-
仅接收可以由int32类型标识的请求数据块,对于不符合要求的数据块,生成错误信息并返回给客户端。处理业务逻辑,发送给客户端的每块响应数据都应该带有约定好的数据边界。
-
需要鉴别闲置连接,在过去10秒内,没有任何数据请求的客户端连接。
const ( serverNetwork = "tcp" serverAddress = "127.0.0.1:8085" DELIMITER = '\t' ) func main() { //1.网络监听 listener, err := net.Listen(serverNetwork, serverAddress) if err != nil { log.Fatalf("listener err:%s", err) } defer listener.Close() log.Printf("get listener for the server [address:%s]", listener.Addr()) for true { //2.等待客户端三次握手,建立连接 conn, err := listener.Accept() if err != nil { log.Fatalf("Accept err:%s", err) } log.Printf("established a connection with a client application [remote address:%s]", conn.RemoteAddr()) //3.接收客户端一个连接请求 go handleConn(conn) } } /** 处理每次请求 */ func handleConn(conn net.Conn) { //1.退出时释放连接 defer conn.Close() for true { //2.设置读超时 conn.SetReadDeadline(time.Now().Add(time.Second * 10)) req, err := read(conn) if err != nil { if err == io.EOF { log.Printf("conn is closed by another side:%s", err) return } else { log.Printf("conn read err:%s", err) } break } log.Printf("received req :%s\n", req) } } /** 一次有边界的消息读取 + 字节缓冲区方式 */ func read(conn net.Conn) (string, error) { //1.按字节读取 readBytes := make([]byte, 1) //2. 设置字节缓冲区 var buffer bytes.Buffer for true { _, err := conn.Read(readBytes) if err != nil { return "", err } //3. 判断消息边界 if readBytes[0] == DELIMITER { break } buffer.WriteByte(readBytes[0]) } return buffer.String(), nil } /** 千万不可用这个版本的read函数 带缓冲器的读取器,使得存在 读取的数据 比你想要数据多一点 每次的客户端请求进来都会新创建 读取器,意味着 上一次多读出来的数据会丢弃 造成数据不完整,本应该留给后面一个读取器的数据,却被提前读到了前面一个读取器的缓冲区。 */ //func read(conn net.Conn) (string, error) { // reader := bufio.NewReader(conn) // reader.re // readBytes, err := reader.ReadBytes(DELIMITER) // if err != nil { // return "", err // } // // return string(readBytes[:len(readBytes)-1]), nil //}
-
-
-