通过 Wifi 进行的简单 TCP 通信太慢(延迟秒)?
Posted
技术标签:
【中文标题】通过 Wifi 进行的简单 TCP 通信太慢(延迟秒)?【英文标题】:Simple TCP communication over Wifi too slow (seconds lag)? 【发布时间】:2013-06-09 19:36:39 【问题描述】:我正在尝试通过 Arduino 无线控制机器人(使用计算机上的 X360 控制器),这需要非常低的延迟。出于这个原因我选择了 Wifi(事实上我将流式传输视频),经过一些测试,结果发现我在使用 TCP 时有很大的延迟。这正常吗(54Mbits/s,不应该!)?我怎样才能减少它是可控的?
服务器代码(Arduino 草图):
#include <SPI.h>
#include <Ethernet.h>
byte mac[] = 0x90, 0xA2, 0xDA, 0x0D, 0x48, 0x0D ;
byte ip[] = 192, 168, 0, 11 ;
byte gateway[] = 192, 168, 0, 254 ;
byte subnet[] = 255, 255, 255, 0 ;
byte localPort = 99;
EthernetServer server = EthernetServer(localPort);
void setup()
// initialize the ethernet device
Ethernet.begin(mac, ip, gateway, subnet);
Serial.begin(9600);
// start listening for clients
server.begin();
Serial.println("Server ready");
void loop()
// if an incoming client connects, there will be bytes available to read:
EthernetClient client = server.available();
if (client == true)
Serial.println("Received:");
byte received = 0;
while((received = client.read()) != -1)
Serial.println(received);
server.write(received);
Serial.println("Over\n");
客户端代码(PC、QtCreator):
#include <QTextStream>
#include <QTCPSocket>
QString arduinoIP = "192.168.0.11";
char arduinoPort = 99;
int main(void)
QTcpSocket socket;
QTextStream in(stdin);
QTextStream out(stdout);
out << "Connection... "; out.flush();
socket.connectToHost(arduinoIP, arduinoPort);
if(!socket.waitForConnected(5000))
out << socket.errorString() << "\n";
else
out << "OK\n"; out.flush(); //I don't know why \n doesn't flush
out << "Type a message to send to the Arduino or quit to exit\n"; out.flush();
QString command;
in >> command;
while(command != "quit")
QByteArray bufOut = command.toUtf8();
socket.write(bufOut);
socket.waitForReadyRead(1000); //Wait for answer (temp)
out << "Answer: " << socket.readAll() << "\n";
return 0;
提前感谢您的帮助。
问候, 神秘先生
【问题讨论】:
我认为发布它也是一个好主意,因为在网络上绝对没有(据我所知,经过数小时的搜索),我们可以找到一个简单的应用程序(服务器和客户端)通过以太网/wifi 在 PC 和 arduino 之间进行通信,而不使用 HTTP。通常这个评论应该足以被谷歌索引并帮助那些正在寻找这个的人。考虑到这一点,请随时帮助我改进此代码。 【参考方案1】:TCP 连接需要更多的数据包来提供可靠的数据传输。 TCP 不意味着低延迟,它意味着可靠地传输流数据。例如,如果您要发送文件,则需要接收所有数据包并按正确的顺序拼凑在一起。
您会看到带宽和延迟不相关的事实。大多数流视频系统预先缓冲数据以提供传输流中没有中断的假象。潜在的行为是传输延迟可能会抖动,但缓冲的数据会保持连续流的感知。
对于您的应用程序,请考虑使用 UDP。
UDP on Arduino
TCP 是流,UDP 用于小消息。您的决定将围绕以下问题:
如果一个数据包被发送但从未收到,会有什么影响?
在控制器输入的情况下,最好直接忽略丢失的数据并接收下一次传输。我从您的问题推测您将重复发送控制器的状态(从左到右?)如果是这样,UDP 是您的选择。如果您选择 TCP,则行为将是在发送下一个数据之前不断重试以恢复丢失的数据。在您的用例中,您不妨发送下一个控制器状态,而不是尝试恢复流。
【讨论】:
非常好的答案,非常感谢。我曾考虑过 UDP,因为我听说它更快,但我更喜欢 TCP,因为我对丢失数据感到不安(它迫使我发送所有状态,而不仅仅是按钮命令),我认为吞吐量会是足够的安全协议。我将切换到 UDP 并让您知道。 - 我应该将主题标记为已解决并为 UDP 重新做一个以防万一它出错还是让这个主题保持打开状态? 像魅力一样工作。谢谢,这是一个巨大的差异!【参考方案2】:你检查过那些相当笼统的评论吗?
1) 您是否检查过您的 Wi-Fi 频谱。重叠的通道会导致丢包。这些数据包将在一个小的额外延迟下重新传输。一个很好的帮助工具:http://www.metageek.net/products/inssider/
2) 您的 Arduino 是否没有被网络上其他设备的广播数据包淹没。也许您的网络堆栈正忙于检查和忽略广播数据包。 TCP 连接的数据包可以被丢弃,也可以重新传输。为您的 PC 和 Arduino 尝试一个私有 AP 并查看性能。
【讨论】:
感谢您的回答。计算机和 arduino+ethernet shield 都单独连接到处于 AP 模式的 TP-LINK nano。我明天会检查频谱,可能需要相应地更改频道,但它真的可以为这样的延迟负责吗? 我正在研究 Miracast 解决方案。该标准通过 Wi-Fi 发送视频。在非常糟糕的 Wi-Fi 环境中,延迟可能会比正常情况长 400 毫秒(不是秒!!)。我们也使用 UDP 来发送视频数据。请参阅下一个命令。您还可以分享您的 Arduino 上的处理器吗?几年前,我在一个基本的微控制器上使用 lwip。如果 lwip 的网络堆栈接收到大量流量,则微控制器的速度非常慢。这就是我要求您使用专用网络的原因。 这是一个 Arduino mega 2560。就像 jdr5ca 说的,延迟可能来自预缓冲,我会考虑改为 UDP 通信并回复您。【参考方案3】:UDP 真的更快,我用这个测试代码代替:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
#include <QVBoxLayout>
#include <QSpinBox>
#include <QtNetwork/QUdpSocket>
class MainWindow : public QMainWindow
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void onTimer();
private:
QTimer timer;
QVBoxLayout layout;
QWidget centralW;
QSpinBox sendBox;
QSpinBox receiveBox;
QUdpSocket *socket;
;
#endif // MAINWINDOW_H
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
connect(&timer, SIGNAL(timeout()), this, SLOT(onTimer()));
timer.start(100);
sendBox.setMaximum(1000);
layout.addWidget(&sendBox);
receiveBox.setMaximum(1000);
layout.addWidget(&receiveBox);
centralW.setLayout(&layout);
setCentralWidget(¢ralW);
socket = new QUdpSocket(this);
MainWindow::~MainWindow()
void MainWindow::onTimer()
QByteArray datagram = QByteArray::number(sendBox.value());
socket->writeDatagram(datagram.data(), datagram.size(), QHostAddress("192.168.0.11"), 99);
if(socket->hasPendingDatagrams())
datagram.resize(socket->pendingDatagramSize());
socket->readDatagram(datagram.data(), datagram.size());
receiveBox.setValue(QString(datagram.data()).toInt());
在服务器端:
#include <SPI.h> // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <EthernetUdp.h> // UDP library from: bjoern@cs.stanford.edu 12/30/2008
byte mac[] = 0x90, 0xA2, 0xDA, 0x0D, 0x48, 0x0D ;
byte ip[] = 192, 168, 0, 11 ;
byte gateway[] = 192, 168, 0, 254 ;
byte subnet[] = 255, 255, 255, 0 ;
byte localPort = 99;
EthernetUDP Udp;
void setup()
Ethernet.begin(mac,ip);
Udp.begin(localPort);
Serial.begin(9600);
void loop()
// if there's data available, read a packet
int packetSize = Udp.parsePacket();
if(packetSize)
static char buffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,
//Udp.read does not erase the rest of the buffer, without that we would
//get 989 instead of 98 after having entered 999 for example
for(int i = 0; i < UDP_TX_PACKET_MAX_SIZE; ++i) buffer[i] = 0;
Udp.read(buffer,UDP_TX_PACKET_MAX_SIZE);
Serial.println(buffer);
// send a reply, to the IP address and port that sent us the packet we received
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(buffer);
Udp.endPacket();
【讨论】:
以上是关于通过 Wifi 进行的简单 TCP 通信太慢(延迟秒)?的主要内容,如果未能解决你的问题,请参考以下文章
WIFI模块开发教程之W600网络篇2:AP模式下TCP Server通信
如何使 AT 命令在 arduino 中为 ESP8266 wifi 模块以编程方式工作