C # сокет ненормальная задержка - PullRequest
0 голосов
/ 08 января 2009

Я работаю над небольшой многопользовательской онлайн-игрой в понг с C # и XNA.

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

Проблема со скоростью: передача медленная.

Когда я проверяю второй компьютер, он показывает задержку 2 мс. Я установил небольшой таймер внутри своего кода, и он показывает задержку около 200 мс. Даже если сервер и клиент находятся на одном компьютере (с использованием 127.0.0.1), задержка по-прежнему составляет около 15 мс. Я считаю это медленным.

Вот фрагмент моего кода:

server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(new IPEndPoint(IPAddress.Any, port));
server.Listen(1);
// Begin Accept
server.BeginAccept(new AsyncCallback(ClientAccepted), null);

в ClientAccepted я настроил NetworkStream, StreamReader и StreamWriter. Вот как я отправляю сообщение, когда хочу обновить местоположение игрока:

string message = "P" + "\n" + player.Position + "\n";
byte[] data = Encoding.ASCII.GetBytes(message);
ns.BeginWrite(data, 0, data.Length, new AsyncCallback(EndUpdate), null);

Единственное, что делает EndUpdate, это вызывает EndWrite. Вот как я получаю данные:

message = sr.ReadLine();

Это не блокирует мою игру, поскольку она находится во втором потоке.

Это то, что я пробовал: - Используйте IP вместо TCP - Используйте двоичное сообщение вместо текста - Используйте IPv6 вместо IPv4 Ничто не помогло.

Есть мысли о том, как я могу улучшить время ожидания?

Спасибо

Ответы [ 4 ]

9 голосов
/ 08 января 2009

Задержка, которую вы видите, наиболее вероятна из-за алгоритма Nagle , который используется для повышения эффективности при отправке большого количества небольших пакетов с использованием TCP. По сути, стек TCP собирает маленькие пакеты вместе и объединяет их в больший пакет перед передачей. Это, очевидно, означает задержку на небольшой интервал - до 200 мс - чтобы увидеть, отправляются ли еще какие-либо данные, перед отправкой того, что ожидает в выходных буферах.

Попробуйте отключить Nagle, установив для свойства NoDelay значение true в сокете.

Однако имейте в виду, что если вы выполняете МНОЖЕСТВО небольших записей в сокет, вы можете потерять производительность из-за накладных расходов заголовка TCP и обработки каждого пакета. В этом случае вы можете попытаться собрать записи в пакеты, если это возможно.

2 голосов
/ 08 января 2009

Есть несколько других мета-проблем, которые следует учитывать:

  1. Точность таймера: многие системные таймеры обновляются только каждые 15 мс или около того. Я не могу вспомнить точное значение, но если ваши значения времени всегда кратны примерно 15 мс, будьте подозрительны, что ваш таймер не обладает высокой точностью. Это, вероятно, не ваша проблема, но имейте это в виду.

  2. Если вы тестируете на одном и том же компьютере и используете только одноядерный компьютер, частота переключений между потоками и процессами будет преобладать во время пинга. Не так много, но попробуйте добавить вызовы Sleep (0), чтобы разрешить перестановку потоков / процессов после отправки данных.

  3. Настройки передачи по TCP / IP. Как упоминалось ранее в SocketOptionName.NoDelay. Также, как упоминалось ранее, рассмотрите UDP, если вы постоянно обновляете состояние. Порядок не гарантируется, но для изменчивых данных пропущенные пакеты являются приемлемыми, так как в любом случае состояние будет вскоре переопределено. Чтобы избежать использования устаревших пакетов, добавьте в пакет возрастающее значение и никогда не используйте пакет, помеченный ранее текущего состояния. Разница между tcp и udp не должна составлять 200 мс, но комбинация других факторов может.

  4. Опрос и выбор. Если вы опрашиваете полученные данные, частота опроса, естественно, будет влиять на скорость приема. Я использую Socket.Select в выделенном сетевом потоке, чтобы ждать поступающие данные и обрабатывать их как можно скорее.

2 голосов
/ 08 января 2009

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

В TCP существует ряд взаимодействий между хостом / сервером, чтобы гарантировать данные упорядоченным образом (еще одна вещь, которую вы должны учитывать при использовании UDP, тот факт, что данные не упорядочены).

Кроме того, таймер в вашем коде должен ждать, пока байты не всплывают из уровня неуправляемых сокетов через неуправляемый код и т. Д., И т. Д. Там будут некоторые издержки, которых вы не будете способен преодолеть (неуправляемый к управляемому переходу).

0 голосов
/ 08 января 2009

Вы сказали, что пробовали «IP», и вы, вероятно, сделали это, указав ProtocolType.Ip, но я не знаю, что это на самом деле означает (т.е. я не знаю, какой протокол выбран «под капотом»), когда Вы комбинируете это с SocketType.Stream.

Кроме того, что сказал casperOne, если вы используете TCP, посмотрите, имеет ли значение настройка SocketOptionName.NoDelay для вас.

...