Как улучшить синхронизацию данных с помощью Unity через многоадресный UDP-сокет - PullRequest
1 голос
/ 10 июля 2020

Я учусь некоторым простым сетевым технологиям с использованием Unity и Sockets, и у меня возникают проблемы с синхронизацией данных между клиентом и сервером. Я знаю, что есть и другие варианты использования Unity Networking, но, прежде чем двигаться дальше, я хочу лучше понять, как улучшить свой код с помощью системных библиотек.

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

Я мой сервер настройки отправляет значения со скоростью 60 кадров в секунду, и клиент читает с той же скоростью. У меня проблема в том, что когда клиент получает значения, он обычно получает сразу несколько значений. Если я регистрирую полученные значения с помощью ----- между каждым кадром, я обычно получаю следующий результат:

------
------
------
------
------
------
------
119,396
91,396
45,391
18,379
-8,362
-35,342
-59,314
------
------
------
------
------
------
------

Я ожидал бы, что несинхронизированные циклы обновления приведут к получению двух значений на кадр, но я не уверен, чем может объясняться большее несоответствие.

Вот код сервера:

using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;

public class Server : MonoBehaviour
{
    Socket _socket;

    void OnEnable ()
    {
        var ip = IPAddress.Parse ("224.5.6.7");
        var ipEndPoint = new IPEndPoint(ip, 4567);

        _socket = new Socket (AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        _socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption (ip));
        _socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 2);
        _socket.Connect(ipEndPoint);
    }

    void OnDisable ()
    {
        if (_socket != null)
        {
            _socket.Close();
            _socket = null;
        }
    }

    public void Send (string message)
    {
        var byteArray = Encoding.ASCII.GetBytes (message);
        _socket.Send (byteArray);
    }
}

И клиент:

using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;

public class Client : MonoBehaviour
{
    Socket _socket;
    byte[] _byteBuffer = new byte[16];

    public delegate void MessageRecievedEvent (string message);
    public MessageRecievedEvent messageWasRecieved = delegate {};
    

    void OnEnable ()
    {
        var ipEndPoint = new IPEndPoint(IPAddress.Any, 4567);
        var ip = IPAddress.Parse("224.5.6.7");

        _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        _socket.Bind (ipEndPoint);
        _socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(ip,IPAddress.Any));
    }

    void Update ()
    {
        while (_socket.Available > 0)
        {
            for (int i = 0; i < _byteBuffer.Length; i++) _byteBuffer[i] = 0;
            _socket.Receive (_byteBuffer);
            messageWasRecieved (Encoding.ASCII.GetString (_byteBuffer));
        }
    }
}

Если кто-нибудь может пролить свет на то, что я могу сделать для улучшения синхронизации, будет большим подспорьем.

1 Ответ

1 голос
/ 10 июля 2020

Сетевой ввод-вывод подвержен большому количеству внешних воздействий, а TCP / IP как протокол имеет несколько требований. Конечно, ни один из них не обеспечил бы гарантию того поведения, которое вам кажется желаемым.

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

Но если мы предположим, что сам код идеален, при использовании UDP по-прежнему нет гарантий, что датаграммы не будут объединены в пакет в какой-то момент вдоль таким образом, что большое количество сразу появляется в сетевом буфере. Я бы ожидал, что это произойдет с большей частотой, когда дейтаграммы отправляются через несколько сетевых узлов (например, коммутатор и особенно через Inte rnet), но это может так же легко произойти, когда сервер и клиент находятся на одном компьютере. .

По иронии судьбы, один из вариантов, который может заставить дейтаграммы расширяться, - это заполнение каждой дейтаграммы дополнительными байтами. Точное количество требуемых байтов будет зависеть от точного сетевого маршрута; чтобы сделать это "идеально", может потребоваться запись некоторого журнала калибровки c, который пробует разные значения заполнения, пока код не увидит дейтаграммы, прибывающие с ожидаемыми интервалами. O, и все же не гарантирует желаемого поведения. И у этого есть некоторые очевидные отрицательные побочные эффекты, в том числе дополнительные накладные расходы в сети (что люди, использующие лимитированные сетевые соединения, безусловно, не оценят), а также увеличение вероятности полного отбрасывания дейтаграммы UDP. * Из вашего вопроса неясно, действительно ли ваш проект требует многоадресного UDP или это просто что-то в вашем коде, потому что это то, что вы использовали в каком-то учебном пособии или другом примере. Если многоадресная рассылка на самом деле не является требованием, вам обязательно стоит попробовать использовать прямой UDP без многоадресной рассылки.

FWIW: Я бы не реализовал это так, как вы. Вместо этого я бы использовал асинхронные операции приема, чтобы мой клиент получал дейтаграммы сразу же, как только они становятся доступны, а не только периодически проверять каждый кадр рендеринга. Я бы также включил порядковый номер в дейтаграмму и отбросил (проигнорировал) любые дейтаграммы, которые поступают не по порядку (то есть, где порядковый номер не строго больше, чем самый последний уже полученный порядковый номер). Такой подход улучшит (возможно, незначительно) отзывчивость, но также будет обрабатывать ситуации, когда дейтаграммы поступают не по порядку или дублируются (две из трех основных проблем доставки, с которыми одна будет сталкиваться с UDP… третья, конечно же, отказ доставка).

...