前言
本文主要给大家介绍了关于golang 如何将多路复异步io转变成阻塞io的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍:
package main
import (
"net"
)
func handleConnection(c net.Conn) {
//读写数据
buffer := make([]byte, 1024)
c.Read(buffer)
c.Write([]byte("Hello from server"))
}
func main() {
l, err := net.Listen("tcp", "host:port")
if err != nil {
return
}
defer l.Close()
for {
c, err := l.Accept()
if err!= nil {
return
}
go handleConnection(c)
}
}
对于我们都会写上面的代码,很简单,的确golang的网络部分对于我们隐藏了太多东西,我们不用像c++一样去调用底层的socket函数,也不用去使用epoll等复杂的io多路复用相关的逻辑,但是上面的代码真的就像我们看起来的那样在调用accept和read时阻塞吗?
// Multiple goroutines may invoke methods on a Conn simultaneously.
//官方注释:多个goroutines可能同时调用方法在一个连接上,我的理解就是所谓的惊群效应吧
//换句话说就是你多个goroutines监听同一个连接同一个事件,所有的goroutines都会触发,
//这只是我的猜测,有待验证。
type Conn interface {
Read(b []byte) (n int, err error)
Write(b []byte) (n int, err error)
Close() error
LocalAddr() Addr
RemoteAddr() Addr
SetDeadline(t time.Time) error
SetReadDeadline(t time.Time) error
SetWriteDeadline(t time.Time) error
}
type conn struct {
fd *netFD
}
这里面又一个Conn接口,下面conn实现了这个接口,里面只有一个成员netFD.
// Network file descriptor.
type netFD struct {
// locking/lifetime of sysfd + serialize access to Read and Write methods
fdmu fdMutex
// immutable until Close
sysfd int
family int
sotype int
isConnected bool
net string
laddr Addr
raddr Addr
// wait server
pd pollDesc
}
func (fd *netFD) accept() (netfd *netFD, err error) {
//................
for {
s, rsa, err = accept(fd.sysfd)
if err != nil {
nerr, ok := err.(*os.SyscallError)
if !ok {
return nil, err
}
switch nerr.Err {
/* 如果错误是EAGAIN说明Socket的缓冲区为空,未读取到任何数据
则调用fd.pd.WaitRead,*/
case syscall.EAGAIN:
if err = fd.pd.waitRead(); err == nil {
continue
}
case syscall.ECONNABORTED:
continue
}
return nil, err
}
break
}
//.........
//代码过长不再列出,感兴趣看go的源码,runtime 下的fd_unix.go
return netfd, nil
}
上面代码段是accept部分,这里我们注意当accept有错误发生的时候,会检查这个错误是否是syscall.EAGAIN,如果是,则调用WaitRead将当前读这个fd的goroutine在此等待,直到这个fd上的读事件再次发生为止。当这个socket上有新数据到来的时候,WaitRead调用返回,继续for循环的执行,这样以来就让调用netFD的Read的地方变成了同步“阻塞”。有兴趣的可以看netFD的读和写方法,都有同样的实现。










