Skip to content

Commit 9885ed1

Browse files
committed
Merge pull request astaxie#189 from syhou/master
添加服务端读取TCP请求内容和处理的部分
2 parents 8a608dd + 17fdc43 commit 9885ed1

File tree

1 file changed

+70
-0
lines changed

1 file changed

+70
-0
lines changed

ebook/08.1.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,76 @@ Go语言中通过net包中的`DialTCP`函数来建立一个TCP连接,并返回
226226

227227
通过把业务处理分离到函数`handleClient`,我们就可以进一步地实现多并发执行了。看上去是不是很帅,增加`go`关键词就实现了服务端的多并发,从这个小例子也可以看出goroutine的强大之处。
228228

229+
有的朋友可能要问:这个服务端没有处理客户端实际请求的内容。如果我们需要通过从客户端发送不同的请求来获取不同的时间格式,而且需要一个长连接,该怎么做呢?请看:
230+
231+
package main
232+
233+
import (
234+
"fmt"
235+
"net"
236+
"os"
237+
"time"
238+
"strconv"
239+
)
240+
241+
func main() {
242+
service := ":1200"
243+
tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
244+
checkError(err)
245+
listener, err := net.ListenTCP("tcp", tcpAddr)
246+
checkError(err)
247+
for {
248+
conn, err := listener.Accept()
249+
if err != nil {
250+
continue
251+
}
252+
go handleClient(conn)
253+
}
254+
}
255+
256+
func handleClient(conn net.Conn) {
257+
conn.SetReadDeadline(time.Now().Add(2 * time.Minute)) // set 2 minutes timeout
258+
request := make([]byte, 128) // set maxium request length to 128KB to prevent flood attack
259+
260+
for {
261+
read_len, err := conn.Read(request)
262+
263+
if err != nil {
264+
if err != io.EOF { // ignore EOF since client might send nothing for the moment
265+
fmt.Println(err)
266+
break
267+
}
268+
269+
neterr, ok := err.(net.Error)
270+
if ok && neterr.Timeout() {
271+
fmt.Println(neterr)
272+
break
273+
}
274+
}
275+
276+
if read_len == 0 {
277+
continue
278+
} else if string(request) == "timestamp" {
279+
daytime := strconv.FormatInt(time.Now().Unix(), 10)
280+
conn.Write([]byte(daytime))
281+
} else {
282+
daytime := time.Now().String()
283+
conn.Write([]byte(daytime))
284+
}
285+
286+
request = make([]byte, 128) // clear last read content
287+
}
288+
}
289+
290+
func checkError(err error) {
291+
if err != nil {
292+
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
293+
os.Exit(1)
294+
}
295+
}
296+
297+
在上面这个例子中,我们使用`conn.Read()`不断读取客户端发来的请求。由于我们需要保持与客户端的长连接,所以不能在读取完一次请求后就关闭连接。由于`conn.SetReadDeadline()`设置了超时,当一定时间内客户端无请求发送,`conn`便会自动关闭,下面的for循环即会因为连接已关闭而跳出。需要注意的是,`request`在创建时需要指定一个最大长度以防止flood attack;每次读取到请求处理完毕后,需要清理request,因为`conn.Read()`会将新读取到的内容append到原内容之后。
298+
229299
### 控制TCP连接
230300
TCP有很多连接控制函数,我们平常用到比较多的有如下几个函数:
231301

0 commit comments

Comments
 (0)