目录
UDP是无连接、不可靠、面向数据报(datagram)的协议,可以应用于对可靠性要求不高的场合。与TCP通信不同,UDP通信无需预先建立持久的socket连接,UDP每次发送数据报都需要指定目标地址和端口。
QUdpSocket以数据报传输数据,而不是以连续的数据流。发送数据报使用函数QUdpSocket::writeDatagram(),数据报的长度一般少于512字节,每个数据报包含发送者和接收者的IP地址和端口等信息。
UDP数据接收,首先要使用QUdpSocket::bind()绑定一个端口,绑定端口后,socket的状态会变为已绑定状态“BoundState”。当有数据报传入时,QudpSocket会自动发射readyRead()信号,在其槽函数中使用QUdpSocket::readDatagram()进行数据读取。abort()为解除绑定,解除后socket状态变为未连接状态“UnconnectedState”。
单播模式(unicast):一个UDP客户端发送数据报到指定地址和端口的另一UDP客户端,是一对一的数据传输。
广播模式(broadcast):一个UDP客户端发出的数据报,在同一网络范围内其他所有的UDP客户端都可以收到。QUdpSocket支持IPv4广播。需要在数据报中指定接收端地址为QHostAddress::Broadcast,一般的广播地址是255.255.255.255。
组播模式(multicast):UDP客户端加入到另一个组播IP地址的多播组,成员向组播地址发送的数据报,其加入组播的所有成员都可以接收到,类似于QQ群功能。QUdpSocket::joinMulticastGroup()函数实现加入多播组的功能。
在单播、广播和多播模式下,UDP程序都是对等的,不像TCP通信分为客户端和服务端。
TCP通信只有单播模式。UDP通信虽然不能保证数据传输的准确性,但是具有灵活性,一般的即时通信软件都是基于UDP通信的。
bool bind(quint16 port = 0) 为UDP通信绑定一个端口
qint64 writeDatagram(QByteArray& datagram, QHostAddress& host, quint16 port) 向目标地址和端口的UDP客户端发送数据报,返回成功发送的字节数,数据报的长度一般不超过512字节。
bool hasPendingDatagrams() 当至少有一个数据报需要读取时,返回true
qint64 pendingDatagramSize() 返回第一个待读取的数据报的大小
qint64 readDatagram(char* data, qint64 maxSize) 读取一个数据报,返回成功读取的字节数
qint64 readDatagram(char* data, qint64 maxSize, QHostAddress* address, quint16* port) 读取一个数据报,返回成功读取的字节数。发送方的主机地址和端口存储在*address和*port中(除非指针为0)
bool joinMulticastGroup(QHostAddress& groupAddress) 加入一个多播组
bool leaveMulticastGroup(QHostAddress& groupAddress) 离开一个多播组
void abort() 终止当前连接并重置套接字。通常在析构函数中写入。与disconnectFromHost()不同,该函数立即关闭套接字,丢弃写入缓冲区中的任何挂起数据。
本实例实现UDP通信的单播和广播。两个实例可以运行在同一台计算机上,也可以运行在不同的计算机上。
这里的两个实例是运行在同一台计算机上,需要注意,在同一台计算机上运行时,两个实例需要绑定不同的端口。例如实例A绑定端口1600,实例B绑定端口3200,实例A向实例B发送数据报时,需要指定实例B的端口,这样实例B才能收到数据。
如果两个实例在不同的计算机上运行,则端口可以一样,因为IP地址不同了,不会导致绑定时发生冲突。一般的UDP通信程序都是在不同的计算机上运行的,约定一个固定的端口作为通信端口。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QAction>
#include <QComboBox>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QHostInfo>
#include <QLabel>
#include <QLineEdit>
#include <QMainWindow>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QSpinBox>
#include <QUdpSocket>
#include <QVBoxLayout>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = 0);
~MainWindow();
private slots:
void slotActBindPort();
void slotActUnbindPort();
void slotActClearText();
void slotActQuit();
void slotSocketStateChanged(QAbstractSocket::SocketState socketState);
void slotBtnSend();
void slotBtnBroad();
void slotSocketReadyRead(); //读取socket传入的数据
private:
Ui::MainWindow* ui;
QAction* m_pActBindPort;
QAction* m_pActUnbindPort;
QAction* m_pActClearText;
QAction* m_pActQuit;
QWidget* m_pCentralWidget;
QLabel* m_pLabBindPort;
QLabel* m_PLabTargetAddr;
QLabel* m_pLabTargetPort;
QSpinBox* m_pSpinBindPort;
QComboBox* m_pComboTargetAddr;
QSpinBox* m_pSpinTargetPort;
QLineEdit* m_pLineEdit;
QPushButton* m_pBtnSend;
QPushButton* m_pBtnBroad;
QPlainTextEdit* m_pPlainText;
QLabel* m_pLabState;
QUdpSocket* m_pUdpSocket;
QString getLocalIP();
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
this->setWindowIcon(QIcon(":/new/prefix1/res/TitleIcon.png"));
this->setWindowTitle(QStringLiteral("UDP Send/Receiver"));
//工具栏
ui->mainToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
m_pActBindPort = new QAction(QIcon(":/new/prefix1/res/绑定端口.png"), QStringLiteral("绑定端口"), this);
m_pActUnbindPort = new QAction(QIcon(":/new/prefix1/res/解除绑定.png"), QStringLiteral("结束绑定"), this);
m_pActClearText = new QAction(QIcon(":/new/prefix1/res/清空文本.png"), QStringLiteral("清空文本"), this);
m_pActQuit = new QAction(QIcon(":/new/prefix1/res/退出.png"), QStringLiteral("退出"), this);
ui->mainToolBar->addAction(m_pActBindPort);
ui->mainToolBar->addAction(m_pActUnbindPort);
ui->mainToolBar->addSeparator();
ui->mainToolBar->addAction(m_pActClearText);
ui->mainToolBar->addSeparator();
ui->mainToolBar->addAction(m_pActQuit);
//界面布局
m_pCentralWidget = new QWidget(this);
QHBoxLayout* HLay1 = new QHBoxLayout;
m_pLabBindPort = new QLabel(QStringLiteral("绑定端口"), m_pCentralWidget);
m_pSpinBindPort = new QSpinBox(m_pCentralWidget);
m_pSpinBindPort->setMinimum(1);
m_pSpinBindPort->setMaximum(65535);
m_pSpinBindPort->setValue(1600);
m_PLabTargetAddr = new QLabel(QStringLiteral("目标地址"), m_pCentralWidget);
m_pComboTargetAddr = new QComboBox(m_pCentralWidget);
m_pLabTargetPort = new QLabel(QStringLiteral("目标端口"), m_pCentralWidget);
m_pSpinTargetPort = new QSpinBox(m_pCentralWidget);
m_pSpinTargetPort->setMinimum(1);
m_pSpinTargetPort->setMaximum(65535);
m_pSpinTargetPort->setValue(3200);
HLay1->addWidget(m_pLabBindPort, 1, Qt::AlignRight);
HLay1->addWidget(m_pSpinBindPort, 2);
HLay1->addWidget(m_PLabTargetAddr, 1, Qt::AlignRight);
HLay1->addWidget(m_pComboTargetAddr, 4);
HLay1->addWidget(m_pLabTargetPort, 1, Qt::AlignRight);
HLay1->addWidget(m_pSpinTargetPort, 2);
QHBoxLayout* HLay2 = new QHBoxLayout;
m_pLineEdit = new QLineEdit(m_pCentralWidget);
m_pBtnSend = new QPushButton(QStringLiteral("发送消息"), m_pCentralWidget);
m_pBtnBroad = new QPushButton(QStringLiteral("广播消息"), m_pCentralWidget);
HLay2->addWidget(m_pLineEdit);
HLay2->addWidget(m_pBtnSend);
HLay2->addWidget(m_pBtnBroad);
QVBoxLayout* VLay = new QVBoxLayout(m_pCentralWidget); //主布局必须设置parent,否则不会显示布局
// QVBoxLayout* VLay = new QVBoxLayout();
VLay->addLayout(HLay1);
VLay->addLayout(HLay2);
m_pPlainText = new QPlainTextEdit(m_pCentralWidget);
VLay->addWidget(m_pPlainText);
this->setCentralWidget(m_pCentralWidget);
this->setLayout(VLay); //设置为窗体的主布。在指定了主布局的parent之后,这句话可有可无
QString localIP = getLocalIP();
this->setWindowTitle(this->windowTitle() + "---IP:" + localIP);
m_pComboTargetAddr->addItem(localIP);
m_pUdpSocket = new QUdpSocket(this);
//状态栏
m_pLabState = new QLabel(QStringLiteral("socket状态:"), this);
m_pLabState->setMinimumWidth(150);
ui->statusBar->addWidget(m_pLabState);
// connect
connect(m_pActBindPort, &QAction::triggered, this, &MainWindow::slotActBindPort);
connect(m_pActUnbindPort, &QAction::triggered, this, &MainWindow::slotActUnbindPort);
connect(m_pActClearText, &QAction::triggered, this, &MainWindow::slotActClearText);
connect(m_pActQuit, &QAction::triggered, this, &MainWindow::slotActQuit);
connect(m_pBtnSend, &QPushButton::clicked, this, &MainWindow::slotBtnSend);
connect(m_pBtnBroad, &QPushButton::clicked, this, &MainWindow::slotBtnBroad);
connect(m_pUdpSocket, &QUdpSocket::stateChanged, this, &MainWindow::slotSocketStateChanged);
connect(m_pUdpSocket, &QUdpSocket::readyRead, this, &MainWindow::slotSocketReadyRead);
}
MainWindow::~MainWindow() {
m_pUdpSocket->abort();
delete m_pUdpSocket;
m_pUdpSocket = nullptr;
delete ui;
}
void MainWindow::slotActBindPort() {
quint16 port = m_pSpinBindPort->value(); //本机UDP端口
if (m_pUdpSocket->bind(port)) {
m_pPlainText->appendPlainText("**已成功绑定");
m_pPlainText->appendPlainText("绑定端口:" + QString::number(m_pUdpSocket->localPort()));
//使能
m_pActBindPort->setEnabled(false);
m_pActUnbindPort->setEnabled(true);
} else {
m_pPlainText->appendPlainText("绑定失败");
}
}
void MainWindow::slotActUnbindPort() {
m_pUdpSocket->abort(); //解除绑定
m_pPlainText->appendPlainText("**已解除绑定");
m_pActBindPort->setEnabled(true);
m_pActUnbindPort->setEnabled(false);
}
void MainWindow::slotActClearText() { m_pPlainText->clear(); }
void MainWindow::slotActQuit() {
QMessageBox::StandardButton button = QMessageBox::question(this, "", "是否要退出?");
if (button == QMessageBox::StandardButton::Yes)
this->close();
}
void MainWindow::slotSocketStateChanged(QAbstractSocket::SocketState socketState) {
switch (socketState) {
case QAbstractSocket::UnconnectedState: m_pLabState->setText("socket状态:UnconnectedState"); break;
case QAbstractSocket::HostLookupState: m_pLabState->setText("socket状态:HostLookupState"); break;
case QAbstractSocket::ConnectingState: m_pLabState->setText("socket状态:ConnectingState"); break;
case QAbstractSocket::ConnectedState: m_pLabState->setText("socket状态:ConnectedState"); break;
case QAbstractSocket::BoundState: m_pLabState->setText("socket状态:BoundState"); break;
case QAbstractSocket::ClosingState: m_pLabState->setText("socket状态:ClosingState"); break;
default: break;
}
}
void MainWindow::slotBtnSend() {
QString msg = m_pLineEdit->text();
QByteArray str = msg.toUtf8();
QString targetIp = m_pComboTargetAddr->currentText(); //目标IP
QHostAddress targetAddr(targetIp);
quint16 targetPort = m_pSpinTargetPort->value(); //目标端口
m_pUdpSocket->writeDatagram(str, targetAddr, targetPort);
m_pPlainText->appendPlainText("[out] " + msg);
m_pLineEdit->clear();
m_pLineEdit->setFocus();
}
void MainWindow::slotBtnBroad() {
QString msg = m_pLineEdit->text();
QByteArray str = msg.toUtf8();
quint16 targetPort = m_pSpinTargetPort->value();
m_pUdpSocket->writeDatagram(str, QHostAddress::Broadcast, targetPort);
m_pPlainText->appendPlainText("[out] " + msg);
m_pLineEdit->clear();
m_pLineEdit->setFocus();
}
void MainWindow::slotSocketReadyRead() {
while (m_pUdpSocket->hasPendingDatagrams()) {
QByteArray dataGram;
dataGram.resize(m_pUdpSocket->pendingDatagramSize());
QHostAddress peerAddress;
quint16 peerPort;
m_pUdpSocket->readDatagram(dataGram.data(), dataGram.size(), &peerAddress, &peerPort);
QString str = dataGram.data();
QString peer = "[From " + peerAddress.toString() + ":" + QString::number(peerPort) + "]";
m_pPlainText->appendPlainText(peer + str);
}
}
QString MainWindow::getLocalIP() {
QString hostName = QHostInfo::localHostName();
QHostInfo hostInfo = QHostInfo::fromName(hostName);
QString localIP = "";
QList<QHostAddress> addrList = hostInfo.addresses();
if (!addrList.isEmpty()) {
for (int i = 0; i < addrList.size(); i++) {
QHostAddress addr = addrList.at(i);
if (QAbstractSocket::IPv4Protocol == addr.protocol()) {
localIP = addr.toString();
break;
}
}
}
return localIP;
}

组播报文的目的地址使用D类IP地址,关于组播IP地址,有以下约定:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QAction>
#include <QComboBox>
#include <QHBoxLayout>
#include <QHostInfo>
#include <QLabel>
#include <QLineEdit>
#include <QMainWindow>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QRegExp>
#include <QSpinBox>
#include <QUdpSocket>
#include <QVBoxLayout>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = 0);
~MainWindow();
private slots:
void slotActJoinMulti();
void slotActLeaveMulti();
void slotActClearText();
void slotActQuit();
void slotSocketStateChanged(QAbstractSocket::SocketState socketState);
void slotBtnMultiMsg();
void slotReadyRead();
private:
Ui::MainWindow* ui;
QAction* m_pActJoinMulti;
QAction* m_pActLeaveMulti;
QAction* m_pActClearText;
QAction* m_pActQuit;
QWidget* m_pCentralWidget;
QLabel* m_pLabPort;
QLabel* m_pLabAddr;
QSpinBox* m_pSpinPort;
QComboBox* m_pComboAddr;
QLineEdit* m_pLineEdit;
QPushButton* m_pBtnSendMulti;
QPlainTextEdit* m_pPlainText;
QLabel* m_pLabState;
QUdpSocket* m_pUdpSocket;
QHostAddress m_multicastAddr;
QString getLocalIP();
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
this->setWindowTitle(QStringLiteral("UDP Multicast"));
//工具栏
ui->mainToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
m_pActJoinMulti = new QAction(QIcon(":/new/prefix1/res/添加群组.png"), QStringLiteral("加入组播"), this);
m_pActLeaveMulti = new QAction(QIcon(":/new/prefix1/res/退出群组.png"), QStringLiteral("退出组播"), this);
m_pActClearText = new QAction(QIcon(":/new/prefix1/res/清空.png"), QStringLiteral("清空文本"), this);
m_pActQuit = new QAction(QIcon(":/new/prefix1/res/退出.png"), QStringLiteral("退出"), this);
ui->mainToolBar->addAction(m_pActJoinMulti);
ui->mainToolBar->addAction(m_pActLeaveMulti);
ui->mainToolBar->addSeparator();
ui->mainToolBar->addAction(m_pActClearText);
ui->mainToolBar->addSeparator();
ui->mainToolBar->addAction(m_pActQuit);
//界面布局
m_pCentralWidget = new QWidget(this);
m_pLabPort = new QLabel(QStringLiteral("组播端口"), m_pCentralWidget);
m_pSpinPort = new QSpinBox(m_pCentralWidget);
m_pSpinPort->setMinimum(1);
m_pSpinPort->setMaximum(65535);
m_pSpinPort->setValue(3200);
m_pLabAddr = new QLabel(QStringLiteral("组播地址"), m_pCentralWidget);
m_pComboAddr = new QComboBox(m_pCentralWidget);
m_pComboAddr->setEditable(true); //下拉框可编辑输入
m_pComboAddr->addItem("239.0.0.1");
// 正则匹配 D类IP:224.0.0.0~239.255.255.255
// .必须使用转义字符\,否则.会匹配任意字符
// C++中"\"在字符串中表示要用"\\"
// 是 - 不是 ~ ; 是[0-9]不是[0~9]
QRegExp regExp("^(22[4-9]|23[0-9])(\\.((\\d)|([1-9]\\d)|(1\\d{2})|(2[0-4]\\d)|(25[0-5]))){3}$");
QValidator* pValidator = new QRegExpValidator(regExp, this);
m_pComboAddr->setValidator(pValidator);
QHBoxLayout* HLay1 = new QHBoxLayout();
HLay1->addWidget(m_pLabPort, 1, Qt::AlignRight);
HLay1->addWidget(m_pSpinPort, 1);
HLay1->addWidget(m_pLabAddr, 1, Qt::AlignRight);
HLay1->addWidget(m_pComboAddr, 2);
m_pLineEdit = new QLineEdit(m_pCentralWidget);
m_pBtnSendMulti = new QPushButton(QStringLiteral("组播消息"), m_pCentralWidget);
QHBoxLayout* HLay2 = new QHBoxLayout();
HLay2->addWidget(m_pLineEdit, 4);
HLay2->addWidget(m_pBtnSendMulti, 1);
m_pPlainText = new QPlainTextEdit(m_pCentralWidget);
QVBoxLayout* VLay = new QVBoxLayout(m_pCentralWidget);
VLay->addLayout(HLay1);
VLay->addLayout(HLay2);
VLay->addWidget(m_pPlainText);
this->setCentralWidget(m_pCentralWidget);
this->setLayout(VLay);
//状态栏
m_pLabState = new QLabel(QStringLiteral("socket状态:"), this);
m_pLabState->setMinimumWidth(150);
ui->statusBar->addWidget(m_pLabState);
QString str = getLocalIP();
this->setWindowTitle(this->windowTitle() + "---IP:" + str);
m_pUdpSocket = new QUdpSocket(this);
m_pUdpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption, 1);
// connect
connect(m_pActJoinMulti, &QAction::triggered, this, &MainWindow::slotActJoinMulti);
connect(m_pActLeaveMulti, &QAction::triggered, this, &MainWindow::slotActLeaveMulti);
connect(m_pActClearText, &QAction::triggered, this, &MainWindow::slotActClearText);
connect(m_pActQuit, &QAction::triggered, this, &MainWindow::slotActQuit);
connect(m_pUdpSocket, &QUdpSocket::stateChanged, this, &MainWindow::slotSocketStateChanged);
connect(m_pBtnSendMulti, &QPushButton::clicked, this, &MainWindow::slotBtnMultiMsg);
connect(m_pUdpSocket, &QUdpSocket::readyRead, this, &MainWindow::slotReadyRead);
}
MainWindow::~MainWindow() { delete ui; }
void MainWindow::slotActJoinMulti() {
QString ip = m_pComboAddr->currentText();
m_multicastAddr = QHostAddress(ip);
quint16 multicastPort = m_pSpinPort->value();
if (m_pUdpSocket->bind(QHostAddress::AnyIPv4, multicastPort, QUdpSocket::ShareAddress)) {
m_pUdpSocket->joinMulticastGroup(m_multicastAddr); //加入多播组
m_pPlainText->appendPlainText("**加入组播成功");
m_pPlainText->appendPlainText("**组播地址IP:" + ip);
m_pPlainText->appendPlainText("**绑定端口:" + QString::number(multicastPort));
m_pActJoinMulti->setEnabled(false);
m_pActLeaveMulti->setEnabled(true);
m_pComboAddr->setEditable(false);
} else {
m_pPlainText->appendPlainText("**绑定端口失败");
}
}
void MainWindow::slotActLeaveMulti() {
m_pUdpSocket->leaveMulticastGroup(m_multicastAddr); //退出组播
m_pUdpSocket->abort(); //解除绑定
m_pActJoinMulti->setEnabled(true);
m_pActLeaveMulti->setEnabled(false);
m_pComboAddr->setEnabled(true);
m_pComboAddr->setEditable(true);
m_pPlainText->appendPlainText("**已退出组播,解除端口绑定");
}
void MainWindow::slotActClearText() { m_pPlainText->clear(); }
void MainWindow::slotActQuit() {
QMessageBox::StandardButton button = QMessageBox::question(this, "", "是否退出?");
if (QMessageBox::StandardButton::Yes == button) {
this->close();
}
}
void MainWindow::slotSocketStateChanged(QAbstractSocket::SocketState socketState) {
// case并不包含所有的情况,因为没有写listening的情况,所以就需要写default
switch (socketState) {
case QAbstractSocket::UnconnectedState: m_pLabState->setText("socket状态:UnconnectedState"); break;
case QAbstractSocket::HostLookupState: m_pLabState->setText("socket状态:HostLookupState"); break;
case QAbstractSocket::ConnectingState: m_pLabState->setText("socket状态:ConnectingState"); break;
case QAbstractSocket::ConnectedState: m_pLabState->setText("socket状态:ConnectedState"); break;
case QAbstractSocket::BoundState: m_pLabState->setText("socket状态:BoundState"); break;
case QAbstractSocket::ClosingState: m_pLabState->setText("socket状态:ClosingState"); break;
default: break;
}
}
void MainWindow::slotBtnMultiMsg() {
QString msg = m_pLineEdit->text();
QByteArray str = msg.toUtf8();
quint16 multiPort = m_pSpinPort->value();
m_pUdpSocket->writeDatagram(str, m_multicastAddr, multiPort);
m_pPlainText->appendPlainText("[multicast] " + msg);
m_pLineEdit->clear();
m_pLineEdit->setFocus();
}
void MainWindow::slotReadyRead() {
while (m_pUdpSocket->hasPendingDatagrams()) {
QByteArray dataGram;
dataGram.resize(m_pUdpSocket->pendingDatagramSize());
QHostAddress peerAddr;
quint16 peerPort;
m_pUdpSocket->readDatagram(dataGram.data(), dataGram.size(), &peerAddr, &peerPort);
QString str = dataGram.data();
QString peer = "[From " + peerAddr.toString() + ":" + QString::number(peerPort) + "] ";
m_pPlainText->appendPlainText(peer + str);
qDebug() << m_pUdpSocket->peerAddress();
qDebug() << m_pUdpSocket->localAddress().toString();
qDebug() << m_pUdpSocket->localPort();
}
}
QString MainWindow::getLocalIP() {
QString localName = QHostInfo::localHostName();
QHostInfo hostInfo = QHostInfo::fromName(localName);
QList<QHostAddress> addrList = hostInfo.addresses();
QString localIP = "";
if (!addrList.isEmpty()) {
for (int i = 0; i < addrList.size(); i++) {
QHostAddress addr = addrList.at(i);
if (QAbstractSocket::IPv4Protocol == addr.protocol()) {
localIP = addr.toString();
break;
}
}
}
return localIP;
}

我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
在前面两节的例子中,主界面窗口的尺寸和标签控件显示的矩形区域等,都是用C++代码编写的。窗口和控件的尺寸都是预估的,控件如果多起来,那就不好估计每个控件合适的位置和大小了。用C++代码编写图形界面的问题就是不直观,因此Qt项目开发了专门的可视化图形界面编辑器——QtDesigner(Qt设计师)。通过QtDesigner就可以很方便地创建图形界面文件*.ui,然后将ui文件应用到源代码里面,做到“所见即所得”,大大方便了图形界面的设计。本节就演示一下QtDesigner的简单使用,学习拖拽控件和设置控件属性,并将ui文件应用到Qt程序代码里。使用QtDesigner设计界面在开始菜单中找到「Q
MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO
目录一.大致如下常见问题:(1)找不到程序所依赖的Qt库version`Qt_5'notfound(requiredby(2)CouldnotLoadtheQtplatformplugin"xcb"in""eventhoughitwasfound(3)打包到在不同的linux系统下,或者打包到高版本的相同系统下,运行程序时,直接提示段错误即segmentationfault,或者Illegalinstruction(coredumped)非法指令(4)ldd应用程序或者库,查看运行所依赖的库时,直接报段错误二.问题逐个分析,得出解决方法:(1)找不到程序所依赖的Qt库version`Qt_5'
在Rails中,什么是集成更新模型某些元素的UDP监听过程的最佳方式(特别是它将向其中一个表添加行)。简单的答案似乎是在同一个进程中使用UDP套接字对象启动一个线程,但我什至不清楚我应该在哪里做适合Rails方式的事情。有没有一种巧妙的方法来开始收听UDP?具体来说,我希望能够编写一个UDPController并在每个数据报消息上调用一个特定的方法。理想情况下,我希望避免在UDP上使用HTTP(因为它会浪费一些在这种情况下非常宝贵的空间),但我完全控制消息格式,因此我可以为Rails提供它需要的任何信息。 最佳答案 Rails是一个
功能需求:主机使用一个串口,与两个从机进行双向通信,主机向从机发送数据,从机能够返回数据,由于结构限制,主机与从机之间只有3根线(电源、地、数据线),并且从机上没有设物理的电源开关,需要通过与主机连接的数据线来控制开机,总结如下:1、数据线只有1根2、能够双向通信3、主机能够控制从机开机4、主机可以单独向1个从机发数据,也可以同时向两个从机发送数据根据需求,设计出如下电路:工作原理分析:VCC_24V_IN、GND、LINE_L(LINE_R)三根线接线连接到从机,电源开启电路是从机内部的电源控制。开机的逻辑:*主机先上电,LINE_L因为主机的R1上拉而有高电平,使Q6导通,Q5的G极电压被
我有一个RaspberryPiTFT7"触摸屏显示器,我想创建一个简单的应用程序来显示和输出系统数据(即CPU使用率、温度等)。我注意到目前常见的实现方法是使用pygame库输出到显示器连接到的帧缓冲区/dev/fb1。我想执行相同的操作,但使用Ruby,因为我更熟悉这门语言。有人可以为我指明正确的方向,让我知道如何开始吗?我查看了rubygame和gosu库,它们似乎能够做我想做的事情,即绘制屏幕,但我找不到任何关于如何将输出定向到的信息帧缓冲区本身。 最佳答案 rubycorelib有一个IO您应该能够使用该类将输出定向
目录一、原理部分1、什么是串行通信(1)并行通信与串行通信(2)串行通信的制式(3)串行通信的主要方式 2、配置串口(1)SCON和PCON:串行口1的控制寄存器(2)SBUF:串行口数据缓冲寄存器 (3)AUXR:辅助寄存器编辑(4)ES、PS:与串行口1中断相关的寄存器(5)波特率设置 3、串口框架编写二、程序案例一、原理部分1、什么是串行通信(1)并行通信与串行通信微控制器与外部设备的数据通信,根据连线结构和传送方式的不同,可以分为两种:并行通信和串行通信。并行通信:数据的各位同时发送与接收,每个数据位使用一条导线,这种方式传输快,但是需要多条导线进行信号传输。串行通信:数据一位一
假设我有一个包含帖子的博客应用程序。创建帖子后,将创建一个工作人员来处理一些后台操作。我的情况是,在提交表单后我想显示某种加载消息(gif加载器等),当工作人员完成时我想隐藏加载消息并显示工作人员提供的一些数据。我的问题是,传达工作人员已完成工作并将其显示在用户前端的最佳方式是什么。worker回调看起来像这样defworker_finish#messagetheuserend 最佳答案 我认为您可能忽略了拥有后台工作人员的意义,基本上,您试图做的是弄巧成拙。--如果用户提交表单并且你在你的Controller中排队作业,只是为了让
我正在尝试访问我无法控制的SOAP服务。其中一个操作称为ProcessMessage。我按照这个例子生成了一个SOAP请求,但我收到一条错误消息,指出该操作不存在。我将问题追溯到生成信封正文的方式。USER658e702d5feff1777a6c741847239eb5d6d86e482010-02-18T02:05:25Zpassword......ProcessMessage标签应该是:这就是示例Java应用程序生成它时的样子,并且可以正常工作。该标记是我的Ruby应用程序生成的内容与示例Java应用程序之间的唯一区别。有什么方法可以去掉那个标签前面的"wsdl:"命名空间并添加这