Non-blocking connect

M

mp

Code is at bottom. Basically, if I turn off socket blocking prior to
connecting, I get a "Socket is not connected" error when I try to send
data. However, if I do not turn off blocking, OR if I place a print
statement anywhere before the send call, it works! WTF?

I'd like to understand what's going on in the background here, if you
know don't skimp on the details.

Thanks

---------------------------------------------------------------
import
socket
sock = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
sock.setblocking(0)
sock.connect_ex(('localhost',
9000))
sock.setblocking(1)
sock.send('foo')
sock.close()
 
R

Roy Smith

mp said:
Code is at bottom. Basically, if I turn off socket blocking prior to
connecting, I get a "Socket is not connected" error when I try to send
data. However, if I do not turn off blocking, OR if I place a print
statement anywhere before the send call, it works! WTF?

I'd like to understand what's going on in the background here, if you
know don't skimp on the details.

Thanks

---------------------------------------------------------------
import
socket
sock = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
sock.setblocking(0)
sock.connect_ex(('localhost',
9000))
sock.setblocking(1)
sock.send('foo')
sock.close()

I can't be 100% sure about this because I don't know what's running on your
port 9000 that you're trying to connect to, but I think I know what's going
on.

I just tried your code, but changed 9000 to 22 (ssh). What I found is that
if you eliminate the setblocking(0) call, the connect_ex() call returns 0,
indicating it succeeded. If you leave the setblocking(0) call in,
connect_ex() returns EINPROGRESS (Operation now in progress). This makes
sense. Here's the code:

import socket
import os
import errno

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(0)
err = sock.connect_ex(('localhost', 22))
print errno.errorcode[err], os.strerror(err)

When you do a TCP connect, there is what's called a three-way handshake
that happens between the TCP stacks at the two ends of the connection. I
send a packet with the SYN bit set, you respond with a packet with both SYN
and ACK set, and I respond with an ACK. Even when both ends are on the
same box (i.e. localhost), this has to happen.

Quoting from http://docs.python.org/lib/socket-objects.html:
Some notes on socket blocking and timeouts: [...] In non-blocking mode,
operations fail (with an error that is unfortunately system-dependent) if
they cannot be completed immediately.

Since the three-way handshake can't be completed immediately, the
connect_ex call fails when you try it in non-blocking mode.

So, that's the explanation. Now the question: What are you trying to do
that you need non-blocking mode? Or are you just experimenting to see what
happens?

BTW, this was done on an OSX-10.5.2 box, but I think the result would be
essentially the same on any platform.
 
M

mp

Thanks Roy. I was just trying to understand someone else's code, but
in the end it turns out that this was just a bug.

What weirded me out was how injecting a print statement preventing the
error from occurring, but now I get it. Without blocking, the
connection handshake occurs in parallel after the connect_exc method
is called. In my example, my processor reaches the send call before
the connection manages to complete in the background. However, if you
stick in a print statement after the connect_exc call and before the
send call, it delays processing just long enough for the connection to
complete, thus no exception is thrown by the send call.
 
R

Roy Smith

mp said:
Thanks Roy. I was just trying to understand someone else's code, but
in the end it turns out that this was just a bug.

What weirded me out was how injecting a print statement preventing the
error from occurring, but now I get it. Without blocking, the
connection handshake occurs in parallel after the connect_exc method
is called. In my example, my processor reaches the send call before
the connection manages to complete in the background. However, if you
stick in a print statement after the connect_exc call and before the
send call, it delays processing just long enough for the connection to
complete, thus no exception is thrown by the send call.

I don't really understand that last part. There shouldn't be any
background processing going on, unless there's a lot more code than you
showed.

The three-way handshake *can't* happen in the background, because
connect_ex() has no way to know what value to return until the handshake is
completed. Let's say I send a SYN, and 15 seconds later, you send a RST
(indicating that the connection has been refused). The connect_ex() call
has to have waited the 15 seconds for this to happen so it knows to return
an appropriate error code.

I do not understand why sticking a print statement in there should make any
difference.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,190
Members
46,736
Latest member
zacharyharris

Latest Threads

Top