diff --git a/ebook/08.1.md b/ebook/08.1.md index 740ffe6d..757fb49b 100644 --- a/ebook/08.1.md +++ b/ebook/08.1.md @@ -226,6 +226,76 @@ Go语言中通过net包中的`DialTCP`函数来建立一个TCP连接,并返回 通过把业务处理分离到函数`handleClient`,我们就可以进一步地实现多并发执行了。看上去是不是很帅,增加`go`关键词就实现了服务端的多并发,从这个小例子也可以看出goroutine的强大之处。 +有的朋友可能要问:这个服务端没有处理客户端实际请求的内容。如果我们需要通过从客户端发送不同的请求来获取不同的时间格式,而且需要一个长连接,该怎么做呢?请看: + + 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 maxium request length to 128KB to prevent flood attack + + for { + read_len, err := conn.Read(request) + + if err != nil { + if err != io.EOF { // ignore EOF since client might send nothing for the moment + fmt.Println(err) + break + } + + neterr, ok := err.(net.Error) + if ok && neterr.Timeout() { + fmt.Println(neterr) + break + } + } + + if read_len == 0 { + continue + } else if string(request) == "timestamp" { + daytime := strconv.FormatInt(time.Now().Unix(), 10) + conn.Write([]byte(daytime)) + } else { + daytime := time.Now().String() + conn.Write([]byte(daytime)) + } + + request = make([]byte, 128) // clear last read content + } + } + + func checkError(err error) { + if err != nil { + fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error()) + os.Exit(1) + } + } + +在上面这个例子中,我们使用`conn.Read()`不断读取客户端发来的请求。由于我们需要保持与客户端的长连接,所以不能在读取完一次请求后就关闭连接。由于`conn.SetReadDeadline()`设置了超时,当一定时间内客户端无请求发送,`conn`便会自动关闭,下面的for循环即会因为连接已关闭而跳出。需要注意的是,`request`在创建时需要指定一个最大长度以防止flood attack;每次读取到请求处理完毕后,需要清理request,因为`conn.Read()`会将新读取到的内容append到原内容之后。 + ### 控制TCP连接 TCP有很多连接控制函数,我们平常用到比较多的有如下几个函数: