def start_connecting(self): print "Connecting to %s:%d" % self.address self._is_connecting = True self._socket_to_host = socketutils.new_udp_socket() for i in xrange(ATTEMPT_COUNT): # Send version to identify both our version and the local port of this connection self._socket_to_host.sendto(dumps((self.protocol_version, self.local_host.id, self.local_host.name)), self.address) # Receive hello as ack and to know the remote port of this particular connection try: socketutils.wait_for_read([self._socket_to_host], timeout=0.5) except socketutils.TimeoutError: pass else: request, remote_address = self._socket_to_host.recvfrom(MAX_PACKET_SIZE) request_type, request_data = loads(request) if request_type == 'NOTCONNECTING!': raise ConnectionFailed("Other host has stopped connecting to us!") assert request_type == 'WELCOME!', "Expected WELCOME, received %r" % (request_type,) print "Received welcome response" self._publicized_data, host_ids = request_data # The welcome is sent from a different port (not the listening port)... self._socket_to_host.connect(remote_address) self._listener_socks = dict([(socketutils.udp_listener_on(0), (id, name)) for id, name in host_ids]) self._send_listening_on_ports() break else: raise ConnectionFailed("Cannot connect to given host") self.update = self._attempt_connecting
def _send_welcome(self): self._sync_actions() conn = self._awaiting_connection remote_hostname, remote_port = conn.remote_address # Flood him with WELCOME!'s until he responds... conn.sock.send(conn._welcome_to_send) try: socketutils.wait_for_read([conn.sock], timeout=0.5) except socketutils.TimeoutError: conn._welcome_count += 1 if conn._welcome_count >= ATTEMPT_COUNT: self.update = self._attempt_listening self._awaiting_connection = None return data = conn.sock.recv(MAX_PACKET_SIZE) request_type, ports = loads(data) assert request_type == 'listening_on' self.notify("new_connector2", conn.remote_host_name, conn.remote_address) for host, port in zip(self.hosts, ports): self.run_action_on(host, 'INTERNAL_connect_to', conn.remote_host_id, conn.remote_host_name, conn.remote_address, (remote_hostname, port)) self.update = self._attempt_listening self._awaiting_connection = None
def _attempt_listening(self): self._sync_actions() if self._awaiting_connection is not None: # Cannot handle multiple connection requests at the same time return try: socketutils.wait_for_read([self._listener_sock], timeout=0) except socketutils.TimeoutError: return conn = Bunch() data, conn.remote_address = self._listener_sock.recvfrom(MAX_PACKET_SIZE) remote_hostname, remote_port = conn.remote_address remote_version, conn.remote_host_id, conn.remote_host_name = loads(data) assert remote_version == self.protocol_version, "Attempt to connect with client of different version" if conn.remote_address in self._already_connected: # We may be receiving a retransmit of the connection # packet from before, anyhow, he is already connected, so # ignore it. return self.notify("new_connector1", conn.remote_host_name, conn.remote_address) conn.sock=socketutils.new_udp_socket() conn.sock.connect(conn.remote_address) host_ids = [(host.id, host.name) for host in self.hosts] welcome_to_send = dumps(('WELCOME!', (self._publicized_data, host_ids))) conn._welcome_to_send = welcome_to_send conn._welcome_count = 0 self._awaiting_connection = conn self.update = self._send_welcome
def _recv(self, timeout=RECV_FRAME_TIMEOUT): # Note that UDP receive will only receive from the correct # port due to the connect call from before socketutils.wait_for_read([self.sock], timeout=timeout) try: data = self.sock.recv(MAX_PACKET_SIZE) except socket.error, s: self._handle_socket_error(s) # Convert the disconnected error to another error that may occur # when disconnection occurs to unify the error handling. raise socketutils.TimeoutError()
def _listen_to_all_create_remote_hosts(self): while self._listener_socks: read_available = socketutils.wait_for_read(list(self._listener_socks), timeout=60) for sock in read_available: remote_host_id, remote_host_name = self._listener_socks[sock] del self._listener_socks[sock] # Get the first Actions packet only to extract # iteration_count and the remote address data, address = sock.recvfrom(MAX_PACKET_SIZE) sock.connect(address) frame_type, (executed_iteration_count, iterations) = loads(data) assert frame_type == 'Actions' self.iteration_count = executed_iteration_count # Discard this Actions packet # It is okay because it is assumed to be losable and # because the next packet will contain all of its # actions again. # The first action set we receive MUST fix the random # seed (or we'll be out of sync) remote_host = RemoteHost(remote_host_id, remote_host_name, address, sock) self._add_remote_host(remote_host)
def _attempt_connecting(self): try: all_files = [self._socket_to_host] + list(self._listener_socks) read_available = socketutils.wait_for_read(all_files, timeout=0) except socketutils.TimeoutError: return if read_available == [self._socket_to_host]: request, remote_address = self._socket_to_host.recvfrom(MAX_PACKET_SIZE) request_type, request_data = loads(request) # This can only be WELCOME because UNWELCOME cannot follow WELCOME assert request_type == 'WELCOME!', "Expected WELCOME, received %r" % (request_type,) # Hmm.. we are Welcome'd again... so appearantly he did not see we sent him our port list.. self._send_listening_on_ports() else: # Somebody connects to us! This means that the entire network is in sync, and knows we're in! self.notify('got_first_connection') self._listen_to_all_create_remote_hosts() self.notify('in_network') self.update = self._sync_actions