I had an exercise of port carousel which means that I need to build a server-client which the server asks the client for a port and then they starting to listen to the port that given, and this is the loop I got a error and I don't know how to fix it.
server:
import socket
import random
def main():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('0.0.0.0', 1729))
server_socket.listen(1)
(client_socket, server_socket) = server_socket.accept()
done = False
while not done:
port = client_socket.recv(4096)
client_socket.send('i got the port' + port)
port = int(port)
if port != 1:
server_socket.bind(('0.0.0.0', port))
continue
else:
done = True
if __name__ == '__main__':
main()
client:
import socket
import random
def main():
print 'hi at anytime enter 1 to break the loop'
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(('127.0.0.1', 1729))
done = False
while not done:
port = client_socket.send(raw_input("enter port:"))
data = client_socket.recv(4096)
print data
port = int(port)
if port != 1:
client_socket.connect(('127.0.0.1', port))
continue
else:
done = True
client_socket.close()
if __name__ == '__main__':
main()
the error output for the server:
File "C:/Cyber/ServerFolder/ports_carrousel.py", line 18, in main
server_socket.bind(('0.0.0.0', port))
AttributeError: 'tuple' object has no attribute 'bind'
2 Answers 2
In your main function, you do the following:
(client_socket, server_socket) = server_socket.accept()
but, server_socket.accept() actually returns two objects. The first, is a socket object, and the second one is a tuple that contains (sourceIPString, sourcePort).
Thus, by using this line of code, outlined above, you are essentially overriding the server_socket by a tuple object. Notice that later, in line 18, you are trying to access the "bind" function of a socket, but, using a reference to a tuple object, that does not implement such a function.
What you should be doing is something along the lines of
(client_socket, client_connection_info) = server_socket.accept()
and adjust your code accordingly.
Comments
Just a couple of things wrong here. First, accept returns a 2-tuple containing the newly-connected socket, and the client's address (which is itself a 2-tuple of IP address and port number). It does not return two sockets. But you're overwriting your server_socket variable with the second returned value. That doesn't make sense and it's why the interpreter is telling you that the 2-tuple has no bind attribute: it's not a socket object. The accept call should look something like this:
client_socket, client_addr = server_socket.accept()
Next, after receiving the new port number from the client, you must create a new socket (you cannot re-use the same listening socket), then bind that new socket to the new port, then listen; finally you can accept a new client connection from the new listening socket.
You should also close sockets you're finished with so that you don't continually leak file descriptors. That means each time you receive a new port number from the client, you should close the client socket, and the listening socket, then create a new listening socket (and bind and listen), then accept the new client socket.
Altogether that will mean restructuring your code in the server significantly. You need to pull the creation of a listening socket down into your main while not done loop.
Another thing to keep in mind. On the client side, immediately after sending the port number to the server, you're attempting a connect to that new port number. However, it's almost certain that your connect request will reach the server before the server has had a chance to create a new listening socket, and bind it. So your client will either need to delay a moment before attempting to connect, or it will need to have logic to retry the connect for some period of time.
EDIT:
Also, you must create a new socket on the client side too when reconnecting. Once a stream socket has been bound to a port (which also happens automatically when you connect), you can never use it to bind or connect to a different address/port.