Set deadline/timeout for TCP listener's Accept()

1,405 views
Skip to first unread message

rhomo...@gmail.com

unread,
May 23, 2016, 2:13:04 PM5/23/16
to golang-nuts
Hi all!

I'm trying to implement non-blocking accept call in Golang and the best I've come so far is the following code snippet (this is a working Go v1.7 program):

package main

import (
"net"
"log"
"time"
)

func createClient() {
tcpConn, err := net.DialTCP("tcp4", nil, &net.TCPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 12819,
})
if err != nil {
log.Fatalln("Error connecting to the server!")
}
log.Println("Managed to dial!")
tcpConn.Close()
}

func main() {
go createClient()
l, err := net.ListenTCP("tcp4", &net.TCPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 12819,
})
if err != nil {
log.Fatalln("Can't listen on provided IP/port!")
}
if err = l.SetDeadline(time.Now().Add(time.Nanosecond)); err != nil {
log.Fatalln("Can't set appropriate deadline!")
}
tcpConn, err := l.AcceptTCP()
if err != nil {
if opError, ok := err.(*net.OpError); ok {
if opError.Timeout() {
log.Fatalln("Timeout error!")
}
}
log.Fatalln("Error while accepting connection!")
}
log.Println("Accepted new connection!")
tcpConn.Close()
}

The problem here is that I always get "Timeout error!". As far as I understand that's because by the time listener's `AcceptTCP()` gets called the timeout previously set will have already expired. Try changing it to `time.Microsecond` and you will probably get the same result (unless you have processor slower than mine). I start having positive results altering timeout to at least `time.Second`. That's where I start having "Accepted new connection!"/"Managed to dial!".

So the basic idea is to get the same functionality one gets using POSIX socket interface with `select` system call to achieve non-blocking 'accept()'. Any opinion would be highly appreciated.

Any thoughts?

Ian Lance Taylor

unread,
May 23, 2016, 3:53:44 PM5/23/16
to rhomo...@gmail.com, golang-nuts
On Sun, May 22, 2016 at 3:17 PM, <rhomo...@gmail.com> wrote:
>
> So the basic idea is to get the same functionality one gets using POSIX
> socket interface with `select` system call to achieve non-blocking
> 'accept()'. Any opinion would be highly appreciated.

I just want make sure you are aware that the Go runtime does the
equivalent of a `select` system call internally. In Go it's rarely
useful to implement non-blocking semantics yourself. Just let the
goroutine block, and handle any required nonblocking at a higher
level. In Go that is simple and efficient.

Ian

rhomo...@gmail.com

unread,
May 23, 2016, 6:02:13 PM5/23/16
to golang-nuts, rhomo...@gmail.com
The main point is to know that my `Accept()` call wouldn't block. From the raw system call perspective I guess the best way to overcome this challenge is to use epoll_create1/epoll_ctl/epoll_wait system calls. To watch for read events (and if there's a queued connection already it would) and make non-blocking `epoll_wait`. Actually, I've already used that (so one may say that I've found the answer for my initial question) using this package which provides API for these system calls but I'd like to know if there's a better way, Go's way, achieving this.

rhomo...@gmail.com

unread,
May 23, 2016, 6:06:19 PM5/23/16
to golang-nuts
Also I must apologize for mistakenly stating that the code snippet is for Go v1.7 when in fact it's for v1.6.2. 

Jesse McNelis

unread,
May 23, 2016, 6:32:27 PM5/23/16
to rhomo...@gmail.com, golang-nuts


On 24 May 2016 8:02 a.m., <rhomo...@gmail.com> wrote:
>
> The main point is to know that my `Accept()` call wouldn't block.

That's what the normal Accept() function in the net package does.

It blocks the goroutine but not a thread by using epoll/select.

All functions in the net package are async from the perspective of a system thread.

Ian Lance Taylor

unread,
May 23, 2016, 7:36:48 PM5/23/16
to rhomo...@gmail.com, golang-nuts
On Mon, May 23, 2016 at 3:02 PM, <rhomo...@gmail.com> wrote:
>
> The main point is to know that my `Accept()` call wouldn't block.

But why do you care? Just start a goroutine that calls Accept, and
let that goroutine block.

Ian

Bakul Shah

unread,
May 23, 2016, 8:48:38 PM5/23/16
to rhomo...@gmail.com, golang-nuts
On Mon, 23 May 2016 15:02:12 PDT rhomo...@gmail.com wrote:
>
> The main point is to know that my `Accept()` call wouldn't block. From the
> raw system call perspective I guess the best way to overcome this challenge
> is to use epoll_create1/epoll_ctl/epoll_wait system calls. To watch for
> read events (and if there's a queued connection already it would) and make
> non-blocking `epoll_wait`. Actually, I've already used that (so one may say
> that I've found the answer for my initial question) using this
> <http://golang.org/x/sys/unix> package which provides API for these system
> calls but I'd like to know if there's a better way, Go's way, achieving
> this.

Why do you want to do this?

Having done this (bypass Go's network layer) for a specific
application in the past, I can tell you this will be
nontrivial. You'll spend a bunch of time getting the syscall
to your Go code interface right and deal with possible race
conditions etc., so make sure it is truly worth it. You
should *first* try the recommended Go way. *Always*. If you
are set on doing it your way, at the very least implement one
version using the recommended Go way even as you implement
your syscall based version.

> On Monday, May 23, 2016 at 10:53:44 PM UTC+3, Ian Lance Taylor wrote:
> >
> > On Sun, May 22, 2016 at 3:17 PM, <rhomo...@gmail.com <javascript:>>
Reply all
Reply to author
Forward
0 new messages