I'm currently experimenting with sockets in python. I tried the following 3 variants (skip over the code to read the question first):
class Server(threading.Thread):
def __init__(self):
super(Server, self).__init__()
self.ip = "localhost"
self.port = 23071
self.connectionNumber = 0
def run(self):
self.server= socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind((self.ip, self.port))
self.server.listen(self.connectionNumber)
print("SERVER: Server is running")
print("SERVER: Waiting for connection")
client, addr = self.server.accept()
print("SERVER: Something connected at {}".format(addr))
time.sleep(10) # Simulating doing something
client.close()
self.server.close()
print("Server is closed")
class Client(threading.Thread):
def __init__(self):
super(Client, self).__init__()
self.ip = "localhost"
self.port = 23071
def run(self):
time.sleep(3) #Ensure the client socket is created after the server socket
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
print("CLIENT. Trying to connect")
self.server.connect((self.ip, self.port))
print("CLIENT. Connection sucessful")
time.sleep(2) # SImulating doing something
except Exception as e:
print("CLIENT: Exception \"{}\" happend".format(e))
finally:
self.server.close()
print("CLIENT: Socket closed")
if __name__ == "__main__":
server = Server()
client = Client()
server.start()
client.start()
server.join()
client.join()
class Server(threading.Thread):
def __init__(self):
super(Server, self).__init__()
self.ip = "localhost"
self.port = 23071
self.connectionNumber = 0
def run(self):
time.sleep(3) #Ensure server socket is created after client socket
self.server= socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind((self.ip, self.port))
self.server.listen(self.connectionNumber)
print("SERVER: Server is running")
print("SERVER: Waiting for connection")
client, addr = self.server.accept()
print("SERVER: Something connected at {}".format(addr))
time.sleep(10) # Simulating doing something
client.close()
self.server.close()
print("Server is closed")
class Client(threading.Thread):
def __init__(self):
super(Client, self).__init__()
self.ip = "localhost"
self.port = 23071
def run(self):
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
print("CLIENT. Trying to connect")
self.server.connect((self.ip, self.port))
print("CLIENT. Connection sucessful")
time.sleep(2) # SImulating doing something
except Exception as e:
print("CLIENT: Exception \"{}\" happend".format(e))
finally:
self.server.close()
print("CLIENT: Socket closed")
class Server(threading.Thread):
def __init__(self):
super(Server, self).__init__()
self.ip = "localhost"
self.port = 23071
self.connectionNumber = 0
def run(self):
self.server= socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind((self.ip, self.port))
self.server.listen(self.connectionNumber)
print("SERVER: Server is running")
#Ensure the server socket is created when the client wants to make a connection
#but the server isn't waiting for new connections when the client establishes a connection
#(.accept() call delayed)
time.sleep(3)
print("SERVER: Waiting for connection")
client, addr = self.server.accept()
print("SERVER: Something connected at {}".format(addr))
time.sleep(10) # Simulating doing something
client.close()
self.server.close()
print("Server is closed")
class Client(threading.Thread):
def __init__(self):
super(Client, self).__init__()
self.ip = "localhost"
self.port = 23071
def run(self):
time.sleep(1) #Ensure client socket is created after server socket
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
print("CLIENT. Trying to connect")
self.server.connect((self.ip, self.port))
print("CLIENT. Connection sucessful")
time.sleep(2) # SImulating doing something
except Exception as e:
print("CLIENT: Exception \"{}\" happend".format(e))
finally:
self.server.close()
print("CLIENT: Socket closed")
if __name__ == "__main__":
server = Server()
client = Client()
server.start()
client.start()
server.join()
client.join()
In the first variant, a connection can be established without problems. I expected it to be this way, because the server is waiting for new connections with .accept(). In second variant a connection can not be establish, due to the fact, that there is no server socket yet. The connection cannot reach a target (Exception happens. WinError 1106..something). Also expected. But the third one confuses me. I expected that a connection cannot be established, due to the fact that a server socket exists, but the server is not accepting new connections yet (with .accept() not called) and the max. number of connections in the backlog is 0 (.listen(0)). Still the .connect() call of the client is not blocking nor throwing an exception. It states: "CLIENT: Connection sucessful". What happend? I expected the call to block, because I never specified a timeout and the connection will never be accepted.
I hope someone of you can explain it to me what has happend in detail. I found similiar topics here on Stackoverflow and on other sides, but I've not found an answer that got rid of my confusion.
Greetings, Tmirror
EDIT:
After further investigations with wireshark I found out the following:
server.listen(x) "fires up" the socket. From this point on, the server socket can perform the three way handshake through responding to the client initiating the three way handshake.
server.listen(x) allows up to x elements in the backlog. This means up to x elements can perform the three way handshake. After performing it or during it, they are queued in the backlog. They are taken out of the backlog if the server socket calls .accept(). So the backlog queue always contains client connection which have already performed a 3-way handshake or are doing it currently. x determines the size of this backlog queue.
Therefor I'd like to change my question a little bit. It is quiet obvious now why socket.connect() doesn't throw an exception or is blocking. It can sucessfully perform the 3-way handshake and the connection is open. But what does socket.accept() do then? It obviously takes one element out of the backlog queue, but what does it mean in terms of communication between server and client and in terms of the tcp protocol?
But what does socket.accept() do then? It obviously takes one element out of the backlog queue, but what does it mean in terms of communication between server and client and in terms of the tcp protocol?
It does nothing in terms of communication between server and client and in terms of the tcp protocol. It just associates a file descriptor of a process with the connection, i. e. it sets up operating system data structure. We can see the effect with netstat
, e. g. on Windows with the -b
or -o
option.