def send_scp_burst(self, buffer_size, window_size,
                       parameters_and_callbacks):
        """Send a burst of SCP packets and call a callback for each returned
        packet.

        Parameters
        ----------
        buffer_size : int
            Number of bytes held in an SCP buffer by SARK, determines how many
            bytes will be expected in a socket.
        window_size : int
            Number of packets which can be awaiting replies from the SpiNNaker
            board.
        parameters_and_callbacks: iterable of :py:class:`.scpcall`
            Iterable of :py:class:`.scpcall` elements.  These elements can
            specify a callback which will be called with the returned packet.
        """
        parameters_and_callbacks = iter(parameters_and_callbacks)

        self.sock.setblocking(False)

        # Calculate the receive length, this should be the smallest power of
        # two greater than the required size
        max_length = buffer_size + consts.SDP_HEADER_LENGTH
        receive_length = int(2**math.ceil(math.log(max_length, 2)))

        class TransmittedPacket(object):
            """A packet which has been transmitted and still awaits a response.
            """
            __slots__ = ["callback", "packet", "bytestring", "n_tries",
                         "timeout", "timeout_time"]

            def __init__(self, callback, packet, timeout):
                self.callback = callback
                self.packet = packet
                self.bytestring = packet.bytestring
                self.n_tries = 1
                self.timeout = timeout
                self.timeout_time = time.time() + self.timeout

        queued_packets = True
        outstanding_packets = {}
        outstanding_callbacks = collections.deque()

        # While there are packets in the queue or packets for which we are
        # still awaiting returns then continue to loop.
        while queued_packets or outstanding_packets or outstanding_callbacks:
            # If there are fewer outstanding packets than the window can take
            # and we still might have packets left to send then transmit a
            # packet and add it to the list of outstanding packets.
            while len(outstanding_packets) < window_size and queued_packets:
                try:
                    args = next(parameters_and_callbacks)
                except StopIteration:
                    queued_packets = False

                if queued_packets:
                    # If we extracted a new packet to send then create a new
                    # outstanding packet and transmit it.
                    seq = next(self.seq)
                    while seq in outstanding_packets:
                        # The seq should rarely be already taken, it normally
                        # means that one packet is taking such a long time to
                        # send that the sequence has wrapped around.  It's not
                        # a problem provided that we don't reuse the number.
                        seq = next(self.seq)

                    # Construct the packet that we'll be sending
                    packet = SCPPacket(
                        reply_expected=True, tag=0xff, dest_port=0,
                        dest_cpu=args.p, src_port=7, src_cpu=31,
                        dest_x=args.x, dest_y=args.y, src_x=0, src_y=0,
                        cmd_rc=args.cmd, seq=seq,
                        arg1=args.arg1, arg2=args.arg2, arg3=args.arg3,
                        data=args.data
                    )

                    # Create a reference to this packet so that we know we're
                    # expecting a response for it and can retransmit it if
                    # necessary.
                    outstanding_packets[seq] = TransmittedPacket(
                        args.callback, packet,
                        self.default_timeout + args.timeout
                    )

                    # Actually send the packet
                    self.sock.send(outstanding_packets[seq].bytestring)

            # Call all outstanding callbacks
            while outstanding_callbacks:
                callback, packet = outstanding_callbacks.pop()
                callback(packet)

            # Listen on the socket for an acknowledgement packet, there may not
            # be one.
            if outstanding_packets:
                timeout = min((o.timeout_time for o in
                               six.itervalues(outstanding_packets)
                               )) - time.time()
            else:
                timeout = 0.0
            r, w, x = select.select([self.sock], [], [], max(timeout, 0.0))
            # Process the received packet (if there is one).
            while r:
                # Note that 'r' is never changed so this while loop will either
                # get skipped if r is empty or loop until 'break' of r is not.
                # Since we may receive multiple packets at once, it is better
                # to try and pull all out of the socket immediately rather than
                # running around the parent loop again and incuring the
                # 'select' cost.
                try:
                    ack = self.sock.recv(receive_length)
                except IOError:
                    break

                # Extract the sequence number from the bytestring, iff possible
                rc, seq = struct.unpack_from("<2H", ack,
                                             consts.SDP_HEADER_LENGTH + 2)

                # If the code is an error then we respond immediately
                if rc != consts.SCPReturnCodes.ok:
                    if rc in consts.RETRYABLE_SCP_RETURN_CODES:
                        # If the error is timeout related then treat the packet
                        # as though it timed out, just discard.  This avoids us
                        # hammering the board when it's most vulnerable.
                        pass
                    else:
                        # For all other errors, we'll just fall over
                        # immediately.
                        packet = outstanding_packets.get(seq)
                        if packet is not None:
                            packet = packet.packet
                        raise FatalReturnCodeError(rc, packet)
                else:
                    # Look up the sequence index of packet in the list of
                    # outstanding packets.  We may have already processed a
                    # response for this packet (indicating that the response
                    # was delayed and we retransmitted the initial message) in
                    # which case we can silently ignore the returned packet.
                    # XXX: There is a danger that a response was so delayed
                    # that we already reused the seq number... this is probably
                    # sufficiently unlikely that there is no problem.
                    outstanding = outstanding_packets.pop(seq, None)
                    if outstanding is not None:
                        outstanding_callbacks.appendleft((outstanding.callback,
                                                          ack))

            # Look through all the remaining outstanding packets, if any of
            # them have timed out then we retransmit them.
            current_time = time.time()
            for outstanding in six.itervalues(outstanding_packets):
                if outstanding.timeout_time < current_time:
                    # This packet has timed out, if we have sent it more than
                    # the given number of times then raise a timeout error for
                    # it.
                    if outstanding.n_tries >= self.n_tries:
                        raise TimeoutError(
                            "No response after {} attempts.".format(
                                self.n_tries),
                            outstanding.packet)

                    # Otherwise we retransmit it
                    self.sock.send(outstanding.bytestring)
                    outstanding.n_tries += 1
                    outstanding.timeout_time = (current_time +
                                                outstanding.timeout)