The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]

Каталог документации / Раздел "Программирование, языки" / Оглавление документа

13.4. Протокол UDP и класс QSocketDevice.

Класс QSocketDevice реализует низкоуровневый интерфейс для работы с протоколами UDP и TCP. В большинстве TCP-приложений, достаточно будет функциональности, заложенной в QSocket, но если в приложении необходимо работать с протоколом UDP, то тогда вам придется обратить свой взор на класс QSocketDevice.

UDP -- это протокол с негарантированной доставкой сообщений, ориентированный на работу с датаграммами. Некоторые нестандартные протоколы работают поверх UDP, поскольку он не такой "тяжелый" как TCP. По протоколу UDP, данные передаются в виде отдельных пакетов - датаграмм. В этом протоколе отсутствует понятие "соединения", если UDP-пакет теряется где-то в недрах сети, то система не получит уведомления об ошибке.

Рисунок 13.3. Внешний вид приложения TheWeather Station.


Рассмотрим принципы организации сетевых взаимодействий по протоколу UDP, на примере приложений Weather Balloon и Weather Station. Приложение Weather Balloon не имеет графического интерфейса. Его задача -- раз в 5 секунд отсылать UDP-датаграмму, которая содержит информацию о погодных условиях. Приложение Weather Station будет принимать эти датаграммы и отображать полученные сведения на экране. Начнем с приложения Weather Balloon.
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 Высота над уровнем моря (в метрах)
Передача датаграммы осуществляется вызовом writeBlock(). Третий и четвертый аргументы функции writeBlock() -- это IP-адрес и номер порта клиентского узла (Weather Station). В данном случае мы исходим из предположения, что оба приложения работают на одной машине, поэтому IP-адрес -- 127.0.0.1 (0x7F000001). В отличие от QSocket, экземпляры класса QSocketDevice не работает с сетевыми именами компьютеров, ему нужны IP-адреса. Если вам необходимо преобразовать имя хоста в его IP-адрес, это можно сделать с помощью класса QDns.
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-адреса и номера порта, на которые нужно послать ответ.




Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

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