Как я могу отладить задержку в многоадресном запросе / ответе UDP? - PullRequest
0 голосов
/ 15 сентября 2018

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

Для тестирования я написал скрипт Python, который отправляет многоадресные UDP-пакеты по моей сети WiFi,И затем в другой многоадресной группе у меня есть два ESP8266, отправляющих ответы.Один посылает сигнал с частотой 60 Гц, чтобы имитировать реальную настройку, а другой - в 10 раз быстрее, чтобы имитировать перегрузку с других устройств.

Сначала я посмотрел только на потерю пакетов, что было довольно ожидаемым.Но потом я начал смотреть в оба конца.Я заставил свой скрипт Python записать текущее время, и «реальный» узел вернул их обратно.(сообщения о спамере игнорируются)

При отключенном узле спаммера я получаю круговую передачу в 10 мс, но при спамере он начинается со 100 мс и медленно увеличивается до секунды.

Как я могу отладить эту проблему?

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

Я подумал, что пакеты, вероятно, где-то застряли в очереди, поэтому некоторые поиски в Google предложили посмотреть длину очереди с помощью следующей команды.Конечно, очередь не пуста, но если я отключу спамерский узел, длина очереди не изменится, а обратная передача сократится до 10 мс.

netstat -c --udp -an
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
udp    40256      0 0.0.0.0:8080            0.0.0.0:*   

Я подумал, что мой код Pythonслишком медленно обрабатывать 600 сообщений в секунду.Работа на PyPy не имела абсолютно никакого значения, поэтому я немного колеблюсь, чтобы написать все это на C. Он имеет почти 2 мс на пакет, чтобы почти ничего не делать.

Код базовой станции:

import socket
import struct
import sched, time
import threading

base_multicast = '239.255.0.34'
robot_multicast = '239.255.0.35'
port = 8080


sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', port))

mreq = struct.pack("4sl", socket.inet_aton(robot_multicast), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

# schedule base station packets

send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
send_sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)

rate = 1/60
#sock.settimeout(rate)

s = sched.scheduler(time.time, time.sleep)

def send(now):
    nxt = now+rate
    s.enterabs(nxt, 1, send, argument=(nxt,))

    tstr = struct.pack("d", time.time())
    send_sock.sendto(tstr, (base_multicast, port))
    print(".", end='')


s.enter(1, 1, send, argument=(time.time(),))

t = threading.Thread(target=s.run)
t.daemon = True
t.start()

start = time.time()
avg_dur = 0
avg_ping = 0
while True:
  try:
      data = sock.recv(10240)
  except socket.timeout:
      continue
  now = time.time()
  duration = now-start
  start = now
  avg_dur = 0.99*avg_dur + 0.01*duration

  try:
      (t,) = struct.unpack("d", data)
      ping = now-t
      avg_ping = 0.99*avg_ping + 0.01*ping
      print(len(data), 1/avg_dur, avg_ping)
  except struct.error: # spammer message
      pass

Узел робота: (спаммер идентичен, за исключением того, что он отправляет replyPacket 10 раз в цикле)

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

const char* ssid = "mywifi";
const char* password = "password";

IPAddress base_multicast(239, 255, 0, 34);
IPAddress robot_multicast(239, 255, 0, 35);

WiFiUDP Udp;
unsigned int port = 8080;  // local port to listen on
char incomingPacket[255];  // buffer for incoming packets
char  replyPacket[] = "Hi there! Got the message :-))))";  // a reply string to send back

int rate = 1000/60;
unsigned long mytime;
unsigned long count;

float avg_diff = 0;


void setup()
{
  Serial.begin(115200);
  Serial.println();

  Serial.printf("Connecting to %s ", ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println(" connected");

  //Udp.begin(localUdpPort);
  Udp.beginMulticast(WiFi.localIP(), base_multicast, port);
  Serial.printf("Now listening at IP %s, UDP port %d\n", WiFi.localIP().toString().c_str(), port);

  mytime = millis();
}

void loop()
{
  int packetSize = Udp.parsePacket();
  if (packetSize)
  {
    // receive incoming UDP packets
    unsigned long diff = micros() - mytime;
    avg_diff = 0.9*avg_diff + 0.1*diff;
    mytime = micros();
    int rcv_rate = 1000000/avg_diff;
    Serial.println(rcv_rate);
    int len = Udp.read(incomingPacket, 255);
    delay(random(rate));
    //Serial.printf("UDP packet contents: %s\n", incomingPacket);
    Udp.beginPacketMulticast(robot_multicast, port, WiFi.localIP());
    Udp.write(incomingPacket, len);
    Udp.endPacket();
  }
}
...