如何调试UDP多播请求/答复中的延迟?

时间:2018-09-15 12:40:55

标签: python udp esp8266 multicast arduino-esp8266

我正在PC基站和十二个机器人之间建立实时无线链接,每个机器人都以60Hz的频率传输。

为了进行测试,我编写了一个Python脚本,该脚本通过WiFi网络发送UDP多播数据包。然后在另一个多播组上,我有两个ESP8266发送回复。一个以60Hz进行发送以模拟实际设置,另一个以10倍的速率发送垃圾邮件以模拟其他设备的拥塞。

首先,我只查看了数据包丢失的情况,这与预期的差不多。但是后来我开始考虑往返时间。我让我的Python脚本编写当前时间,并使“真实”节点回显它们。 (垃圾邮件消息将被忽略)

关闭垃圾邮件发送者节点后,我会往返10毫秒,而使用垃圾邮件发送者,它始于100毫秒,然后慢慢增长到一秒钟。

如何调试此问题?

我尝试使用Wireshark,但是由于它们是单独的UDP流,其中包含自定义数据,因此我无法找出任何明智的分析方法。

我认为数据包可能卡在队列中的某个地方,因此建议使用以下命令查看一下队列长度。当然,该队列不是空的,但是如果我关闭垃圾邮件发送者节点,则队列长度没有太大变化,而往返传输回落到10ms。

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编写整个内容。每个数据包几乎有2ms的时间,几乎什么都不做。

基站代码:

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();
  }
}

0 个答案:

没有答案