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:

  1. telefonul este conectat prin USB (adb port forwarding)
  2. telefonul este conectat prin WiFi
  3. 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 PanelPrograms and FeaturesTurn Windows Features on or off, selectându-se produsul Telnet Client.

telnet03_windows81.png

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:

  1. serverul dispune de un câmp text prin intermediul căruia:
    1. pot fi realizate diferite operații asupra sa:
      1. pornire, prin introducerea mesajului Start Server;
      2. oprire, prin introducerea mesajului Stop Server.
    2. se poate preciza un mesaj care să fie transmis către client prin intermediul canalului de comunicație;
  2. 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:

  1. se obține o referință către fluxul de ieșire asociat socket-ului, prin intermediul metodei ajutătoare Utilities.getWriter();
  2. se scrie o linie conținând textul din serverTextEditText.

b) Să se implementeze funcționalitatea clientului.

În clasa ClientAsyncTask:

  1. 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 metoda onPreExecute();
  2. 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 text serverPortEditText); aceștia sunt transmiși prin intermediul argumentelor metodei doInBackground();
  3. se deschide un socket, folosind configurația precizată de utilizator;
  4. se obține o referință către fluxul de intrare asociat socket-ului, prin intermediul metodei ajutătoare Utilities.getReader();
  5. se citesc linii din contextul canalului de comunicație, atâta timp cât nu se întâlnește EOF (nu se întoarce valoarea null), valoarea fiind concatenată la câmpul text serverMessageTextView; 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 metodei publishProgress() care determină apelul metodei de callback onPublishProgress();
  6. 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 nc se rulează cu opțiunea suplimentară -p pentru 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