Narzędzia użytkownika

Narzędzia witryny


sk2:qt

Różnice

Różnice między wybraną wersją a wersją aktualną.

Odnośnik do tego porównania

Both sides previous revision Poprzednia wersja
Nowa wersja
Poprzednia wersja
sk2:qt [2018/11/19 12:44]
jkonczak
sk2:qt [2022/11/28 16:26] (aktualna)
jkonczak [Sieć w Qt]
Linia 17: Linia 17:
 ===== Wprowadzenie ===== ===== Wprowadzenie =====
  
-O Qt [[https://​www.qt.io/​what-is-qt/|[1]]] [[https://​pl.wikipedia.org/​wiki/​Qt|[2]]] \\ +=== O Qt === 
-<​html><​small></​html>​Wersje frameworku (Qt4 i Qt5), ''​qtchooser''<​html></​small></​html>​+Qt to framework (zestaw bibliotek i narzędzi) dla języka C+''''​+. Duży nacisk w Qt jest położony na budowę aplikacji z graficznym interfejsem użytkownika i tworzenie wieloplatformowego kodu. \\ 
 +[[https://​www.qt.io/​product|Strona produktu]]     [[https://​wiki.qt.io/About_Qt|Strona "O Qt" w wiki Qt]]     [[https://​pl.wikipedia.org/​wiki/​Qt|Polska Wikipedia o Qt]]
  
-Qt dostarcza własny system ​budowania ​- program ​''​[[http://​doc.qt.io/​qt-5/​qmake-manual.html|qmake]]''​, co oczywiście nie przeszkadza korzystać z innych systemów budowania (np. CMake). +=== Narzędzia === 
-\\ +Qt do wersji 5 jako podstawowego systemu ​budowania ​używa dostarczonego z Qt programu ​''​[[https://​doc.qt.io/​qt-5/​qmake-manual.html|qmake]]''​. ​Dodatkowo Qt 5 wspiera oficjalnie ​CMake (nieoficjalnie Qt jest wspierane też przez inne systemy budowania).\\ 
-Do graficznego projektowania GUI dostarczany jest program ''​[[http://​doc.qt.io/​qt-5/​qtdesigner-manual.html|designer]]'',​ generujący pliki w formacie XML. Następnie programem ''​[[http://​doc.qt.io/​qt-5/​uic.html|uic]]''​ są one konwertowane na kod w C+''''​+. +Qt od wersji 6 jako podstawowego systemu budowania używa CMake ([[https://​doc.qt.io/​qt-6/​cmake-manual.html|dokumentacja dla Qt]]) nie zaprzestając rozwoju qmake. 
-\\ + 
-Typowe aplikacje w Qt wykorzystują [[http://​doc.qt.io/​qt-5/​qapplication.html#​exec|pętlę zdarzeń]]. Obiekty mogą zażądać obsługi zdarzenia (zdarzenie w Qt nazywa się ''​signal''​),​ a programista za pomocą funkcji ''​connect''​ określa jaka funkcja (nazywana w Qt ''​slot''​) ma być wywoływana gdy wystąpi podane zdarzenie ([[http://​doc.qt.io/​qt-5/​signalsandslots.html|signals and slots]]).+Do graficznego projektowania GUI opartego o [[https://​pl.wikipedia.org/​wiki/​Wid%C5%BCet|widżety]] ​dostarczany jest program ''​[[https://​doc.qt.io/​qt-6/​qtdesigner-manual.html|designer]]'',​ generujący pliki w formacie XML. Następnie programem ''​[[https://​doc.qt.io/​qt-6/​uic.html|uic]]''​ są one automatycznie ​konwertowane na kod w C+''''​+. ​\\ 
 +<​html><​small></​html>​Qt dostarcza też silnik [[https://​doc.qt.io/​qt-6/​qmlapplications.html|języka QML]] i biblioteki [[https://​doc.qt.io/​qt-6/​qtquickcontrols-index.html|Qt Quick]] do budowy GUI w QML. \\ Ponadto ''​assistant''​ pozwala przeglądać dokumentację,​ ''​linguist''​ w łatwy sposób umożliwia tłumaczenie programu. \\ Częścią SDK do Qt jest też IDE ''​qtcreator''​.<​html></​small></​html>​ 
 + 
 +=== Pętla zdarzeń, sygnały i sloty === 
 +Typowe aplikacje w Qt wykorzystują [[https://​doc.qt.io/​qt-6/​qapplication.html#​exec|pętlę zdarzeń]]. Obiekty mogą zażądać obsługi zdarzenia (zdarzenie w Qt nazywa się ''​signal''​),​ a programista za pomocą funkcji ''​connect''​ określa jaka funkcja (nazywana w Qt ''​slot''​) ma być wywoływana gdy wystąpi podane zdarzenie ([[https://​doc.qt.io/​qt-6/​signalsandslots.html|signals and slots]]).
 \\ \\
-Z historycznych przyczyn ​Qt korzysta z dodatkowego preprocesora ''​[[http://​doc.qt.io/​qt-5/​why-moc.html|moc]]''​. Od Qt5 użycie ''​moc''​ można pominąć, jeśli korzysta się z nowej składni funkcji ''​connect''​.+Qt korzysta z dodatkowego preprocesora ''​[[https://​doc.qt.io/​qt-6/​why-moc.html|moc]]''​. Od Qt5 użycie ''​moc''​ można pominąć, jeśli korzysta się z nowej składni funkcji ''​connect'' ​i nie definiuje własnych sygnałów.
 \\ \\
-<​html><​small></​html>​Zarówno ''​uic''​ jak i ''​moc''​ są wywoływane automatycznie przez system budowania.<​html></​small></​html>​+<​html><​small></​html>​Zarówno ''​uic''​ jak i ''​moc''​ są wywoływane automatycznie przez system budowania ​qmake. W cmake trzeba włączyć opcje [[https://​cmake.org/​cmake/​help/​latest/​prop_tgt/​AUTOUIC.html|AUTOUIC]] i [[https://​cmake.org/​cmake/​help/​latest/​prop_tgt/​AUTOMOC.html|AUTOMOC]].<​html></​small></​html>​
  
 ===== Sieć w Qt ===== ===== Sieć w Qt =====
  
-Opis z podręcznika Qt: [[https://​doc.qt.io/​qt-5.11/​qtnetwork-programming.html|Network Programming with Qt]]+Opis z podręcznika Qt: [[https://​doc.qt.io/​qt-6/​qtnetwork-programming.html|Network Programming with Qt]] 
 + 
 +W Qt funkcje sieciowe są **nieblokujące** (asynchroniczne).\\ 
 +Bufory nadawcze i odbiorcze w Qt są nieograniczone.
  
 Ważniejsze klasy do obsługi socketów i podstawowe metody i sygnały: Ważniejsze klasy do obsługi socketów i podstawowe metody i sygnały:
-  * ''​[[http://​doc.qt.io/​qt-5/​qtcpserver.html|QTcpServer]]''​ - klasa tworząca gniazdo nasłuchujące protokołu TCP +  * ''​[[https://​doc.qt.io/​qt-6/​qtcpserver.html|QTcpServer]]''​ - klasa tworząca gniazdo nasłuchujące protokołu TCP 
-    * klasa udostępnia sygnał (zdarzenie) ''​[[http://​doc.qt.io/​qt-5/​qtcpserver.html#​newConnection|newConnection]]''​ +    * klasa udostępnia sygnał (zdarzenie) ''​[[https://​doc.qt.io/​qt-6/​qtcpserver.html#​newConnection|newConnection]]''​ 
-    * metoda ''​[[http://​doc.qt.io/​qt-5/​qtcpserver.html#​listen|listen]]''​ wywołuje ''​bind'', ​opcjonalnie ​ustawia ''​SO_REUSEADDR''​ i wywołuje ''​listen''​ +    * metoda ''​[[https://​doc.qt.io/​qt-6/​qtcpserver.html#​listen|listen]]''​ wywołuje ''​bind'',​ ustawia ''​SO_REUSEADDR''​((w uproszczeniu;​ szczegóły tutaj: https://​doc.qt.io/​qt-6/​qabstractsocket.html#​BindFlag-enum)) ​i wywołuje ''​listen''​ 
-    * metoda ''​[[http://​doc.qt.io/​qt-5/​qtcpserver.html#​nextPendingConnection|nextPendingConnection]]''​ wykonuje ''​accept''​ +    * metoda ''​[[https://​doc.qt.io/​qt-6/​qtcpserver.html#​nextPendingConnection|nextPendingConnection]]''​ wykonuje ''​accept''​ 
-  * ''​[[http://​doc.qt.io/​qt-5/​qtcpsocket.html|QTcpSocket]]''​ - klasa gniada TCP klienckiego (łączącego się pod podany adres) +  * ''​[[https://​doc.qt.io/​qt-6/​qtcpsocket.html|QTcpSocket]]''​ - klasa gniada TCP klienckiego (łączącego się pod podany adres) 
-    * sygnały ''​[[http://​doc.qt.io/​qt-5/​qabstractsocket.html#​signals|connected]]''​ i ''​[[http://​doc.qt.io/​qt-5/​qabstractsocket.html#​signals|disconnected]]''​ +    * sygnały ''​[[https://​doc.qt.io/​qt-6/​qabstractsocket.html#​signals|connected]]''​ i ''​[[https://​doc.qt.io/​qt-6/​qabstractsocket.html#​signals|disconnected]]''​ 
-    * sygnał ''​[[http://​doc.qt.io/​qt-5/​qabstractsocket.html#​signals|error]]''​ (( Uwaga, w nowej składni ''​connect''​ wymagany jest wskaźnik na funkcję, a klasa QTcpSocket ma dwie funkcje ''​error''​. W efekcie potrzebne jest wybranie funkcji przez rzutowanie na właściwy typ  [[https://​wiki.qt.io/​New_Signal_Slot_Syntax#​Asynchronous_made_easier|[1]]] )) +    * sygnał ''​[[https://​doc.qt.io/​qt-6/​qabstractsocket.html#​signals|errorOccured]]''​ (Qt w wersji ≥5.15) \\ <​html><​small></​html>​ sygnał ''​[[https://​doc.qt.io/​qt-5.14/​qabstractsocket.html#​signals|error]]'' ​(Qt w wersji przed 5.15)(( Uwaga, w nowej składni ''​connect''​ wymagany jest wskaźnik na funkcję, a klasa QTcpSocket ma dwie funkcje ''​error''​. W efekcie potrzebne jest wybranie funkcji przez rzutowanie na właściwy typ  [[https://​wiki.qt.io/​New_Signal_Slot_Syntax#​Asynchronous_made_easier|[1]]] ))<​html></​small></​html>​ 
-    * sygnał ''​[[http://​doc.qt.io/​qt-5/​qiodevice.html#​signals|readyRead]]'',​ określający że przyszły nowe dane do odbioru  +    * sygnał ''​[[https://​doc.qt.io/​qt-6/​qiodevice.html#​signals|readyRead]]'',​ określający że przyszły nowe dane do odbioru  
-    * metoda ''​[[http://​doc.qt.io/​qt-5/​qabstractsocket.html#​connectToHost|connectToHost]]''​ żąda połączenia pod podany adres +    * metoda ''​[[https://​doc.qt.io/​qt-6/​qabstractsocket.html#​connectToHost|connectToHost]]''​ żąda połączenia pod podany adres 
-    * metoda ''​[[http://​doc.qt.io/​qt-5/​qiodevice.html#​write|write]]''​ pisze dane, natomiast ''​[[http://​doc.qt.io/​qt-5/​qiodevice.html#​read|read]]''​ i ''​[[http://​doc.qt.io/​qt-5/​qiodevice.html#​readAll|readAll]]'' ​ odbierają dane +    * metoda ''​[[https://​doc.qt.io/​qt-6/​qiodevice.html#​write|write]]''​ pisze dane, natomiast ''​[[https://​doc.qt.io/​qt-6/​qiodevice.html#​read|read]]''​ i ''​[[https://​doc.qt.io/​qt-6/​qiodevice.html#​readAll|readAll]]'' ​ odbierają dane 
-  * ''​[[http://​doc.qt.io/​qt-5/​qudpsocket.html|QUdpSocket]]''​ - klasa obsługująca gniazda UDP +  * ''​[[https://​doc.qt.io/​qt-6/​qudpsocket.html|QUdpSocket]]''​ - klasa obsługująca gniazda UDP 
-    * sygnały ​podobne ​jak dla ''​QTcpSocket''​ +    * sygnały ​identyczne ​jak dla ''​QTcpSocket''​ 
-    * odbiór wiadomości metodą ''​[[http://​doc.qt.io/​qt-5/​qudpsocket.html#​readDatagram|readDatagram]]''​ +    * odbiór wiadomości metodą ''​[[https://​doc.qt.io/​qt-6/​qudpsocket.html#​receiveDatagram|receiveDatagram]]''​ 
-    * wysłanie wiadomości metodą ''​[[http://​doc.qt.io/​qt-5/​qudpsocket.html#​writeDatagram|writeDatagram]]''​+    * wysłanie wiadomości metodą ''​[[https://​doc.qt.io/​qt-6/​qudpsocket.html#​writeDatagram|writeDatagram]]''​
  
 Qt jest modularny. Do włączenia modułu odpowiedzialnego za sieć należy: Qt jest modularny. Do włączenia modułu odpowiedzialnego za sieć należy:
   * dla qmake dodać linię: \\ ''​QT += network''​   * dla qmake dodać linię: \\ ''​QT += network''​
-  * dla cmake dodać do linii szukające Qt komponent Network, a następnie dodać go do bibliotek łączonych:​ \\ ''​find_package(Qt5 COMPONENTS … Network …) \\ target_link_libraries(target … Qt5::Network …)''​+  * dla cmake dodać do linii szukające Qt komponent Network, a następnie dodać go do bibliotek łączonych:​ \\ ''​find_package(Qt6 COMPONENTS … Network …) \\ target_link_libraries(target … Qt6::Network …)'' ​\\ <​html><​small></​html>​Dla Qt 5 trzeba zmienić oczywiście Qt6 na Qt5 <​html></​small></​html>​ 
 + 
 +<​html><​small></​html>​
   ​   ​
 +++++ Przykład konsolowego klienta TCP w Qt |
 +**Uwaga**: ten przykład pokazuje kilka niewygodnych / nieprzenośnych / rzadko używanych rzeczy:
 +  * nie ma przenośnego sposobu czekania na zdarzenia na standardowym wejściu – stąd osobny kod dla Windowsa
 +  * raczej rzadko spotyka się w wysokopoziomowym,​ obiektowym języku funkcje i zmienne globalne - stąd potrzeba ''​QObject::​connect''​ zamiast ''​connect'',​ ''&​socket''​ zamiast ''​socket''​ i podawanie do obsługi zdarzenia globalnej funkcji zamiast obiektu i wskaźnika na metodę z klasy.
 +
 +<code cmake CMakeLists.txt>​
 +cmake_minimum_required(VERSION 3.12)
 +project(tcp_client LANGUAGES CXX)
 +set(CMAKE_CXX_STANDARD 20)
 +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Network REQUIRED)
 +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network REQUIRED)
 +add_executable(tcp_client main.cpp)
 +target_link_libraries(tcp_client Qt${QT_VERSION_MAJOR}::​Core Qt${QT_VERSION_MAJOR}::​Network)
 +</​code>​
 +<code cpp main.cpp>​
 +#include <​QtCore>​
 +#include <​QtNetwork>​
 +#include <​unistd.h>​
 +#ifdef _WIN32
 +#include "​Windows.h"​
 +#endif
 +
 +void readStdin();​
 +void onConnect();​
 +void readSocket();​
 +void onError(QTcpSocket::​SocketError err);
 +
 +QTcpSocket socket;
 +
 +int main(int argc, char *argv[])
 +{
 +    QCoreApplication a(argc, argv);
 +
 +    // obsługa standardowego wejścia jako zdarzenie
 +    #ifdef _WIN32
 +    QWinEventNotifier inputNotifier(GetStdHandle(STD_INPUT_HANDLE));​
 +    QObject::​connect(&​inputNotifier,​ &​QWinEventNotifier::​activated,​ readStdin);
 +    #else
 +    QSocketNotifier inputNotifier(0,​ QSocketNotifier::​Read);​
 +    QObject::​connect(&​inputNotifier,​ &​QSocketNotifier::​activated,​ readStdin);
 +    #endif
 +
 +    QObject::​connect(&​socket,​ &​QTcpSocket::​connected,​ onConnect);
 +    QObject::​connect(&​socket,​ &​QTcpSocket::​readyRead,​ readSocket);​
 +    QObject::​connect(&​socket,​ &​QTcpSocket::​errorOccurred,​ onError);
 +
 +    socket.connectToHost(a.arguments().at(1),​ a.arguments().at(2).toUShort());​
 +
 +    return a.exec();
 +}
 +
 +void readStdin(){
 +    QByteArray ba(1024, '​\0'​);​
 +    int count = read(STDIN_FILENO,​ ba.data(), ba.size());
 +    if(count<​=0){
 +        socket.disconnectFromHost();​
 +        QByteArray ba = "​\033[1mError or EOF on standard input\033[0m\n";​
 +        write(STDOUT_FILENO,​ ba.data(), ba.length());​
 +        QCoreApplication::​exit(0);​
 +    }
 +    ba.resize(count);​
 +    socket.write(ba);​
 +}
 +
 +void onConnect(){
 +    QByteArray ba = ("​\033[1mConnected successfully to : " +
 +                     ​socket.peerAddress().toString() + ":"​ +
 +                     ​QString::​number(socket.peerPort()) +
 +                     "​\033[0m\n"​).toLocal8Bit();​
 +    write(STDOUT_FILENO,​ ba.data(), ba.length());​
 +}
 +
 +void readSocket(){
 +    QByteArray ba = socket.readAll();​
 +    write(STDOUT_FILENO,​ ba.data(), ba.length());​
 +}
 +void onError(QTcpSocket::​SocketError err){
 +    QByteArray ba = ("​\033[1mError or disconnect on socket: "​+socket.errorString()+"​\033[0m\n"​).toLocal8Bit();​
 +    write(STDOUT_FILENO,​ ba.data(), ba.length());​
 +    QCoreApplication::​exit(0);​
 +}
 +</​code>​
 +++++
 +
 +<​html></​small></​html>​
 ===== Zadania ===== ===== Zadania =====
   ​   ​
Linia 60: Linia 155:
  
  
-IP multicast - przypomnienie ​\\+=== IP multicast - przypomnienie ​===
 [[http://​www.iana.org/​assignments/​multicast-addresses/​multicast-addresses.xhtml|Pula adresów]] (224.0.0.0/​4),​ w tym: lokalne dla łącza adresy (224.0.0.0/​24),​ [[https://​tools.ietf.org/​html/​rfc2365|Organization-Local Scope]] (239.0.0.0/​24)\\ [[http://​www.iana.org/​assignments/​multicast-addresses/​multicast-addresses.xhtml|Pula adresów]] (224.0.0.0/​4),​ w tym: lokalne dla łącza adresy (224.0.0.0/​24),​ [[https://​tools.ietf.org/​html/​rfc2365|Organization-Local Scope]] (239.0.0.0/​24)\\
-Komputer chcąc odbierać wiadomości od grupy multicastowej musi wysłać odpowiednią wiadomość IGMP \\+Komputer chcąc odbierać wiadomości od grupy multicastowej musi wysłać odpowiednią wiadomość IGMP. \\  
 +BSD Socket API pozwala dołączyć do grupy multicastowej na konkretnym sockecie, adresie i interfejsie,​ co powoduje wysłanie wspomnianej wiadomości. ​\\
 IP multicast można używać w połączeniu z protokołem UDP, natomiast nie jest wspierany przez protokół TCP. IP multicast można używać w połączeniu z protokołem UDP, natomiast nie jest wspierany przez protokół TCP.
 \\ \\
Linia 68: Linia 164:
 Użycie IP multicast w BSD socket API: [[sk2:​multicast_example]]\\ Użycie IP multicast w BSD socket API: [[sk2:​multicast_example]]\\
 Lista grup mcastowych do których jest zapisany komputer: ''​ip maddr''​ lub ''​netstat -ng''​ lub ''​cat /​proc/​net/​igmp''​ Lista grup mcastowych do których jest zapisany komputer: ''​ip maddr''​ lub ''​netstat -ng''​ lub ''​cat /​proc/​net/​igmp''​
 +++++ Użycie programu socat do testowania multicastu: |
 +Zajęcia w laboratorium:​
 +<code bash>
 +socat UDP-DATAGRAM:​239.0.0.1:​2000,​reuseaddr,​bind=0.0.0.0:​2000,​ip-add-membership=239.0.0.1:​enp0s3 STDIO
 +
 +# UDP-DATAGRAM:​239.0.0.1:​2000 → wysyłaj datagramy do podanego adresu (239.0.0.1:​2000) i odbieraj co przyjdzie
 +# reuseaddr → włącze SO_REUSEADDR;​ pozwala wielu programom na tym samym komputerze używać tego samego portu do mcastu
 +# ip-add-membership=239.0.0.1:​enp0s3 → dołącza do grupy mcastowej 239.0.0.1 na urządzeniu enp0s3
 +# bind=0.0.0.0:​2000 → wykonaj bind na podany adres; pod tym adresem będą odbierane datagramy
 +</​code>​
 +
 +Zajęcia zdalne:
 +<code bash>
 +socat UDP-DATAGRAM:​239.0.0.1:​2000,​reuseaddr,​bind=0.0.0.0:​2000,​ip-add-membership=239.0.0.1:​tap0,​ip-multicast-if=10.0.9.X STDIO
 +
 +# UDP-DATAGRAM:​239.0.0.1:​2000 → wysyłaj datagramy do podanego adresu (239.0.0.1:​2000) i odbieraj co przyjdzie
 +# reuseaddr → włącze SO_REUSEADDR;​ pozwala wielu programom na tym samym komputerze używać tego samego portu do mcastu
 +# ip-add-membership=239.0.0.1:​tap0 → dołącza do grupy mcastowej 239.0.0.1 na urządzeniu tap0
 +# bind=0.0.0.0:​2000 → wykonaj bind na podany adres; pod tym adresem będą odbierane datagramy
 +# ip-multicast-if=10.0.9.X → nakazuje wysyłać wiadomości do grupy multicastowej z podanego adresu ​
 +</​code>​
 +
 +
 +++++
 <​html></​small></​html>​ <​html></​small></​html>​
  
sk2/qt.1542627872.txt.gz · ostatnio zmienione: 2018/11/19 12:44 przez jkonczak