一个小demo,通过goroutine简单高效地实现并发的操作。
goroutine类似Python、C#中的coroutine,都可以使函数在独立的环境中运行,与之不同的是,goroutine某种程度上来说更为强大一些,它允许并行执行,可以通过channel进行进程间的通信。
当然,其他的区别也有很多,但不在本文讨论范围之内,之后会将这方面的知识归纳总结一次。
1.程序分析
程序中需要实现一个简单的telnet服务器,对地址端口进行监听,并将传送来的信息“完璧归赵”。
主方法中主要负责开启server(address string, exitChan chan int)
线程,exitChan
通道中在没有传来server信息之前,会一直阻塞主方法。
server()
中对指定地址端口持续监听(出现问题向exitChan
中传入非正常退出的信号),当有请求传来时,Accept并开启一个处理会话的线程handleSession(conn net.Conn, exitChan)
,handlerSession()
主要对传来的信息进行解析:字节码转为字符串,并判断前缀是否有指令,根据传来的信息不同,执行不同的响应。
该程序的主要处理过程如图所示:
1.1 server.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package main
import ( "fmt" "net" )
func server(address string, exitChan chan int) { l, err := net.Listen("tcp", address) if err != nil { fmt.Println(err.Error()) exitChan <- 1 } fmt.Println("listen:" + address) defer l.Close() for { conn, err := l.Accept() if err != nil { fmt.Printf(err.Error()) continue } go handleSession(conn, exitChan) } }
|
注意使用defer
,将监听关闭,否则会报错。
1.2 session.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package main
import ( "bufio" "fmt" "net" "strings" )
func handleSession(conn net.Conn, exitChan chan int) { fmt.Println("Session started:") reader := bufio.NewReader(conn) for { str, err := reader.ReadString('\n') if err == nil { str = strings.TrimSpace(str) if !processTelnetCommand(str, exitChan) { conn.Close() break } conn.Write([]byte(str + "\r\n")) } else { fmt.Println("Session closed by mistake") fmt.Println(err.Error()) conn.Close() break } } }
|
主要注意网络请求的处理方法、步骤。
1.3 telnet.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package main
import ( "fmt" "strings" )
func processTelnetCommand(str string, exitChan chan int) bool { if strings.HasPrefix(str, "@close") { fmt.Println("Session closed") return false } else if strings.HasPrefix(str, "@shutdown") { fmt.Printf("Server shutdown") exitChan <- 0 return false } fmt.Println(str) return true }
|
1.4 main.go
1 2 3 4 5 6 7 8 9 10
| package main
import "os"
func main() { exitChan := make(chan int) go server("192.168.1.4:60001", exitChan) code := <-exitChan os.Exit(code) }
|
exitChan
的使用。