并发 TCP 服务器

    这个 TCP 并发服务器的工作是接收一个正整数并从斐波纳切序列返回一个自然数。如果输入发送错误,则返回值为-1。由于斐波纳切序列数的计算慢,我们将使用曾首次在第11章出现的一个算法,代码测试,优化和分析都包含在 文件中。另外,这次用到的算法将会详细的讲解。

    我们把程序命名为 fiboTCP.go,并把代码分成五部分。由于把 web 服务的端口号定义为命令行参数被认为是良好的实现方式,这次 fiboTCP.go 将完全按此来做。

    fiboTCP.go 的第一部分如下:

    fiboTCP.go 的第二部分如下:

    1. func f(n int) int {
    2. fn := make(map[int]int)
    3. for i := 0; i <= n; i++ {
    4. var f int
    5. if i <= 2 {
    6. f = 1
    7. } else {
    8. f = fn[i-1] + fn[i-2]
    9. }
    10. fn[i] = f
    11. }
    12. return fn[n]
    13. }

    上面这段代码,您能看到 f() 函数实现了斐波纳切序列自然数的生成。一开始看这个算法很难理解,但它非常有效且运行速度也很快。首先,f() 函数使用了一个被命名为 fn 的字典,这在计算斐波纳切序列数时很不寻常。第二,f() 函数使用了一个 for 循环,这也相当不寻常。最后,f() 函数没有使用递归,这也是它执行快的主要原因。

    fiboTCP.go 的第三部分如下:

    1. func handleConnection(c net.Conn) {
    2. for {
    3. netData, err := bufio.NewReader(c).ReadString('\n')
    4. if err != nil {
    5. fmt.Println(err)
    6. os.Exit(100)
    7. }
    8. if temp == "STOP" {
    9. break
    10. fibo := "-1\n"
    11. n, err := strconv.Atoi(temp)
    12. if err == nil {
    13. fibo = strconv.Itoa(f(n)) + "\n"
    14. }
    15. c.Write([]byte(string(fibo)))
    16. }
    17. time.Sleep(5 * time.Second)
    18. c.Close()
    19. }

    handleConnection() 函数处理并发 TCP 服务器的每个客户端。

    fiboTCP.go的第四部分如下:

    fiboTCP.go的剩余代码如下:

    1. for {
    2. c, err := l.Accept()
    3. if err != nil {
    4. fmt.Println(err)
    5. return
    6. }
    7. go handleConnection(c)
    8. }
    9. }

    go handleConnection(c) 声明实现了程序的并发性,每次连接一个新 TCP 客户端时它就开启一个新的 goroutine。goroutine 被并发执行,使服务器有机会服务更多的客户端。

    1. $ go run fiboTCP.go 9000
    2. n: 10
    3. fibo: 55
    4. n: 0
    5. n: -1
    6. fibo: 0
    7. n: 100
    8. fibo: 3736710778780434371
    9. n: 12
    10. fibo: 144
    11. n: 12
    12. fibo: 144

    TCPclient.go 这边的输出如下:

    netcat(l) 这边的输出如下:

    1. $ nc localhost 9000
    2. 10
    3. 55
    4. 0
    5. 1
    6. -1
    7. 0
    8. 100
    9. 3736710778780434371
    10. ads
    11. -1
    12. STOP

    当您发送 STOP 字符串给服务进程时。为指定 TCP 客户端服务的 goroutine 将终止,这将引起连接关闭。

    最后,值得关注的是俩个客户端被同时提供服务,这可以通过下面的命令输出来验证:

    1. $ netstat -anp TCP | grep 9000
    2. tcp4 0 0 127.0.0.1 9000 127.0.0.1.57309 ESTABLISHED
    3. tcp4 0 0 127.0.0.1.57309 127.0.0.1.9000 ESTABLISHED
    4. tcp4 0 0 127.0.0.1 9000 127.0.0.1.57305 ESTABLISHED
    5. tcp4 0 0 *.9000 *.* LISTEN

    上面命令的最后一行输出告诉我们有一个进程在监听 9000 端口,这意味着您仍能连接 9000 端口。输出的头俩行显示有个客户端使用 57309 端口与服务进程通信。第三和第四行证明有另一个客户端连接到监听 9000 端口的服务上。这个客户端使用的 TCP 端口是 57035