Activitate de Laborator
Se dorește implementarea unei aplicații Android de tip mesagerie instantanee bazată pe legături de tip punct-la-punct stabilite între dispozitive mobile care oferă un serviciu de acest tip și dispozitive mobile care l-au descoperit, putându-l accesa în cadrul rețelei locale.
Funcționalitățile pe care le implementează această aplicație Android sunt:
-
înregistrarea / deînregistrarea unui serviciu de mesagerie instantanee, pe un anumit port pe care îl specifică;
Important note: trebuie folosit un port mai mare de 1024 ca sa nu fie privilegiat.
-
pornirea / oprirea operației de descoperire a serviciilor în rețeaua locală;
-
conectarea / deconectarea la un serviciu descoperit sau la un dispozitiv mobil care a accesat serviciul înregistrat;
-
transmiterea de mesaje în cadrul legăturii de tip punct-la-punct.
Va fi utilizat următorul cod de culori pentru butoanele care au atașată funcționalitatea de înregistrare / deînregistrare, respectiv pornire / oprire:
- roșu - serviciul este oprit;
- verde - serviciul este pornit.
În cazul în care descoperirea serviciilor este pornită, vor fi întreținute două liste:
- lista cu serviciile descoperite, disponibile în cadrul altor dispozitive mobile din rețeaua locală;
- lista conținând conversațiile cu alte dispozitive mobile care au accesat serviciul înregistrat.
În situația în care descoperirea serviciilor este oprită, lista cu serviciile descoperite va fi vidă.
Comunicația se va realiza în cadrul unei ferestre dedicate, în care pot fi vizualizate diferit mesajele trimise și mesajele primite. Din cadrul acestei interfețe grafice se poate reveni în orice moment la panoul de control.
1. În contul GitHub personal, să se creeze un depozit denumit
'Laborator09'. Inițial, acesta trebuie să fie gol (nu trebuie să bifați
nici adăugarea unui fișier README.md
, nici a fișierului .gitignore
sau a a fișierului LICENSE
).
2. Să se cloneze în directorul de pe discul local conținutul depozitului la distanță de la .
În urma acestei operații, directorul Laborator09 va trebui să se conțină
directoarele labtasks
și solutions
.
student@eg106:~$ git clone https://www.github.com/eim-lab/Laborator09.git
3. Să se încarce conținutul descărcat în cadrul depozitului 'Laborator09' de pe contul GitHub personal.
student@eg106:~$ cd Laborator09
student@eg106:~/Laborator09$ git remote add Laborator09_perfectstudent https:*github.com/perfectstudent/Laborator09
student@eg106:~/Laborator09$ git push Laborator09_perfectstudent master
4. Să se importe în mediul integrat de dezvoltare Android Studio
proiectul ChatServiceJmDNS
din directorul labtasks
.
5. Să se ruleze aplicația Android pe două dispozitive mobile care să se găsească în aceeași rețea.
5a.
Note
În cazul Genymotion, se pot crea două imagini pentru
două dispozitive mobile (virtuale) de același tip, care vor putea fi
rulate separat, fiecare dintre acestea primind adrese Internet diferite
din intervalul 192.168.56.101
→ 192.168.56.254
. Pentru interfața
WiFi, Genymotion nu poate folosi decât NAT sau Bridge (manual
Genymotion).
Pentru acest laborator, este necesară configurația bridge.
În VirtualBox (> versiunea 4.3.26), se verifică faptul că dispozitivele virtuale comunică între ele prin intermediul unei interfețe de rețea, configurată să folosească Bridge.
În acest sens, trebuie realizate următoarele operații:
- în configurația aferentă fiecărui dispozitiv virtual (Machine → Settings sau Ctrl + S), se va selecta Bridge folosind rețeaua astfel definită pentru interfața Adapter 2
- apoi se setează o adresă MAC random pentru adapter 2 (manual, sau cu butonul asociat)
Acestea vor putea rula instanțe diferite ale aplicației Android, fiecare
folosind o denumire proprie pentru serviciu (la valoarea
generică Constants.SERVICE_NAME
definită în pachetul
ro.pub.cs.systems.eim.lab09.chatservice.general
se sufixează în mod
automat un șir de caractere generat aleator, astfel încât aceasta să fie
unică în rețeaua locală).
Se verifică faptul că fiecare aplicație Android rulează pe un dispozitiv diferit:

- în configurația aferentă fiecărui dispozitiv virtual (Machine → Settings sau Ctrl + S), se va selecta Bridged folosind rețeaua astfel definită pentru interfața Adapter 2
- În acest mod, fiecare emulator va fi cuplat în rețeaua laboratorului, iar cu comanda avahi-browse -rk _chatservice._tcp se pot vizualiza toate instanțele care rulează în acel moment
Telefon personal
- Pentru a folosi telefonul personal, se recomanda rețeaua WiFi EG106, care este în bridge cu toate PCurile din sala. Se poate folosi un emulator în bridge ca partener pentru telefon.
- În Logcat, se pot utiliza filtre diferite pentru fiecare dintre dintre instanțele aplicației Android, astfel încât să se faciliteze procesul de depanare.
5b. Să se utilizeze utilitarul ZeroConf Browser, deja instalat pe
emulatoare pentru a identifica serviciile pornite în rețea. Dacă
emulatoarele sunt în aceeași rețea cu o mașină Linux, se poate rula
avahi-browse -rk _chatservice._tcp
pentru a vizualiza serviciile
pornite
5c. La momentul publicării cu succes a serviciului (când butonul devine verde), să se colecteze adresele IP locale pe care serviciul devine vizibil. Aceasta se face în funcția OnCreateView a listenerului care primește schimbarea stării serviciului (ServiceDiscoveryStatusButtonListener).
...
if (view == null) {
view = inflater.inflate(R.layout.fragment_chat_network_service, parent, false);
}
* List all IPs where the server is visible
Enumeration interfaces = null;
try {
interfaces = NetworkInterface.getNetworkInterfaces();
} catch (IOException e){
Log.e(Constants.TAG, "Could not query interface list: " + e.getMessage());
if (Constants.DEBUG) {
e.printStackTrace();
}
}
String IPs = "";
while(interfaces.hasMoreElements())
{
NetworkInterface n = (NetworkInterface) interfaces.nextElement();
Enumeration ee = n.getInetAddresses();
while (ee.hasMoreElements())
{
InetAddress i = (InetAddress) ee.nextElement();
if (i instanceof Inet4Address) {
if(IPs.length() > 0)
IPs += ", ";
IPs += i.getHostAddress().toString();
}
}
}
TextView LocalIPs = (TextView)view.findViewById(R.id.service_discovery_local_addr);
LocalIPs.setText(IPs);
return view;
Verificați afișarea adreselor pe care serviciul devine disponibil. Utilitarele de explorare servicii (ZeroconfBrowser, avahi-browser) vor vedea serviciul pe una dintre aceste adrese.
5d. La apăsarea butonului de publicare serviciu, se va modifica titlul aplicației pentru a reflecta starea curentă a publicării:
În register:
chatActivity.setTitle(serviceInfo.getName());
Log.i(Constants.TAG, "Register service " +
serviceInfo.getName() + ":" +
serviceInfo.getTypeWithSubtype() + ":" +
serviceInfo.getPort()
);
În unregister:
chatActivity.setTitle("Chat Service JmDNS");
6. Să se implementeze rutina pentru trimiterea mesajelor, pe metoda
run()
a firului de execuție SendThread
din clasa ChatClient
a
pachetului
ro.pub.cs.systems.eim.lab09.chatservice.networkservicediscoveryoperations
.
Metoda va rula cât timp firul de execuție nu este întrerupt, informație
oferită de metoda isInterrupted()
.
while (!Thread.currentThread().isInterrupted()) {
* ...
}
Pe fiecare iterație, se vor realiza următoarele operații:
- se va prelua un mesaj din coada
messageQueue
(de tipBlockingQueue<String>
); metodatake()
este blocantă, în așteptarea unei valori care să poată fi furnizată - astfel, în momentul în care firul de execuție este întrerupt, se va genera o excepție de tipulInterruptedException
care trebuie tratată; - în cazul unui mesaj nenul, sunt realizate următoarele operații:
- se trimite mesajul pe canalul de comunicație corespunzător (se
apelează metoda
println()
a obiectului de tipPrintWriter
); - se construiește un obiect de tip
Message
, format din:- conținut: valoarea propriu-zisă a mesajului (șirul de caractere);
- tip: mesaj transmis (
Constants.MESSAGE_TYPE_SENT
);
- se atașează mesajul istoricului conversației (obiectul
conversationHistory
este de tipulList<Message>
) - în acest fel, chiar dacă interfața grafică nu este vizibilă la momentul în care este trimis mesajul, conversația poate fi încărcată atunci când se întâmplă acest lucru; - se afișează mesajul în fragmentul de tip
ChatConversationFragment
(prin intermediul metodeiappendMessage()
), dacă acesta este asociat activității la momentul respectivif (context != null) { ChatActivity chatActivity = (ChatActivity)context; FragmentManager fragmentManager = chatActivity.getFragmentManager(); Fragment fragment = fragmentManager.findFragmentByTag(Constants.FRAGMENT_TAG); if (fragment instanceof ChatConversationFragment && fragment.isVisible()) { ChatConversationFragment chatConversationFragment = (ChatConversationFragment)fragment; chatConversationFragment.appendMessage(message); } }
- se trimite mesajul pe canalul de comunicație corespunzător (se
apelează metoda
7. Să se implementeze rutina pentru primirea mesajelor, pe metoda
run()
a firului de execuție ReceiveThread
din clasa ChatClient
a
pachetului
ro.pub.cs.systems.eim.lab09.chatservice.networkservicediscoveryoperations
.
Metoda va rula cât timp firul de execuție nu este întrerupt, informație
oferită de metoda isInterrupted()
.
while (!Thread.currentThread().isInterrupted()) {
* ...
}
Pe fiecare iterație, se vor realiza următoarele operații:
- se primește mesajul pe canalul de comunicație corespunzător (se
apelează metoda
readLine()
a obiectului de tipBufferedReader
); - în cazul unui mesaj nenul, sunt realizate următoarele operații:
- se construiește un obiect de tip
Message
, format din:- conținut: valoarea propriu-zisă a mesajului (șirul de caractere);
- tip: mesaj transmis (
Constants.MESSAGE_TYPE_RECEIVED
);
- se atașează mesajul istoricului conversației (obiectul
conversationHistory
este de tipulList<Message>
); - se afișează mesajul în fragmentul de tip
ChatConversationFragment
(prin intermediul metodeiappendMessage()
), dacă acesta este asociat activității la momentul respectiv.
- se construiește un obiect de tip
8. Să se examineze folosind tcpdump/wireshark schimbul de mesaje între dispozitive
- asigurați-vă folosind
ping
că telefoanele sunt în contact IP între ele, și cu mașina host (Linux) - notați adresele IP folosite de fiecare dispozitiv: pentru telefoane,
ele sunt afișate pe rândul 2 al activității, pentru Linux, folosind
comanda
ip ro
. - porniți din starea în care toate butoanele sunt pe roșu (serviciul nu este public, iar descoperirea este inactivă). În linux, directorul /etc/avahi/services/ nu conține nimic
- Pe linux, cu comanda
avahi-browse -ac
, identificați serviciile și stațiile disponibile în rețeaua locală - Pe Android, folosind utilitarul
ZeroconfBrowser
, identificați serviciile și stațiile disponibile în rețeaua locală. Alternativă din Google Play: ServiceBrowser by Andriy Druk. - Folosind
avahi-browse -rk _chatservice._tcp
, asigurați-vă că nu există instanțe ale serviciului pe vreuna dintre mașini- pregătiți recoltarea pachetelor folosind comanda
tcpdump -ni any 'udp port 5353' -w ./dnssd.pcap
- pregătiți recoltarea pachetelor folosind comanda
- activați serviciul pe unul dintre telefoane, comanda avahi-browse de mai sus identifică apariția numelor și perechii IP/port.
- ce fel de mesaj este folosit pentru publicarea serviciului?
- observați folosirea înregistrărilor de tip PTR, SRV
- dacă ați pornit mai multe emulatoare/telefoane, observați cumularea răspunsurilor anterioare în fiecare răspuns
- cum se face descoperirea serviciilor (prin ce tip de mesaje)?
9. Folosind instrucțiunile din secțiunea Zeroconf sub Linux de mai sus
- pe PC-ul local publicați un serviciu pe portul 5003
- rulați serviciul folosind
while true; do nc -v -l -p 5003; done
- rulați serviciul folosind
- verificați interoperabilitatea cu clienții implementați pe Android
- puteți folosi clienți Linux cu comanda
nc <adresă> <port>
pentru a conversa cu un server descoperit laadresa:port
(pe Android sau Linux).
- puteți folosi clienți Linux cu comanda
10. Să se încarce modificările realizate în cadrul depozitului 'Laborator09' de pe contul GitHub personal, folosind un mesaj sugestiv.
student@eg106:~/Laborator09$ git add *
student@eg106:~/Laborator09$ git commit -m "implemented tasks for laboratory 09"
student@eg106:~/Laborator09$ git push Laborator09_perfectstudent master