go: net: Dial is slow on localhost
See https://github.com/golang/go/issues/23366#issuecomment-374397983 for more details!
What version of Go are you using (go version)?
go version go1.9.2 windows/amd64
Does this issue reproduce with the latest release?
yes
What operating system and processor architecture are you using (go env)?
set GOARCH=amd64 set GOBIN= set GOEXE=.exe set GOHOSTARCH=amd64 set GOHOSTOS=windows set GOOS=windows set GORACE= set GOROOT=C:\Go set GOTOOLDIR=C:\Go\pkg\tool\windows_amd64 set GCCGO=gccgo set CC=gcc set CXX=g++ set CGO_ENABLED=1 set CGO_CFLAGS=-g -O2 set CGO_CPPFLAGS= set CGO_CXXFLAGS=-g -O2 set CGO_FFLAGS=-g -O2 set CGO_LDFLAGS=-g -O2 set PKG_CONFIG=pkg-config
What did you do?
now := time.Now()
net.Dial("tcp", "localhost:3000")
fmt.Println(time.Since(now))
This code outputs around 1s.
When replacing localhost with 127.0.0.1 it takes around 2ms, sometimes also <1ms.
google.de:80 is around 60ms.
Is this expected to take so much longer with localhost? Took me several hours to find out that this was the root cause of multiple issues in my application.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Comments: 29 (11 by maintainers)
I encountered the same issue, and I’ve done some digging. The first clue was that if I tried to connect to a port that has no listener at all the delay was 2 seconds, not 1 second.
The delay isn’t in the name lookup. But the results of the name lookup do matter: here,
localhostresults in two addresses,::1and127.0.0.1. In this casenet.Dialattempts to connect to::1first and127.0.0.1second.The delay arises from failed connection attempts. Specifically, the call to fd.pfd.ConnectEx takes almost exactly a second when it fails (for any address, not just local ones). You can see this by modifying the implementation to print before/after timestamps.
In the
localhostcase, if you’re listening on both IPv4 and IPv6, then it succeeds immediately. If you’re listening on just IPv4 then you get failed connection attempted and a 1s delay followed by success. If you’re listening on neither then you get two failed connection attempts and thus a 2s delay.The same issue affects PuTTY.
tcpdumpreveals that what’s going on is that Windows apparently does not believe the RST bits it gets back from the target of the connection (even if, apparently, it is itself the target) and keeps trying for up to a second.I doubt that Go can do anything about this. For application developers the answer is to bind to both IPv4 and IPv6 addresses, so that it doesn’t matter what connection order clients use.
A couple of references:
Change https://go.dev/cl/495875 mentions this issue:
net: make Dial fail faster on Windows closed loopback devices@EllieLockhart the root cause is in Microsoft’s TCP stack and only they can fix it.
There are some possible application/operator responses and they could be documented in https://golang.org/pkg/net/ (or elsewhere).
127.0.0.1instead oflocalhost. This isn’t a general fix but as a tactical response to the problem I suspect it would be widely acceptable.::1and127.0.0.1, if the local platform supports both.