Как получить задержку между сервером и клиентом в C #? - PullRequest
7 голосов
/ 21 апреля 2009

Я работаю над приложением C # Server для игрового движка, который пишу в ActionScript 3. Я использую авторитетную модель сервера, чтобы предотвратить мошенничество и обеспечить честную игру. Пока все работает хорошо:

Когда клиент начинает перемещаться, он сообщает серверу и начинает рендеринг локально; Затем сервер сообщает всем остальным, что клиент X начал перемещаться, в том числе подробности, чтобы они также могли начать рендеринг. Когда клиент перестает перемещаться, он сообщает серверу, который выполняет вычисления на основе времени, когда клиент начал перемещаться, и клиент отображает задержку тика и отвечает всем, чтобы они могли обновиться с правильными значениями.

Дело в том, что когда я использую задержку по умолчанию на 20 мс для вычислений на сервере, когда клиент перемещается на довольно большое расстояние, при его остановке происходит заметный наклон вперед. Если я немного увеличу задержку до 22 мс, в моей локальной сети все будет работать очень плавно, но в других местах наклон все еще есть. Немного поэкспериментировав, я заметил, что необходимая дополнительная задержка в значительной степени связана с задержкой между клиентом и сервером. Я даже свел это к формуле, которая будет работать очень хорошо: задержка = 20 + (задержка / 10).

Итак, как мне поступить, чтобы получить задержку между определенным клиентом и сервером (я использую асинхронные сокеты). Усилие ЦП не может быть слишком большим, чтобы сервер не работал медленно. Кроме того, это действительно лучший способ, или есть более эффективный / простой способ сделать это?

Ответы [ 5 ]

11 голосов
/ 25 апреля 2009

Извините, что это не дает прямого ответа на ваш вопрос, но, вообще говоря, вы не должны слишком сильно полагаться на измерение задержки, потому что она может быть весьма переменной. Мало того, вы не знаете, является ли измеренное вами время пинга симметричным, что важно. Нет смысла применять 10 мс коррекции задержки, если выясняется, что время пинга 20 мс фактически составляет 19 мс от сервера к клиенту и 1 мс от клиента к серверу. И задержка в терминах приложения не такая, как в терминах сети - вы можете пропинговать определенную машину и получить ответ через 20 мс, но если вы обращаетесь к серверу на этой машине, который обрабатывает сетевой ввод только 50 раз в секунду, то Ваши ответы будут задержаны на дополнительные от 0 до 20 мс, и это будет довольно непредсказуемо.

Это не означает, что измерение задержки не имеет смысла в сглаживании прогнозов, но не решит вашу проблему, просто немного ее очистите.

На первый взгляд, проблема здесь заключается в том, что вы отправили информацию в первом сообщении, которое вы используете для экстраполяции данных до получения последнего сообщения. Если все остальное остается постоянным, то вектор движения, указанный в первом сообщении, умноженный на время между сообщениями, даст серверу правильную конечную позицию, в которой клиент находился примерно сейчас - (latency / 2). Но если задержка изменится вообще, время между сообщениями будет увеличиваться или уменьшаться. Клиент может знать, что он переместился на 10 единиц, но сервер имитировал его перемещение на 9 или 11 единиц, прежде чем ему было приказано привязать его к 10 единицам.

Общее решение этого заключается в том, чтобы не предполагать, что задержка будет оставаться постоянной, а отправлять периодические обновления позиции, которые позволяют серверу проверять и исправлять позицию клиента. Всего 2 сообщения, как у вас сейчас, все ошибки найдены и исправлены после 2-го сообщения. При большем количестве сообщений ошибка распространяется на большее количество точек выборки, что обеспечивает более плавную и менее видимую коррекцию.

Это никогда не может быть совершенным, хотя: все, что требуется, - это скачок задержки в последнюю миллисекунду движения, и представление сервера будет превышено. Вы не сможете обойти это, если прогнозируете будущее движение на основе прошлых событий, поскольку нет реальной альтернативы выбору правильного, но позднего или неправильного, но своевременного, поскольку информация требует времени для путешествия. (Виноват Эйнштейн.)

4 голосов
/ 04 ноября 2009

При использовании ICMP на основе ping следует помнить, что сетевое оборудование часто будет отдавать трафику ICMP более низкий приоритет, чем нормальные пакеты, особенно когда пакеты пересекают границы сети, такие как каналы WAN. Это может привести к тому, что эхо-запросы будут сброшены или будут иметь большую задержку, чем трафик на самом деле, и могут служить индикатором проблем, а не инструментом измерения.

Растущее использование Качество обслуживания (QoS) в сетях только усугубляет это, и, как следствие, хотя пинг по-прежнему остается полезным инструментом, необходимо понимать, что он не может быть истинным отражением сетевая задержка для реального трафика, не основанного на ICMP.

В блоге Itrinegy есть хороший пост Как вы оцениваете латентность (RTT) в сети в эти дни? об этом.

4 голосов
/ 25 апреля 2009

Вы можете использовать уже доступный Ping Класс. Должно быть предпочтительнее написания вашего собственного ИМХО.

2 голосов
/ 21 апреля 2009

Имеется команда «ping», в которой вы отправляете сообщение с сервера клиенту, а затем время, необходимое для получения ответа. За исключением сценариев перегрузки процессора, он должен быть довольно надежным. Чтобы получить время поездки в одну сторону, просто разделите время на 2.

1 голос
/ 12 марта 2018

Мы можем измерить туда-обратно время , используя Ping класс .NET Framework.

Создание экземпляра Ping и подписка на событие PingCompleted:

Ping pingSender = new Ping();
pingSender.PingCompleted += PingCompletedCallback;

Добавьте код для настройки и действия пинга.

Наш обработчик событий PingCompleted (PingCompletedEventHandler) имеет аргумент PingCompletedEventArgs. PingCompletedEventArgs.Reply дает нам объект PingReply. PingReply.RoundtripTime возвращает время приема-передачи («количество миллисекунд, затраченное на отправку эхо-запроса протокола управляющих сообщений Интернета (ICMP) и получение соответствующего эхо-сообщения ответа ICMP»):

public static void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
    ...
    Console.WriteLine($"Roundtrip Time: {e.Reply.RoundtripTime}");
    ...
}

Дамп кода полного рабочего примера, основанный на примере MSDN . Я изменил его, чтобы записать RTT на консоль:

public static void Main(string[] args)
{
    string who = "www.google.com";
    AutoResetEvent waiter = new AutoResetEvent(false);

    Ping pingSender = new Ping();

    // When the PingCompleted event is raised,
    // the PingCompletedCallback method is called.
    pingSender.PingCompleted += PingCompletedCallback;

    // Create a buffer of 32 bytes of data to be transmitted.
    string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
    byte[] buffer = Encoding.ASCII.GetBytes(data);

    // Wait 12 seconds for a reply.
    int timeout = 12000;

    // Set options for transmission:
    // The data can go through 64 gateways or routers
    // before it is destroyed, and the data packet
    // cannot be fragmented.
    PingOptions options = new PingOptions(64, true);

    Console.WriteLine("Time to live: {0}", options.Ttl);
    Console.WriteLine("Don't fragment: {0}", options.DontFragment);

    // Send the ping asynchronously.
    // Use the waiter as the user token.
    // When the callback completes, it can wake up this thread.
    pingSender.SendAsync(who, timeout, buffer, options, waiter);

    // Prevent this example application from ending.
    // A real application should do something useful
    // when possible.
    waiter.WaitOne();
    Console.WriteLine("Ping example completed.");
}

public static void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
    // If the operation was canceled, display a message to the user.
    if (e.Cancelled)
    {
        Console.WriteLine("Ping canceled.");

        // Let the main thread resume. 
        // UserToken is the AutoResetEvent object that the main thread 
        // is waiting for.
        ((AutoResetEvent)e.UserState).Set();
    }

    // If an error occurred, display the exception to the user.
    if (e.Error != null)
    {
        Console.WriteLine("Ping failed:");
        Console.WriteLine(e.Error.ToString());

        // Let the main thread resume. 
        ((AutoResetEvent)e.UserState).Set();
    }

    Console.WriteLine($"Roundtrip Time: {e.Reply.RoundtripTime}");

    // Let the main thread resume.
    ((AutoResetEvent)e.UserState).Set();
}

Возможно, вы захотите выполнить несколько пингов, а затем рассчитать среднее значение, в зависимости от ваших требований.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...