Accept
- Accept
// tcpsock_posix.go
// TCPListener is a TCP network listener. Clients should typically
// use variables of type Listener instead of assuming TCP.
type TCPListener struct {
fd *netFD
}
// AcceptTCP accepts the next incoming call and returns the new
// connection.
func (l *TCPListener) AcceptTCP() (*TCPConn, error) {
if l == nil || l.fd == nil {
return nil, syscall.EINVAL
}
fd, err := l.fd.accept(sockaddrToTCP)
if err != nil {
return nil, err
}
return newTCPConn(fd), nil
}
// Accept implements the Accept method in the Listener interface; it
// waits for the next call and returns a generic Conn.
func (l *TCPListener) Accept() (Conn, error) {
c, err := l.AcceptTCP()
if err != nil {
return nil, err
}
return c, nil
}
- netFD->accept
func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err error) {
if err := fd.readLock(); err != nil {
return nil, err
}
defer fd.readUnlock()
var s int
var rsa syscall.Sockaddr
if err = fd.pd.PrepareRead(); err != nil {
return nil, &OpError{"accept", fd.net, fd.laddr, err}
}
for {
s, rsa, err = accept(fd.sysfd) ///accept
if err != nil {
if err == syscall.EAGAIN {
if err = fd.pd.WaitRead(); err == nil {
continue
}
} else if err == syscall.ECONNABORTED {
// This means that a socket on the listen queue was closed
// before we Accept()ed it; it's a silly error, so try again.
continue
}
return nil, &OpError{"accept", fd.net, fd.laddr, err}
}
break
}
if netfd, err = newFD(s, fd.family, fd.sotype, fd.net); err != nil {
closesocket(s)
return nil, err
}
if err = netfd.init(); err != nil {
fd.Close()
return nil, err
}
lsa, _ := syscall.Getsockname(netfd.sysfd)
netfd.setAddr(toAddr(lsa), toAddr(rsa))
return netfd, nil
}
Linux的accept是在net/sock_cloexec.go中实现的。
/// net/sock_cloexec.go
// Wrapper around the accept system call that marks the returned file
// descriptor as nonblocking and close-on-exec.
func accept(s int) (int, syscall.Sockaddr, error) {
ns, sa, err := syscall.Accept4(s, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC)
// On Linux the accept4 system call was introduced in 2.6.28
// kernel and on FreeBSD it was introduced in 10 kernel. If we
// get an ENOSYS error on both Linux and FreeBSD, or EINVAL
// error on Linux, fall back to using accept.
switch err {
default: // nil and errors other than the ones listed
return ns, sa, err
case syscall.ENOSYS: // syscall missing
case syscall.EINVAL: // some Linux use this instead of ENOSYS
case syscall.EACCES: // some Linux use this instead of ENOSYS
case syscall.EFAULT: // some Linux use this instead of ENOSYS
}
// See ../syscall/exec_unix.go for description of ForkLock.
// It is probably okay to hold the lock across syscall.Accept
// because we have put fd.sysfd into non-blocking mode.
// However, a call to the File method will put it back into
// blocking mode. We can't take that risk, so no use of ForkLock here.
ns, sa, err = syscall.Accept(s)
if err == nil {
syscall.CloseOnExec(ns)
}
if err != nil {
return -1, nil, err
}
if err = syscall.SetNonblock(ns, true); err != nil {
syscall.Close(ns)
return -1, nil, err
}
return ns, sa, nil
}