| |
Класс 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 |
Закладки на сайте Проследить за страницей |
Created 1996-2024 by Maxim Chirkov Добавить, Поддержать, Вебмастеру |