docs: async def _connect(self):
          """
          Performs the actual connection, retrying, generating the
          authorization key if necessary, and starting the send and
          receive loops.
          """
          self._log.info('Connecting to %s...', self._connection)
          for attempt in retry_range(self._retries):
              try:
                  self._log.debug('Connection attempt {}...'.format(attempt))
                  await self._connection.connect(timeout=self._connect_timeout)
              except (IOError, asyncio.TimeoutError) as e:
                  self._log.warning('Attempt {} at connecting failed: {}: {}'
                                    .format(attempt, type(e).__name__, e))
                  await asyncio.sleep(self._delay)
              else:
                  break
          else:
              raise ConnectionError('Connection to Telegram failed {} time(s)'
                                    .format(attempt))
  
          self._log.debug('Connection success!')
          if not self.auth_key:
              plain = MTProtoPlainSender(self._connection, loggers=self._loggers)
              for attempt in retry_range(self._retries):
                  try:
                      self._log.debug('New auth_key attempt {}...'
                                      .format(attempt))
                      self.auth_key.key, self._state.time_offset =\
                          await authenticator.do_authentication(plain)
  
                      # This is *EXTREMELY* important since we don't control
                      # external references to the authorization key, we must
                      # notify whenever we change it. This is crucial when we
                      # switch to different data centers.
                      if self._auth_key_callback:
                          self._auth_key_callback(self.auth_key)
  
                      break
                  except (SecurityError, AssertionError) as e:
                      self._log.warning('Attempt {} at new auth_key failed: {}'
                                        .format(attempt, e))
                      await asyncio.sleep(self._delay)
              else:
                  e = ConnectionError('auth_key generation failed {} time(s)'
                                      .format(attempt))
                  await self._disconnect(error=e)
                  raise e
  
          self._log.debug('Starting send loop')
          self._send_loop_handle = self._loop.create_task(self._send_loop())
  
          self._log.debug('Starting receive loop')
          self._recv_loop_handle = self._loop.create_task(self._recv_loop())
  
          # _disconnected only completes after manual disconnection
          # or errors after which the sender cannot continue such
          # as failing to reconnect or any unexpected error.
          if self._disconnected.done():
              self._disconnected = self._loop.create_future()
  
          self._log.info('Connection to %s complete!', self._connection)