Where are my TIME_WAITs on Mac OS X?
No TIME_WAIT
s on Mac OS X
Normally, when a TCP connection is closed, the socket on the side where close()
is called first is left in the TIME_WAIT
state.
When one of the peers is a Mac OS X (Lion) machine, no TIME_WAIT
is listed by netstat -an
on the Mac if close()
is called first on the Mac side. However, it seems that the socket is actually in TIME_WAIT
state, because trying to call listen()
again (without using the socket option SO_REUSEADDR
) causes listen()
to fail.
Waiting for 2*MSL (Maximum Segment Lifetime which is 15 seconds on Mac OS X Lion as reported by sysctl net.inet.tcp.msl
) clears the TIME_WAIT
state, and listen()
can be called again without error.
Why can't I see the socket in TIME_WAIT
?
Testing
Here are two simple test programs in Python.
Server
#!/usr/bin/env python
import socket
HOST = ''
PORT = 50007
l = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
l.bind((HOST, PORT))
l.listen(1)
print("Listening on %d" % PORT)
(s, _) = l.accept()
print("Connected")
raw_input("Press <enter> to close...")
l.close()
s.close()
print("Closed")
Client
#!/usr/bin/env python
import socket
import sys
HOST = sys.argv[1]
PORT = 50007
print("Opening connection to server")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
raw_input("Press <enter> to close...")
s.close()
print("Closed")
When running both the server and the client on two different Linux machines, the peer that press <enter>
to call close()
first gets a TIME_WAIT
as expected:
$ ./server-timewait.py
Listening on 50007
Connected
Press <enter> to close...
Closed
$ netstat -an | grep 50007
tcp 0 0 172.16.185.219:50007 172.16.185.42:49818 TIME_WAIT
$
When one of the peers is a Mac (running OS X Lion) I never see a TIME_WAIT
when running netstat -an | grep 50007
after closing first on the Mac.
This bug report claims that the problem is in the netstat implementation. The code attached to the bug report shows sockets in TIME_WAIT state correctly. You need to remove the following lines
if (lip == INADDR_LOCALHOST ||
lip == INADDR_ANY
) { continue; }
to make it show sockets bound to localhost.