[Golang] 简单并发telnet服务器

一个小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()主要对传来的信息进行解析:字节码转为字符串,并判断前缀是否有指令,根据传来的信息不同,执行不同的响应。

该程序的主要处理过程如图所示:

channel.png

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
}
// Echo with the same string
conn.Write([]byte(str + "\r\n"))
} else {
// Error
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的使用。