7
open_sockets = []
listening_socket = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
listening_socket.bind( ("", 1234) )
listening_socket.listen(5)
while True:
 rlist, wlist, xlist = select.select( [listening_socket] + open_sockets, [], [] )
 for i in rlist:
 if i is listening_socket:
 new_socket, addr = listening_socket.accept()
 open_sockets.append(new_socket)
 else:
 data = i.recv(1024)
 if data == "":
 i.close()
 open_sockets.remove(i)
 print "Connection closed"
 else:
 i.send(data)
 print repr(data)

Now I know this is simple server code that can handle a few clients - the only thing that I don't understand are these two lines:

 data = i.recv(1024)
 if data == "":

I understand that when the client has already accepted it will go to the other option, the option that checks if there is something in the buffer. I didn't understand why, when there is nothing in the buffer, it goes on and doesn't check the line:

if data == "":

but when the client just presses enter which is equivalent to "" it disconnects

Why when nothing is pressed it is not the same as ""?

Nathan
4,3974 gold badges22 silver badges21 bronze badges
asked Dec 7, 2012 at 22:20
0

4 Answers 4

7

It starts with the select call. This function watches sets sockets and waits for something noteworthy to happen. For sockets in the first list, "noteworthy" means that the socket has data available for reading.

rlist, wlist, xlist = select.select( [listening_socket] + open_sockets, [], [] )

The code now iterates through the list of sockets with data ready for reading, and acts based on the type of socket being handled.

 for i in rlist:
 if i is listening_socket:

Connection-oriented ("listening") sockets are used to accept new connections. Since it's in rlist, we know it has something for us to "read". In the context of a listening sockets, this means that a new connection has been received. So we accept the connection, and save the new socket in the list of open_sockets.

 new_socket, addr = listening_socket.accept()
 open_sockets.append(new_socket)

If the socket is not listening_socket, then it's a socket which is (or was) connected to a remote client. And again since it's in rlist, we know that it has something for us to "read". In the context of connected sockets, this means either that data is actually available to be read, or that the socket has been closed.

So we call recv to get any available data,

 else:
 data = i.recv(1024)

and see if we've actually read anything. If no data was available, then the connection must have been closed, so we close the socket object and remove it from open_sockets.

 if data == "":
 i.close()
 open_sockets.remove(i)
 print "Connection closed"

If we actually did receive data, we just write it back to the client and print it on the screen.

 else:
 i.send(data)
 print repr(data)

The first call to select will wait until a connection is received. You can see for yourself by updating the code as

print "About to call select"
rlist, wlist, xlist = select.select( [listening_socket] + open_sockets, [], [] )
print "Returned from select"

After the first call, rlist will include listening_socket. We know this because open_sockets is empty, and as called select will not return until something is read to 'read'. So we accept the new connection and add it to open_sockets.

When select is called again, there are three possible events. First, listening_socket might have received another connection. In this case it is handled as before: we accept the connection and add it to open_sockets.

Second, the new connection could have received data. Since select included it in rlist, we know there is data ready to be "read" from the socket (meaning that data is ready for reading, or the socket has been closed). i.recv will return the new data.

Third, the new connection could have been closed. Since select included it in rlist, we know there is data ready to be read from the socket (with the same meaning as above). But i.recv will return "" since the socket doesn't have any new data. So we know the socket has been closed, and clean up accordingly.

If no data is sent from the client (and the connection is still open), then select will not include it in rlist. So the loop will not process it, and i.recv won't be called on that particular socket.

answered Dec 7, 2012 at 22:45
Sign up to request clarification or add additional context in comments.

5 Comments

It's worth mentioning socket.setblocking() and socket.settimeout() here I think, as it's not strictly true that recv will block universally.
it won't block. i in rlist says i is "ready" to read. 1024 is bufsize, recv() can return less bytes
i didnt quit understand.what you said is if nothing is pressed it wont pass the line,so it will be stuck and wont get through to the other client or you meant that it will skip the if condition?
@J.F.Sebastian, you are absolutely correct. I've edited my answer. Thank you.
@user1779374, please let me know if the edit helps to clarify. Thanks!
3

When a socket sends "" in response, that generally means the socket is closed (or shutdown?). Someone correct me if I'm wrong here. Without that statement, it could become caught in an infinite loop if the remote server suddenly stops responding.

answered Dec 7, 2012 at 22:43

2 Comments

This is correct. This just happened to me and it was causing a huge memory overload bc I was appending data to a list looking for a terminal marker. Thanks for pointing this out.
@MichaelDavidWatson A fairly popular networking library had a similar bug, and it was causing confusing infinite-loop hangs in my code. A simple if not data: raise Exception is usually the best fix.
0

i (btw, an unfortunate name for a socket) won't be in the rlist unless there is something to read i.e., i.recv(1024) will return something or the connection is done i.e., i.recv(1024) returns b"".

Once .recv() returned b"" you won't receive anything from this socket.

The interpretation of "the client (a human) just presses enter" depends on the client (software). It has nothing to do with the server e.g., the client can buffer input strings until a newline is encountered or a timeout happened or it can send each byte as soon as it receives it from a user, etc.

answered Dec 8, 2012 at 1:06

Comments

-2
[root@pa ]# grep "banner_timeout = " /opt/panel-migrator/thirdparties/python/lib/python2.7/site-packages/paramiko/transport.py
self.banner_timeout = 60 # how long (seconds) to wait for the SSH banner
steliosbl
8,9275 gold badges33 silver badges57 bronze badges
answered Jul 28, 2017 at 9:58

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.