2.4 实现 Lib v0.1
package lib
import (
"bufio"
"log"
"net"
"os"
"os/signal"
"syscall"
)
// 如果 err != nil,输出错误日志并退出进程
func FatalNotNil(err error) {
if err != nil {
log.Fatal(err)
}
}
// 如果 err != nil,输出错误日志
func LogNotNil(err error) {
if err != nil {
log.Println(err)
}
}
// 输出消息到日志
func LogMessage(msg ...any) {
log.Println(msg...)
}
// 输出连接建立与关闭消息到日志,包内私有方法,外部不能调用
func logConn() func() {
log.Println("已连接")
return func() {
log.Println("已断开连接")
}
}
// 接收连接另一侧发送的消息,输出消息到日志
func HandleConnection(conn net.Conn) {
// 连接建立和断开时,分别输出日志
defer logConn()()
// 从当前方法返回时,关闭连接
defer conn.Close()
// 设置如何处理接收到的字节流,bufio.ScanLines 为逐行扫描的方式把字节流分割为消息流
scanner := bufio.NewScanner(conn)
scanner.Split(bufio.ScanLines)
// 循环解析消息,每当解析出一条消息后,scan() 返回 true
for scanner.Scan() {
// 返回解析出的消息字节 slice
bytes := scanner.Bytes()
// 消息内容不为空,则输出到日志
if len(bytes) > 0 {
LogMessage(string(bytes))
}
}
// 如果解析消息遇到错误,则输出错误到日志
LogNotNil(scanner.Err())
}
// 信号监听处理器
func SignalHandler() {
// 创建信号 channel
sigchan := make(chan os.Signal, 1)
// 注册要监听哪些信号
signal.Notify(sigchan, os.Interrupt) // ctrl+c
signal.Notify(sigchan, syscall.SIGTERM) // kill
// 一直阻塞,直到收到信号,恢复执行并退出进程
<-sigchan
// 退出进程
defer os.Exit(0)
}
关键字 defer 可以修饰函数调用,用来推迟该函数的执行,直到执行 defer 语句的上层函数返回。上方的程序有三处使用了 defer,如:
func HandleConnection(conn net.Conn) {
// 连接建立和断开时,分别输出日志
defer logConn()()
// 从当前方法返回时,关闭连接
defer conn.Close()
// 省略...
}
首先执行 logConn(),立刻输出“已连接”并返回一个匿名函数,该匿名函数被 defer 修饰,推迟到 HandleConnection 函数返回时调用。同样地,conn.Close 被 defer 修饰,也推迟到 HandleConnection 函数返回时调用。被 defer 修饰的函数,按照 defer 语句出现顺序逆序执行。也就是说,在 HandleConnection 函数返回时,先执行 conn.Close(),再执行 logConn() 返回的匿名函数。
一般情况,不管使用资源的代码如何执行,资源在使用完成后必须释放,defer 是一种不常见但是非常有效的释放资源的方法。
关于 defer 可以看 Effective Go: Defer。
defer 示例可以看 Tour: Defer 和 Go by Example: Defer。
SignalHandler 函数中使用到了信道 (channel),信道是连接并发协程的管道,你可以在一个协程中向信道写入值,然后从另一个协程中读取值。通过 signal.Notify 向系统注册关心的信号,并提供 chan os.Signal 类型参数 sigchan,当信号出现时,会向 sigchan channel 写入信号值 (sigchan <- signal),此时 <-sigchan 可以读取到该值,并从阻塞状态中恢复。
关于 channel 可以看 Effective Go: Channels。
channel 示例可以看 Tour: Channels 和 Go by Example: Channels