http://www.cnblogs.com/leoncfor/p/5009263.html
//go-tcpsock/server.go
func handleConn(c net.Conn) {
defer c.Close()
// read from the connection
b := make([]byte, 512)
for {
length, err := con.Read(b)
if err != nil {
f.Println("1 connection closed ")
break
}
if b != nil {...}
}
// write to the connection
//... ...
}
func main() {
l, err := net.Listen("tcp", ":8888")
if err != nil {
fmt.Println("listen error:", err)
return
}
for {
c, err := l.Accept()
if err != nil {
fmt.Println("accept error:", err)
break
}
// start a new goroutine to handle
// the new connection.
go handleConn(c)
}
}
用户层眼中看到的goroutine中的“block socket”,实际上是通过Go runtime中的netpoller通过Non-block socket + I/O多路复用机制“模拟”出来的,真实的underlying socket实际上是non-block的,只是runtime拦截了底层socket系统调用的错误码,并通过netpoller和goroutine 调度让goroutine“阻塞”在用户层得到的Socket fd上。比如:当用户层针对某个socket fd发起read操作时,如果该socket fd中尚无数据,那么runtime会将该socket fd加入到netpoller中监听,同时对应的goroutine被挂起,直到runtime收到socket fd 数据ready的通知,runtime才会重新唤醒等待在该socket fd上准备read的那个Goroutine。而这个过程从Goroutine的视角来看,就像是read操作一直block在那个socket fd上似的。具体实现细节在后续场景中会有补充描述。
二、TCP连接的建立
众所周知,TCP Socket的连接的建立需要经历客户端和服务端的三次握手的过程。连接建立过程中,服务端是一个标准的Listen + Accept的结构(可参考上面的代码),而在客户端Go语言使用net.Dial或DialTimeout进行连接建立:
阻塞Dial:
conn, err := net.Dial("tcp", "google.com:80")
if err != nil {
//handle error
}
// read or write on conn
或是带上超时机制的Dial:
conn, err := net.DialTimeout("tcp", ":8080", 2 * time.Second)
if err != nil {
//handle error
}
// read or write on conn
评论
发表评论