Go基础学习记录之Sockets(三)

{app.params.name}}{app.params.name}}{app.params.name}}

TCP Server(TCP 服务端)

上篇文章中的介绍我们已经渔鸥了一个TCP客户端。
现在我们使用net包来编写TCP服务器。
在服务器端,我们需要将服务绑定到特定的非活动端口,并侦听任何传入的客户端请求。

用到的函数如下

func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err os.Error)
func (l *TCPListener) Accept() (c Conn, err os.Error)

这里要求的参数与我们之前使用的DialTCP函数所需的参数相同。
让我们使用端口7777实现时间同步服务,示例代码如下:

package main

import (
    "fmt"
    "net"
    "os"
    "time"
)

func main() {
    service := ":7777"
    tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
    checkError(err)
    listener, err := net.ListenTCP("tcp", tcpAddr)
    checkError(err)
    for {
        conn, err := listener.Accept()
        if err != nil {
            continue
        }
        daytime := time.Now().String()
        conn.Write([]byte(daytime)) // don't care about return value
        conn.Close()                // we're finished with this client
    }
}
func checkError(err error) {
    if err != nil {
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
        os.Exit(1)
    }
}

服务启动后,它会等待客户端请求。
当它收到客户端请求时,它接受它并向客户端返回包含有关当前时间信息的响应。
值得注意的是,当for循环中发生错误时,服务将继续运行而不是退出。
服务器将错误记录到服务器错误日志,而不是崩溃。
但是,上面的代码仍然不够好。
我们没有使用goroutines,这将允许我们接受同时请求。
我们现在就这样做:

package main

import (
    "fmt"
    "net"
    "os"
    "time"
)

func main() {
    service := ":1200"
    tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
    checkError(err)
    listener, err := net.ListenTCP("tcp", tcpAddr)
    checkError(err)
    for {
        conn, err := listener.Accept()
        if err != nil {
            continue
        }
        go handleClient(conn)
    }
}

func handleClient(conn net.Conn) {
    defer conn.Close()
    daytime := time.Now().String()
    conn.Write([]byte(daytime)) // don't care about return value
    // we're finished with this client
}

func checkError(err error) {
    if err != nil {
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
        os.Exit(1)
    }
}

通过将我们的业务流程与handleClient函数分离出来,并使用go关键字,我们已经在服务中实现了并发性。
这是对goroutines的强大和简单性的一个很好的证明。你们中的一些人可能会想到以下内容:这个服务器没有做任何有意义的事情。
如果我们需要通过单个连接发送不同时间格式的多个请求,该怎么办?我们怎么做?

package main

import (
    "fmt"
    "net"
    "os"
    "time"
    "strconv"
)

func main() {
    service := ":1200"
    tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
    checkError(err)
    listener, err := net.ListenTCP("tcp", tcpAddr)
    checkError(err)
    for {
        conn, err := listener.Accept()
        if err != nil {
            continue
        }
        go handleClient(conn)
    }
}

func handleClient(conn net.Conn) {
    conn.SetReadDeadline(time.Now().Add(2 * time.Minute)) // set 2 minutes timeout
    request := make([]byte, 128) // set maximum request length to 128B to prevent flood based attacks
    defer conn.Close()  // close connection before exit
    for {
        read_len, err := conn.Read(request)

        if err != nil {
            fmt.Println(err)
            break
        }

        if read_len == 0 {
            break // connection already closed by client
        } else if string(request[:read_len]) == "timestamp" {
            daytime := strconv.FormatInt(time.Now().Unix(), 10)
            conn.Write([]byte(daytime))
        } else {
            daytime := time.Now().String()
            conn.Write([]byte(daytime))
        }
    }
}

func checkError(err error) {
    if err != nil {
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
        os.Exit(1)
    }
}

在这个例子中,我们使用conn.Read()来不断读取客户端请求。
我们无法关闭连接,因为客户端可能会发出多个请求。
由于我们使用conn.SetReadDeadline()设置的超时,连接在我们分配的时间段后自动关闭。
到期时间结束后,我们的程序会从for循环中断。
请注意,需要创建具有最大大小限制的请求,以防止泛洪攻击。
 

版权声明

durban创作并维护的 小绒毛的足迹博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证。

本文首发于 博客( https://www.xiaorongmao.com ),版权所有,侵权必究。

本文永久链接: https://www.xiaorongmao.com/blog/96


版权声明

durban创作并维护的 小绒毛的足迹博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证。

本文首发于 小绒毛的足迹博客( https://www.xiaorongmao.com ),版权所有,侵权必究。

本文永久链接: https://www.xiaorongmao.com/blog/96