Класс QSocketDevice реализует низкоуровневый интерфейс для работы с протоколами UDP и TCP. В большинстве TCP-приложений, достаточно будет функциональности, заложенной в QSocket, но если в приложении необходимо работать с протоколом UDP, то тогда вам придется обратить свой взор на класс QSocketDevice.
UDP -- это протокол с негарантированной доставкой сообщений, ориентированный на работу с датаграммами. Некоторые нестандартные протоколы работают поверх UDP, поскольку он не такой "тяжелый" как TCP. По протоколу UDP, данные передаются в виде отдельных пакетов - датаграмм. В этом протоколе отсутствует понятие "соединения", если UDP-пакет теряется где-то в недрах сети, то система не получит уведомления об ошибке.
Рисунок 13.3. Внешний вид приложения TheWeather Station.
class WeatherBalloon : public QPushButton
{
Q_OBJECT
public:
WeatherBalloon(QWidget *parent = 0, const char *name = 0);
double temperature() const;
double humidity() const;
double altitude() const;
protected:
void timerEvent(QTimerEvent *event);
private:
QSocketDevice socketDevice;
int myTimerId;
};
Класс WeatherBalloon порожден от
QPushButton. Для взаимодествия с Weather
Station он использует QSocketDevice.
WeatherBalloon::WeatherBalloon(QWidget *parent, const char *name)
: QPushButton(tr("Quit"), parent, name),
socketDevice(QSocketDevice::Datagram)
{
socketDevice.setBlocking(false);
myTimerId = startTimer(5 * 1000);
}
В списке инициализаторов конструктора, вызовом QSocketDevice::Datagram, создается устройство
QSocketDevice. В теле конструктора,
созданное устройство переводится в асинхронный режим работы, вызовом
setBlocking(false). (По-умолчанию,
QSocketDevice работают в синхронном режиме.)
void WeatherBalloon::timerEvent(QTimerEvent *event)
{
if (event->timerId() == myTimerId) {
QByteArray datagram;
QDataStream out(datagram, IO_WriteOnly);
out.setVersion(5);
out << QDateTime::currentDateTime() << temperature()
<< humidity() << altitude();
socketDevice.writeBlock(datagram, datagram.size(),
0x7F000001, 5824);
} else {
QPushButton::timerEvent(event);
}
}
В обработчике событий от таймера генерируется датаграмма,
содержащая текущие дату, время, температуру воздуха, влажность и высоту
над уровнем моря:
QDateTime | Дата и время измерений |
double | Температура (в градусах Цельсия) |
double | Влажность (в %) |
double | Высота над уровнем моря (в метрах) |
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
WeatherBalloon balloon;
balloon.setCaption(QObject::tr("Weather Balloon"));
app.setMainWidget(&balloon);
QObject::connect(&balloon, SIGNAL(clicked()),
&app, SLOT(quit()));
balloon.show();
return app.exec();
}
Функция main() просто создает объект
WeatherBalloon, который выступает в двух
ипостасях: как UDP-узел и как кнопка QPushButton на экране.Теперь перейдем к приложению Weather Station.
class WeatherStation : public QDialog
{
Q_OBJECT
public:
WeatherStation(QWidget *parent = 0, const char *name = 0);
private slots:
void dataReceived();
private:
QSocketDevice socketDevice;
QSocketNotifier *socketNotifier;
QLabel *dateLabel;
QLabel *timeLabel;
...
QLineEdit *altitudeLineEdit;
};
Класс WeatherStation порожден от
QDialog. Его назначение -- ожидать
поступления датаграмм на определенном порту, производить разбор
поступившей информации (от Weather Balloon) и отображать ее в пяти
компонентах QLineEdit.Класс содержит две приватные переменные, которые представляют для нас наибольший интерес: socketDevice и socketNotifier. Первая переменная имеет тип QSocketDevice. Она используется для приема датаграмм. Вторая переменная имеет тип QSocketNotifier. Она используется для того, чтобы известить приложение о поступлении датаграммы.
WeatherStation::WeatherStation(QWidget *parent, const char *name)
: QDialog(parent, name), socketDevice(QSocketDevice::Datagram)
{
socketDevice.setBlocking(false);
socketDevice.bind(QHostAddress(), 5824);
socketNotifier = new QSocketNotifier(socketDevice.socket(),
QSocketNotifier::Read,
this);
connect(socketNotifier, SIGNAL(activated(int)),
this, SLOT(dataReceived()));
...
}
В списке инициализаторов конструктора создается устройство
QSocketDevice, вызовом QSocketDevice::Datagram. В теле конструктора, вызовом
setBlocking(false), оно переводится в
асинхронный режим работы. Вызовом функции bind(), сокету назначаются IP-адрес и номер порта.
Вызовом функции QHostAddress() мы сообщаем,
что будем принимать датаграммы, отправляемые на любой IP-адрес, который
принадлежит компьютеру с запущенным приложением Weather Station.Затем создается объект QSocketNotifier, который будет "следить" за сокетом. Объект QSocketNotifier будет выдавать сигнал activated(int) в момент поступления датаграммы. Этот сигнал соединяется со слотом dataReceived().
void WeatherStation::dataReceived()
{
QDateTime dateTime;
double temperature;
double humidity;
double altitude;
QByteArray datagram(socketDevice.bytesAvailable());
socketDevice.readBlock(datagram.data(), datagram.size());
QDataStream in(datagram, IO_ReadOnly);
in.setVersion(5);
in >> dateTime >> temperature >> humidity >> altitude;
dateLineEdit->setText(dateTime.date().toString());
timeLineEdit->setText(dateTime.time().toString());
temperatureLineEdit->setText(tr("%1 C").arg(temperature));
humidityLineEdit->setText(tr("%1%").arg(humidity));
altitudeLineEdit->setText(tr("%1 m").arg(altitude));
}
В функции dataReceived() производится
чтение датаграммы, вызовом readBlock().
Функция QByteArray::data() возвращает
указатель на данные в QByteArray, по
которому readBlock() запишет полученные
сведения. Затем производится извлечение отдельных значений, которые
заносятся в визуальные компоненты для отображения на экране. С точки
зрения приложения, датаграмма всегда передается и принимается как
единый блок данных. Это означает, что если доступен хотя бы один байт,
то следовательно датаграмма получена целиком.
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
WeatherStation station;
app.setMainWidget(&station);
station.show();
return app.exec();
}
В заключение, в функции main()
создается объект WeatherStation и
назначается главным виджетом приложения.На этом мы заканчиваем рассмотрение принципов работы с UDP. Мы постарались создать приемное и передающее приложения настолько простыми, насколько это возможно. В большинстве реальных применений, приложения должны как передавать, так и принимать датаграммы. В составе класса QSocketDevice имеются функции peerAddress() и peerPort(), которые могут использоваться для определения IP-адреса и номера порта, на которые нужно послать ответ.
Пред. | В начало | След. |
Класс QSocket. | На уровень выше | XML |