Уменьшение дрожания в прямой связи Android по WiFi с использованием транспортного протокола UDP - PullRequest
0 голосов
/ 16 декабря 2018

Я работаю над приложением реального времени, которое требует передачи около 512 байтов данных каждые 5 мс по прямому соединению WiFi от подчиненного телефона / ов к главному устройству (владельцу группы).Средняя задержка передачи составляет около 15 мс, что не так уж и хорошо, но самая большая проблема заключается в том, что я заметил довольно постоянную последовательность периодов, когда задержка составляет примерно 100 мс каждые 2-3 секунды, плюс периоды около 9 секунд каждую минуту, когдапропускная способность значительно уменьшается.Пожалуйста, посмотрите на график график рассеяния задержки во времени .Я также приложил свой код программирования сокета.И главные, и подчиненные внутренние часы синхронизируются с ошибкой в ​​1 мс (не беспокойтесь о смещении тактовой частоты).Пожалуйста, дайте мне знать, если вы тоже испытали эти результаты и есть ли что-то, что можно сделать, чтобы улучшить эту проблему.Интересно, что обычно потеря данных равна 0, и когда пакет задерживается, все пакеты перед ним тоже (это легко увидеть из графика), даже если это UDP.Я где-то читал, что пакеты должны быть подтверждены на уровне MAC, даже если используется UDP, и это может вызывать задержки около 100 мс.

Ниже приведен код на ведомом устройстве (отправителе):

public class DataTransmissionServiceV3 {

public static final String TAG = "DataTransmissionService";

private long mClockOffset;
private DatagramChannel mDatagramChannel;
private boolean running;

public void start(InetAddress remoteAddress, long clockOffset) {
    if (!running) {
        running = true;
        mClockOffset = clockOffset;
        Thread t = new Thread(new TransmissionTask(remoteAddress));
        t.setPriority(Thread.MAX_PRIORITY);
        t.start();
    }
}

public void close() {
    running = false;
    if (mDatagramChannel != null) {
        mDatagramChannel.socket().close();
    }
}

private class TransmissionTask implements Runnable {

    private int mCount;
    private final ByteBuffer mByteBuffer = ByteBuffer.allocateDirect(
            Constants.DATA_PACKET_SIZE);
    private final byte[] mBytes = new byte[Constants.DATA_PACKET_SIZE];
    private final InetAddress mRemoteAddress;

    public TransmissionTask(final InetAddress remoteAddress) {
        mRemoteAddress = remoteAddress;
    }

    @Override
    public void run() {
        try {
            mDatagramChannel = DatagramChannel.open();
            mDatagramChannel.socket().connect(new InetSocketAddress(mRemoteAddress,
                    Constants.TRANS_MASTER_SERVER_PORT));
            mDatagramChannel.configureBlocking(false);
        } catch (IOException ioe) {
            Log.e(TAG, "Exception while connecting", ioe);
        }
        while (running) {
            TimeUtils.busyWait(5000000); // busy wait for 5ms
            send();
        }
    }

    private void send() {
        mCount++;
        DataPacket.setSequenceNumber(mBytes, mCount);
        DataPacket.setTimestamp(mBytes, TimeUtils.getTimestamp() + mClockOffset); // synchronized timestamp to master device to measure delay
        mByteBuffer.clear();
        mByteBuffer.put(mBytes);
        mByteBuffer.flip();
        try {
            mDatagramChannel.write(mByteBuffer);
        } catch (Exception e) {
            Log.e(TAG, "Exception while sending data packet", e);
            running = false;
        }
    }
}

}

И следующий код у мастера (получателя):

public class DataTransmissionServerV3 {

public static final String TAG = "DataTransmissionServer";

private DatagramChannel mDatagramChannel;
private boolean mRunning;
private Handler mHandler;

public DataTransmissionServerV3(Handler handler) {
    mHandler = handler;
}

public void start(InetAddress localAddress) {
    if (!mRunning) {
        mRunning = true;
        Thread t = new Thread(new ReceiveDataTask(localAddress));
        t.setPriority(Thread.MAX_PRIORITY);
        t.start();
    }
}

public void close() {
    mRunning = false;
    if (mDatagramChannel != null) {
        mDatagramChannel.socket().close();
    }
}

private class ReceiveDataTask implements Runnable {

    private final InetAddress mLocalAddress;
    private final ByteBuffer mByteBuffer = ByteBuffer.allocateDirect(
            Constants.DATA_PACKET_SIZE);
    private final byte[] mBytes = new byte[Constants.DATA_PACKET_SIZE];
    private int mCount;

    public ReceiveDataTask(final InetAddress localAddress) {
        mLocalAddress = localAddress;
    }

    @Override
    public void run() {
        try {
            bind();
            while (mRunning) {
                receive();
            }
        } catch (IOException ioe) {
            Log.e(TAG, "Exception while binding datagram socket", ioe);
        }
    }

    private void bind() throws IOException {
        mDatagramChannel = DatagramChannel.open();
        mDatagramChannel.socket().bind(new InetSocketAddress(mLocalAddress,
                Constants.TRANS_MASTER_SERVER_PORT));
        mDatagramChannel.configureBlocking(true);
    }

    private boolean receive() {
        mByteBuffer.clear();
        try {
            SocketAddress isa = mDatagramChannel.receive(mByteBuffer);
            long t2 = TimeUtils.getTimestamp();
            if (isa != null) {
                mByteBuffer.flip();
                mByteBuffer.get(mBytes);
                mCount++;
                TransmissionStat stat = TransmissionStat.get(mBytes, mCount, t2);
                handlePacket(stat); // a statistic that is saved to file for later analysis (ignore this)
                return true;
            }
        } catch (IOException ioe) {
            Log.e(TAG, "Exception while receiving data", ioe);
            mRunning = false;
        }
        return false;
    }

    private void handlePacket(TransmissionStat stat) {
        Message msg = mHandler.obtainMessage();
        msg.what = Constants.TRANSMISSION_PACKET_RECEIVED_CODE;
        msg.obj = stat;
        msg.sendToTarget();
    }
}
...