Activitate de laborator
1. Să se acceseze un server simplu de pe mașina de dezvoltare folosind client în shell-ul Android atunci când:
- telefonul este conectat prin USB (adb port forwarding)
- telefonul este conectat prin WiFi
- clientul rulează în emulator (emulator networking)
Pentru a deschide un server TCP, cel mai rapid, putem folosi utilitarul nc astfel:
# Linux sau OSX (sau Android)
echo "hello world" | nc -l 5000
Ncat este o variantă mai modernă a netcat care funcționerază și pe Windows, cu opțiuni ușor diferite.
Serverul se va opri dupa prima conexiune
Alternativa este să ne creăm propriul server într-un limbaj de programare și să îl rulăm. Cel mai la îndemână este Python, dar putem lucra și în alte limbaje precum C.
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('0.0.0.0', 5000)
server_socket.bind(server_address)
server_socket.listen(1)
while True:
connection, client_address = server_socket.accept()
print(f'Connection from {client_address}')
connection.sendall(b'hello')
2. Să se cloneze în directorul de pe discul local conținutul depozitului la distanță de la aceasta adresa.
3. Primul task este să creăm o aplicație cu un buton și un TextView. La apăsarea butonului, se va porni un nou thread care va comunica cu un server ce va rula pe calculatorul nostru. Acest server va returna un text pe care îl vom afișa în TextView.
4. Să se importe în mediul integrat de dezvoltare Android Studio
proiectul SingleThreadedServer din directorul labtasks.
Acesta reprezintă o aplicație Android care implementează un server ce
ascultă pe un port (2017, în Constants.SERVER_PORT) solicitări de
conexiune provenite de la clienți.
Pe același fir de execuție pe care se face așteptarea, este realizată și transmiterea mesajului către client, preluat dintr-un câmp text.
Pornirea serverului se face prin introducerea în câmpul text a șirului
de caractere Start Server.
Oprirea serverului se face prin introducerea în câmpul text a șirului de
caractere Stop Server.
Preluarea invocărilor de la clienți este realizată doar atâta timp cât serverul se află în execuție.
Linux
Citirea mesajelor se face cu nc care primește ca parametrii adresa
Internet și portul serverului de pe dispozitivul Android. Prin
utilitarul time se verifică durata operației respective.
student@eim-lab:~$ nc 172.16.7.89 2000
Hello, EIM Student!
student@eim-lab:~$ time nc 172.16.7.89 2000
Hello, EIM Student!
real 0m0.009s
user 0m0.000s
sys 0m0.000s
Windows
Citirea mesajelor se face cu telnet care primește ca parametrii adresa
Internet și portul serverului de pe dispozitivul Android.
În situația în care Telnet nu este disponibil, se accesează Control Panel → Programs and Features → Turn Windows Features on or off, selectându-se produsul Telnet Client.




C:\Users\Student> telnet 192.168.56.101 2000
Start Server
Connection to host lost.
C:\Users\Student> telnet 192.168.56.101 2000
Connecting To 192.168.56.101...Could not open connection to the host, on port 2000: Connect failed
a) Să se trimită aplicația Android în fundal prin apăsarea tastei Home. Verificați conectivitatea cu serverul. De ce sunt tratate cererile în continuare?
b) Să se apese tasta Back. De ce sunt tratate cererile în continuare după ce aplicația Android este distrusă? Ce se întâmplă în momentul în care se dorește să se repornească aplicația Android? Cum ar putea fi remediată această problemă?
Indicații de Rezolvare: Aplicația Android este distrusă, însă resursele utilizate de acestea nu sunt eliberate, motiv pentru care serverul continuă să gestioneze solicitările primite de la clienți.
Astfel, în momentul în care se dorește să se repornească aplicația Android, se va genera o excepție, întrucât portul pe care se dorește să asculte serverul invocările de la clienți este deja ocupat.
04-06 00:00:00.000: E/Single Threaded Server(934): An exception has occurred: bind failed: EADDRINUSE (Address already in use)
04-06 00:00:00.000: W/System.err(934): java.net.BindException: bind failed: EADDRINUSE (Address already in use)
04-06 00:00:00.000: W/System.err(934): at libcore.io.IoBridge.bind(IoBridge.java:89)
04-06 00:00:00.000: W/System.err(934): at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:150)
04-06 00:00:00.000: W/System.err(934): at java.net.ServerSocket.<init>(ServerSocket.java:100)
04-06 00:00:00.000: W/System.err(934): at java.net.ServerSocket.<init>(ServerSocket.java:69)
O soluție ar putea fi eliberarea resurselor pe metoda onDestroy().
@Override
public void onDestroy() {
if (serverThread != null) {
serverThread.stopServer();
}
super.onDestroy();
}
c) Să se simuleze faptul că operația de comunicare dintre client și server durează o perioadă de timp mai mare (spre exemplu, 3 secunde). Ce se întâmplă în momentul în care există mai multe solicitări trimise simultan de mai mulți clienți (din console diferite)? Monitorizați timpul de răspuns în această situație.
Indicații de Rezolvare: Metoda Thread.sleep(), primind ca
parametru un interval de timp (exprimat în milisecunde) simulează o
așteptare, după care execuția aplicației Android este reluată în mod
normal.
try {
Thread.sleep(3000);
} catch (InterruptedException interruptedException) {
Log.e(Constants.TAG, interruptedException.getMessage());
if (Constants.DEBUG) {
interruptedException.printStackTrace();
}
}
d) Să se implementeze comunicația pentru fiecare conexiune dintre client și server pe un fir de execuție separat, astfel încât pe server, gestiunea solicitărilor provenite de la clienți să nu fie afectată.
5. Să se importe în mediul integrat de dezvoltare Android Studio
proiectul ClientServerCommunication din directorul labtasks.
Acesta reprezintă o aplicație Android având două fragmente, implementând atât un server cât și un client:
- serverul dispune de un câmp text prin intermediul căruia:
- pot fi realizate diferite operații asupra sa:
- pornire, prin introducerea mesajului Start Server;
- oprire, prin introducerea mesajului Stop Server.
- se poate preciza un mesaj care să fie transmis către client prin intermediul canalului de comunicație;
- pot fi realizate diferite operații asupra sa:
- clientul dispune de două câmpuri text, prin care se precizează parametrii de conexiune la server (adresa de Internet / portul) precum și un buton prin intermediul căruia se realizează legătura propriu-zisă, fiind preluată informația din cadrul canalului de comunicație și afișată corespunzător.

a) Să se implementeze funcționalitatea serverului.
În clasa ComunicationThread:
- se obține o referință către fluxul de ieșire asociat socket-ului,
prin intermediul metodei ajutătoare
Utilities.getWriter(); - se scrie o linie conținând textul din
serverTextEditText.
b) Să se implementeze funcționalitatea clientului.
În clasa ClientAsyncTask:
- se resetează conținutul câmpului text
serverMessageTextView, astfel încât acesta să conțină șirul vid; acest lucru este realizat înainte de a rula firul de execuție dedicat, așadar pe metodaonPreExecute(); - se obțin parametrii de conexiune la server (adresa de Internet,
obținută din câmpul text
serverAddressEditTextși portul, obținut din câmpul textserverPortEditText); aceștia sunt transmiși prin intermediul argumentelor metodeidoInBackground(); - se deschide un socket, folosind configurația precizată de utilizator;
- se obține o referință către fluxul de intrare asociat socket-ului,
prin intermediul metodei ajutătoare
Utilities.getReader(); - se citesc linii din contextul canalului de comunicație, atâta timp
cât nu se întâlnește
EOF(nu se întoarce valoareanull), valoarea fiind concatenată la câmpul textserverMessageTextView; acest lucru este realizat prin transmiterea liniilor către firul de execuție principal - al interfeței grafice - (unde controalele grafice sunt accesibile), prin intermediul metodeipublishProgress()care determină apelul metodei de callbackonPublishProgress(); - se închide socketul.
c) Să se pornească serverul (se introduce textul Start Server). În
client, să se verifice valoarea furnizată în cazul în care se introduc
valorile 127.0.0.1 / 2000, respectiv localhost / 2000.
Să se oprească serverul (se introduce textul Stop Server). Să se verifice ce valori sunt furnizate în această situație.
d) (doar Linux) Să se creeze o listă cu procesele care rulează la momentul curent, textul putând fi obținut, linie cu linie, prin intermediul unei conexiuni pe portul 2000, serverul fiind astfel simulat pe mașina fizică.
student@eim:~$ ps a | while read x; do echo "$x" | nc -N -l 2000; done
Pe mașinile Debian, comanda
ncse rulează cu opțiunea suplimentară-ppentru a se indica portul pe care se dorește ca acesta să accepte invocările.
Să se folosească clientul pentru a citi, linie cu linie, valorile furnizate de script-ul anterior. Va trebui precizată adresa mașinii fizice (în funcție de configurația folosită) și portul 2000
