Articolul descrie cum să lucrați cu rețeaua folosind un exemplu de conversație de rețea foarte simplă și descrie un adaptor de rețea (adaptor, înveliș, înveliș) care nu este conectat la rețea.
În ciuda faptului că chat-ul nostru cât mai simplu posibil (nu vă permite să transferați fișiere și mesaje offline, nu păstrează povestea transmite mesaje nu sunt criptate, etc), toți suntem departamentul aceleași părți responsabile pentru activitatea rețelei. Această parte va fi folosită clasa QTcpSocket, interfață care nu ne convine, și, prin urmare, aplicăm model de design Wrapper.
Serverul de chat funcționează în același fir, deoarece Scopul acestui articol a fost să descriem un exemplu care arată foarte simplu și clar folosirea prizelor Qt. Într-unul din următoarele articole, probabil voi descrie un chat cu mai multe fire.
- design Adaptor;
- lucrați cu rețeaua în Qt. Clase QTcpServer și QTcpSocket;
- un exemplu de utilizare a modelului Adaptor;
- codul sursă al chatului de rețea.
1. Adaptorul de design
Adaptorul se referă la modele de design structural și este utilizat în cazurile în care apare o clasă a cărei interfață nu este adecvată din anumite motive. Soluția evidentă a acestei probleme este crearea unei noi clase cu o interfață "corectă" care va delega cumva solicitări unei clase cu o interfață "rea" - în timp ce interfața care nu ne place nouă pare a fi "ascunsă". Prin urmare, modelul de design al adaptorului este, de asemenea, numit un înveliș.
Fig. 1 adaptor pentru șablon de proiectare. problemă
Figura 1 prezintă o situație tipică în care utilizarea unui adaptor poate fi adecvată. Există un anumit cod (Client) care utilizează instanțe din clasele A și B. implementând o interfață comună. La un moment dat, trebuia să folosim clasa Adaptee la egalitate cu A și B. dar nu implementează interfața de care avem nevoie.
Probabil, clasa Adaptee este destul de complexă încât nu ne apare să o rescriem cu o interfață nouă sau să urcăm în ea pentru a "sub-ilita" interfața.
Prima soluție la această problemă este de a moșteni clasele Interface și Adaptee cu clasa Adaptor. așa cum se arată în figura 2. În acest caz, moștenirea privată este utilizată pentru clasa Adaptee. deoarece adaptorul nu trebuie să furnizeze o interfață suplimentară. Această soluție este numită adaptor de clasă, folosește mai multe moșteniri și toate problemele care decurg din ea.
Fig. Adaptor de clasa 2
O altă opțiune este adaptorul de obiecte, care constă în moștenirea interfeței și agregarea clasei adaptabile (Figura 3). O astfel de soluție este mai flexibilă și nu are dezavantaje din moștenirea multiplă. Cu toate acestea, datorită faptului că accesul la elementele clasei adaptabile este realizat prin pointer, viteza scade.
Fig. 3 adaptor obiect
Exemplul din diagrame este foarte simplu și abstract, în realitate totul se întâmplă destul de diferit - sarcina nu este doar înlocuirea barei funcționale cu foo. dar de a face o mulțime de lucru pe adaptarea interfeței.
2. Lucrul cu rețeaua în Qt. Clase QTcpServer și QTcpSocket
În biblioteca Qt există un modul de lucru cu rețeaua - rețeaua Qt. pentru conectarea căruia în fișierul proiectului este suficient să adăugați o singură linie:
Modulul oferă o serie de clase utile, inclusiv QHttp [2], QTcpSocket și QTcpServer discutate anterior.
Sarcina principală a QTcpServer este de a monitoriza conectarea clienților. Serverul ascultă pe un anumit port. specificat când este apelată metoda de ascultare. Când clientul este conectat, se generează un semnal nouConnection și se creează un socket (QTcpSocket) pentru a face schimb de date cu clientul. Puteți obține un pointer la soclu apelând nextPendingConnection.
Schimbul de date cu clientul se realizează prin intermediul soclului. Când clientul este deconectat, soclul generează un semnal deconectat. și când intrați în mufa de date, semnalul este pregătit. Semnalul gata de citire este apelat de fiecare dată când o nouă bucată de date intră în soclu, puteți afla cât de multe date sunt disponibile pentru citirea în socket apelând metoda bytesAvailable.
Pentru ca datele să intre în socket, acestea trebuie să fie scrise acolo, pentru aceasta folosiți metoda QTcpSocket :: write. Putem scrie secvența de octeți vechi în soclu:
Lista 1 arată codul pentru scrierea unui șir în socket. Dacă în plus față de linia pe care doriți să scrieți altceva, numai linia 5 se va schimba. Scriem în blocurile de date socket, la începutul blocului există un număr de octeți. În linia a 5-a. Rezervăm memoria pentru dimensiunea viitoare la începutul blocului, scriem restul datelor în bloc, setăm indicatorul de înregistrare la începutul liniei 7 și plasăm dimensiunea blocului rezultat în această linie.
Când citiți date dintr-un soclu, trebuie să țineți cont de faptul că datele pot veni în părți. Nimeni nu garantează că, în urma a două apeluri la funcția de scriere. semnalul gata de citire va fi trimis de două ori exact - poate fi trimis de câte ori (chiar o singură dată).
În listare 2, se citește exact o linie din soclu, dar dacă s-ar citi mai multe date, numai 14 linii de cod s-ar schimba. Ciclul etern în acest cod este necesar tocmai pentru că mai multe semnale pentru scrierea în soclu pot veni cu un semnal readyRead - trebuie să procesați toate datele din soclu. La citirea unui șir, semnalul mesajului este generat în acest exemplu.
3. Exemplu de utilizare a modelului adaptorului
Exemplul nostru de adaptare nu va fi foarte tipic, deoarece nu vom avea clasele A și B (Figura 3) - adaptăm clasa nu la sistemul existent, ci la cel proiectat.
Fig. 4 adaptor neobișnuit
Figura 4 arată foarte mult ce vom face. Adaptee va acționa ca un soclu al bibliotecii Qt - clasa QTcpSocket. Nu este foarte plăcut să lucrați cu acest soclu - este încă un lucru foarte scăzut. Clientul va fi un chat sau un server, în dezvoltarea căruia dorim să avem o interfață mai convenabilă - de aceea vom adăuga un strat sub forma unui adaptor. În exterior, acest lucru nu este foarte asemănător cu adaptorul standard. dar rezolvă aceleași probleme - interfața de clasă socket este (se adaptează) la forma dorită.
Aș dori ca soclul să raporteze conexiunea și datele noi să fie rupte atunci când sunt recepționate pe deplin și să permită și trimiterea datelor. Pentru aceasta, interfața adaptorului trebuie să conțină 2 semnale și o metodă (sau slot) de trimitere. Această interfață este afișată în listare 3.
Clasa adaptorului agregă soclul și moștenește interfața. Ca rezultat, toate operațiunile socket sunt ascunse în spatele unei interfețe frumoase.
Un exemplu ar fi mai de succes dacă chatul ar putea primi date din alte surse (pe lângă socketul TCP) care ar implementa interfața noastră. Astfel de surse ar putea fi, de exemplu, wrappers peste QUdpSocket sau variații pe tema transferului de date într-o formă protejată. Exemplul nostru de adaptor asupra rezultatelor aplicației sa dovedit a fi similar fațadei.
4. Codul sursă al chatului de rețea
Diagrama claselor (nu chiar UML) a clientului și a serverului nostru este prezentată în Figura 5. Unele clase au fost deja examinate în detaliu.
Fig. 5 diagrama de clase de chat de rețea
MainWidget este fereastra principală de chat, agregă formularul creat în Qt Designer [4]. Formularul conține 2 câmpuri de introducere și un buton. Când se face clic pe buton, se apelează funcția sendString a clasei ClientSocketAdapter. iar câmpul de introducere este șters. Când semnalul de mesaj este primit de la ClientSocketAdapter. Al doilea câmp de intrare al formularului principal este completat cu linia primită de la server.
Clasa Server agregă QTcpServer. precum și o listă a indicatoarelor la adaptoarele pentru prize. Atunci când clientul este conectat, serverul nostru primește un pointer la socket-ul creat de la QTcpServer, pe baza căruia este creat și adăugat adaptorul de soclu server (ServerSocketAdapter). Serverul stochează o listă de clienți conectați pentru a redirecționa mesajele între ele. Când un mesaj este primit de la un client, serverul ocolește lista adaptoarelor și apelează metoda sendString pentru fiecare. Când clientul este deconectat, adaptorul este eliminat din listă. Distructorul adaptorului asigură eliberarea corectă a memoriei de sub socket (QTcpSocket).
Datorită faptului că clientul și serverul fac schimb de mesaje în același format, adaptorul de soclu client este aproape identic cu adaptorul de soclu server. astfel încât cea mai mare parte a codului poate fi transferată în clasa SocketAdapter.
Prima diferență dintre acestea este că adaptorul de soclu server este creat pe baza obiectului QTcpSocket deja existent. iar adaptorul soclu client trebuie să creeze un astfel de obiect. Această diferență este luată în considerare în constructorul din clasa SocketAdapter.
În plus, soclul clientului trebuie să ruleze metoda connectToHost pentru a porni dialogul cu serverul. Serverul nu trebuie să întreprindă astfel de acțiuni.
Codul sursă complet al clientului și al serverului poate fi descărcat gratuit: chat-server Qt.
În următorul articol, ne vom gândi la paralelizarea serverului utilizând QThread.
Lista surselor utile
Afișați navigația
7 gânduri despre "Lucrul cu rețeaua în Qt. Sockets. Adaptor de model "
Caracteristica este în atașament.
Când toți clienții sunt închise, serverul continuă să funcționeze.
Ie Puteți opri serverul numai prin managerul de activități.
Aceasta nu este o caracteristică. Serverul nu depinde de clienți, poate fi localizat pe altă mașină (undeva pe Internet).
Ieșirea clienților din chat nu trebuie să ducă la oprirea serverului. Acum, toată lumea a plecat, dar într-un minut cineva va veni (va încerca să se conecteze) și ce? - Serverul de-al tău ar trebui să fie inaccesibil, tk. înainte de a ieși toate acestea?
Vladimir, bună după-amiază.
1. De ce nu folosiți metoda override incomingConnection (int)?
2. Ei bine, de ce în loc de # include clasa QTcpSocket este folosit. - La fel ca stilul scrisului?
De ce nu folosiți metoda override incomingConnection (int)
Pe partea de server (unde apar noi conexiuni), folosesc semnalul QTcpServer :: newConnection ():
conectați (m_ptcpServer, SIGNAL (newConnection ()), SLOT (on_newConnection ()));
Acest semnal este generat de fiecare dată când are loc o conexiune nouă. Din documentație:
Acest semnal este emis de fiecare dată când este disponibilă o nouă conexiune.
QTcpServer :: incomingConnection este o funcție virtuală care este de asemenea apelată atunci când apare o conexiune nouă. Documentația spune că această funcție este implementată în QTcpServer astfel încât să adauge soclul (conexiunea) la listă și să execute emit newConnection (). Pentru a schimba acest lucru este necesar doar în cazuri, dacă avem nevoie de alt comportament, dacă avem nevoie doar de un semnal care a creat o nouă conexiune - ar trebui să folosim mereu semnalul QTcpServer :: newConnection ().
Pentru a le schimba este necesar doar în cazuri, dacă avem nevoie de un alt comportament, dacă avem nevoie doar de un semnal că a apărut o nouă conexiune - ar trebui să folosim mereu semnalul QTcpServer :: newConnection ()
Și ce se înțelege printr-un alt comportament. Ei bine, doar că semnalul a intrat și am început să descriem ce avem nevoie, de exemplu, am numărat ceva pe server și l-am transmis clientului. Ca și cum toate acțiunile depind de existența unui semnal sau nu. Ei bine, asta este există un semnal, facem ceva în orice caz.
Când trebuie să folosim intrareaConnection (int) în cazul nostru? Ei bine, doar un exemplu.
2) Aplicație
Uneori fac ceva pe Arduino. Acum am comandat modulele wi-fi ESP-8266 pentru Arduino. De asemenea, am de gând să comand o cameră pentru a insera și a primi informații pe PC în acest ESP-8266. În principiu, Arduino în sine nu este necesară, pentru că totul are ESP-8266 (cu atât mai mult puteți arde IDE-ul Arduino pentru ușurința de funcționare). Pentru comunicarea pe wi-fi și dezvoltarea ta este utilă. Mulțumesc.
Vreau, de asemenea, să folosesc opencv pe un PC pentru a procesa cumva imaginea rezultată.