class SimplestConnectionEstablishmentPlayer(Locust): min_wait = 1000 max_wait = 1000 task_set = SimplestConnectionEstablishmentTaskSet def _init_sio_client(self): try: self.client = SocketIO( self.host, self.port, resource='sio', transports=['websocket'], params={ 'userid': self.player_id, 'roomid': self.room_id }, wait_for_connection=False # Disabling auto-reconnection. ) ''' The NodeJs server MUST be using `"socket.io": "1.7.2"` or an error complaining " ... socketIO_client.__init__, in _get_engineIO_session ... ... transport.recv_packet() ... " would be thrown here. ''' self.client.on('connect', self.on_connect) self.client.on('disconnect', self.on_disconnect) self.client.on('message', self.on_message) except ConnectionError: print( "SimplestConnectionEstablishmentPlayer instance for player.id == %s to room_id == %s. The sio-server is down, try again later." % (self.player_id, self.room_id)) raise GreenletExit() except KeyboardInterrupt: raise GreenletExit() def on_connect(self): print( 'SimplestConnectionEstablishmentPlayer instance for player.id == %s to room_id == %s, connected to sio-server.' % (self.player_id, self.room_id)) def on_disconnect(self): ''' A subtlety to be concerned here. ''' if (True == self.client._should_stop_waiting()): # If the execution reaches here by actively calling `self.client.disconnect()`, then one finds "True == self.client._should_stop_waiting() == self.client._wants_to_close". print( '[ACTIVELY DISCONNECTED] SimplestConnectionEstablishmentPlayer for player.id == %s to room_id == %s.' % (self.player_id, self.room_id)) raise StopLocust() # This is within the "main locust". else: # If the execution reaches here passively within `self.client.wait()`, then one finds "False == self.client._should_stop_waiting() == self.client._wants_to_close", and should help terminate the loop in the spawned `self.client.wait()`. See https://github.com/invisibleroads/socketIO-client/blob/master/socketIO_client/__init__.py for details (socketIO_client v0.7.2). print( '[PASSIVELY DISCONNECTED] SimplestConnectionEstablishmentPlayer for player.id == %s to room_id == %s.' % (self.player_id, self.room_id)) self.client._close() gevent.getcurrent().spawning_greenlet().kill() ''' Killing the current `gevent.Greenlet` instance silently. Quoted from http://www.gevent.org/api/gevent.greenlet.html#gevent.GreenletExit for "gevent v1.3.7.dev0". " A special exception that kills the greenlet silently. When a greenlet raises GreenletExit or a subclass, the traceback is not printed and the greenlet is considered successful. The exception instance is available under value property as if it was returned by the greenlet, not raised. " ''' raise GreenletExit( ) # This is within the "spawned greenlet from main locust". def on_message(self, *args): print( 'SimplestConnectionEstablishmentPlayer instance for player.id == %s to room_id == %s, on_message %s.' % (self.player_id, self.room_id, args)) def __init__(self, *args, **kwargs): ''' Note that `self.setup` will run within the immediately following invocation if "False == Locust._setup_has_run", see https://github.com/locustio/locust/blob/master/locust/core.py for details (Locust v0.9). ''' super(SimplestConnectionEstablishmentPlayer, self).__init__(*args, **kwargs) fp = baseoper.get_single_random_player() self.player_id = fp[0] self.room_id = fp[1] sio_server = baseoper.get_sio_server_host_port() self.host = sio_server[0] self.port = int(sio_server[1]) ''' The call to `self._init_sio_client()` SHOULDN'T be put into `"self.setup" or "Locust.setup"` which runs only once for "all locusts spawned by the current OS process", due to "Locust._setup_has_run" being a class-static-variable (unless otherwise hacked via `self._setup_has_run` which is unnecessary), see https://github.com/locustio/locust/blob/master/locust/core.py for details (Locust v0.9). Same argument holds for "Locust.teardown", "TaskSet.setup" and "TaskSet.teardown". ''' self._init_sio_client() def setup(self): print( "SimplestConnectionEstablishmentPlayer.setup completed. I'm called only once for all locusts (NOT `once per locust`) during the lifetime of the current OS process." ) def teardown(self): print( "SimplestConnectionEstablishmentPlayer all instances have been torn down. I'm called only once for all locusts (NOT `once per locust`) during the lifetime of the current OS process." )