Differences

This shows you the differences between two versions of the page.

Link to this comparison view

laboratoare:laborator06 [2016/04/08 07:10]
Andrei Roșu-Cojocaru
laboratoare:laborator06 [2017/02/20 20:54]
Line 1: Line 1:
-====== Laborator 06. Comunicația prin Sockeți TCP ====== 
  
-===== Realizarea Conexiunii între o Mașină Fizică și Dispozitivul Mobil ===== 
- 
-Pentru a putea comunica prin intermediul unui socket TCP, o mașină fizică și dispozitivul mobil trebuie să se găsească în aceeași rețea, astfel încât adresele IP ale acestora să fie vizibile între ele. 
- 
-==== Dispozitiv Fizic ==== 
- 
-O mașină fizică și un dispozitiv fizic pot comunica: 
-  - prin plasarea lor în aceeași rețea fără fir (WiFi), având adresele IP furnizate de un server DHCP ce rulează pe un router; 
-  - prin stabilirea unei legături punct la punct, folosind Bluetooth; 
-  - printr-o conexiune de date realizată prin intermediul portului USB. 
- 
-Pe telefon, se accesează //​Settings//​ → //Wireless & Networks// → //Tethering & portable hotspot// și se selectează opțiunea //USB  Tethering// 
- 
-{{ :​laboratoare:​laborator06:​usb_tethering_configuration.png?​nolink&​400 }} 
- 
-Astfel, se va activa (în mod automat) interfața ''​rndis0'',​ pentru care se poate determina adresa Internet: 
- 
-<​code>​ 
-student@eim2016:/​opt/​android-sdk-linux/​platform-tools$ ./adb devices 
-List of devices attached 
-0019531d59461f ​   device 
-student@eim2016:/​opt/​android-sdk-linux/​platform-tools$ ./adb -s 0019531d59461f shell 
-shell@n7000:/​ $ su 
-su 
-shell@n7000:/​ # ifconfig rndis0 
-ifconfig rndis0 
-rndis0: ip 192.168.42.129 mask 255.255.255.0 flags [up broadcast running multicast] 
-shell@n7000:/​ # 
-</​code>​ 
- 
-<note important>​Pe Linux, în situația în care dispozitivul mobil este detectat, fără a se indica tipul său, este necesar să se pornească serverul de ADB cu drepturi de administrator:​ 
-<​code>​ 
-student@eim2016:/​opt/​android-sdk-linux/​platform-tools$ ./adb devices 
-List of devices attached 
-0019531d59461f ​   ????????????​ 
-student@eim2016:/​opt/​android-sdk-linux/​platform-tools$ sudo ./adb kill-server 
-student@eim2016:/​opt/​android-sdk-linux/​platform-tools$ sudo ./adb start-server 
-student@eim2016:/​opt/​android-sdk-linux/​platform-tools$ sudo ./adb devices 
-List of devices attached 
-0019531d59461f ​   device 
-</​code>​ 
- 
-Pentru dispozitivele care nu permit conectarea cu ''​adb shell''​ in modul tethering [[http://​stackoverflow.com/​questions/​9210152/​set-up-device-for-development-no-permissions|soluție stackoverflow]],​ trebuie creat în Ubuntu fișierul ''/​etc/​udev/​rules.d/​51-android.rules''​ și adăugat codul obținut cu ''​lsusb''​ pentru fabricantul telefonului. Odată dispozitivul de rețea obținut (de exemplu ''​rndis0''​),​ se folosește ''​dhclient rndis0''​ pentru a obține o adresă IP de la telefon.  ​ 
- 
-Pe Windows, dacă dispozitivul mobil nu este detectat, poate fi necesară instalarea unor drivere suplimentare pentru stabilirea legăturii prin intermediul portului USB. 
-</​note>​ 
- 
-Ulterior, se determină adresa Internet a mașinii fizice, asociată interfeței ''​usb0''​ (Linux), respectiv Ethernet (Windows). 
- 
-<columns 100% 50%> 
- 
-**Linux** 
- 
-<​code>​ 
-student@eim2016:​~$ sudo ifconfig usb0 
-usb0      Link encap:​Ethernet ​ HWaddr 32:​ca:​4b:​1c:​ff:​7b ​ 
-          inet addr:​192.168.42.170 ​ Bcast:​192.168.42.255 ​ Mask:​255.255.255.0 
-          inet6 addr: fe80::​30ca:​4bff:​fe1c:​ff7b/​64 Scope:Link 
-          UP BROADCAST RUNNING MULTICAST ​ MTU:​1500 ​ Metric:1 
-          RX packets:109 errors:0 dropped:0 overruns:0 frame:0 
-          TX packets:319 errors:0 dropped:0 overruns:0 carrier:0 
-          collisions:​0 txqueuelen:​1000 
-          RX bytes:24103 (23.5 KiB)  TX bytes:64369 (62.8 KiB) 
-</​code>​ 
- 
-<​newcolumn>​ 
- 
-**Windows** 
- 
-<​code>​ 
-C:\Program Files (x86)\Android\android-sdk\platform-tools>​ipconfig 
- 
-Windows IP Configuration 
- 
- 
-Ethernet adapter Local Area Connection: 
- 
-   ​Connection-specific DNS Suffix ​ . : 
-   ​Link-local IPv6 Address . . . . . : fe80::​18bf:​d0be:​3625:​6b1%44 
-   IPv4 Address. . . . . . . . . . . : 192.168.42.81 
-   ​Subnet Mask . . . . . . . . . . . : 255.255.255.0 
-   ​Default Gateway . . . . . . . . . : 192.168.42.129 
-</​code>​ 
- 
-</​columns>​ 
- 
-==== Dispozitiv Virtual (Emulator) ==== 
- 
-=== Genymotion === 
- 
-Fiecărui dispozitiv virtual Genymotion îi este alocată în mod automat o adresă IP de către serverul DHCP configurat pe mașina virtuală VirtualBox în cadrul căruia rulează. ​ 
- 
-Implicit, este utilizat spațiul de adrese ''​192.168.56.1/​24'':​ 
-  * mașina fizică (default gateway) are adresa ''​192.168.56.1'';​ 
-  * pentru dispozitivele virtuale sunt alocate adrese în intervalul ''​192.168.56.101''​ .. ''​192.168.56.254''​. 
- 
-Configurarea spațiului de adrese care este folosit poate fi configurat prin intermediul VirtualBox, accesând //File// → //​Preferences//​ → //Network// → //Host Only Networks// 
- 
-{{ :​laboratoare:​laborator06:​virtualbox01.png?​nolink&​500 }} 
- 
-{{ :​laboratoare:​laborator06:​virtualbox02.png?​nolink&​500 }} 
- 
-Adresa IP care a fost atașată fiecărui dispozitiv virtual Genymotion poate fi verificată:​ 
-  * folosind comanda ''​adb devices'';​ <​code>​ 
-student@eim2016:/​opt/​android-sdk-linux/​platform-tools$ ./adb devices 
-List of devices attached 
-192.168.56.101:​5555 ​    ​device 
-</​code>​ 
-  * prin intermediul plugin-ului Genymotion pentru mediile integrate de dezvoltare. 
- 
-{{ :​laboratoare:​laborator06:​genymotion_ip_addresses.png?​nolink&​700 }} 
- 
-Conectivitatea dintre mașina fizică și dispozitivul virtual poate fi verificată folosind comanda ''​ping''​. <​code>​ 
-student@eim2016:​~$ ping 192.168.56.101 
-Pinging 192.168.56.101 with 32 bytes of data: 
-Reply from 192.168.56.101:​ bytes=32 time<1ms TTL=64 
-Reply from 192.168.56.101:​ bytes=32 time<1ms TTL=64 
-Reply from 192.168.56.101:​ bytes=32 time<1ms TTL=64 
-Reply from 192.168.56.101:​ bytes=32 time<1ms TTL=64 
- 
-Ping statistics for 192.168.56.101:​ 
-    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss), 
-Approximate round trip times in milli-seconds:​ 
-    Minimum = 0ms, Maximum = 0ms, Average = 0ms 
-</​code>​ 
- 
-=== Android Virtual Device === 
- 
-Fiecare instanță a unui dispozitiv virtual Android oferă o pereche de porturi pentru diferite conexiuni: 
-  * un port de consolă, prin intermediul căruia este permis accesul prin telnet pentru execuția de diverse comenzi; ​ 
-  * un port pentru ''​adb''​. 
- 
-Numerele folosite pentru aceste porturi sunt succesive. Implicit, numerotarea porturilor începe de la 5554 (portul de consolă) / 5555 (portul adb). Determinarea portului poate fi realizată: 
-  * prin inspectarea ferestrei în care este afișat emulatorul, având forma //Android Emulator (55nr)//, unde nr poate lua valori cuprinse între 54 și 87 (doar valori impare - pentru portul de consolă, valorile pare fiind rezervate pentru portul adb); astfel, sunt suportate maxim 16 instanțe de dispozitive virtuale Android simultan; 
- 
-{{ :​laboratoare:​laborator06:​android_virtual_device_port.png?​nolink&​600 }} 
- 
-  * prin rularea comenzii ''​adb devices''​ <​code>​ 
-student@eim2016:/​opt/​android-sdk-linux/​platform-tools$ ./adb devices 
-List of devices attached 
-emulator-5554 ​  ​device 
-</​code>​ 
- 
-Conectarea la consola dispozitivului virtual Android se face prin comanda: <​code>​ 
-student@eim2016:​~$ telnet localhost 55nr 
-</​code>​ 
-specificându-se portul pe care rulează emulatorul. 
- 
-În consolă, realizarea unei legături între mașina fizică și dispozitivul virtual Android se face prin [[http://​developer.android.com/​tools/​devices/​emulator.html#​portredirection|redirectarea portului]], folosind comanda ''​redir'',​ aceasta suportând mai multe opțiuni: 
-  * ''​list'';​ 
-  * ''​add'';​ 
-  * ''​del''​. 
- 
-^  **OPȚIUNE** ​ ^  **DESCRIERE** ​ ^ 
-| ''​list''​ | afișează toate redirectările de port folosite la momentul respectiv | 
-| ''​add <​protocol>:<​port_masina_fizica>:<​port_dispozitiv_virtual>''​ | adaugă o redirectare de port\\ ✔ ''<​protocol>''​ poate avea doar valorile ''​tcp''​ sau ''​udp''​\\ ✔ ''<​port_masina_fizica>''​ reprezintă numărul portului utilizat pe mașina fizică \\ ✔ ''<​port_dizpozitiv_virtual>''​ reprezintă numărul portului de pe dispozitivul virtual spre care vor fi redirecționate datele\\ | 
-| ''​del <​protocol>:<​port_masina_fizica>''​ | șterge o redirectare de port | 
- 
-**Exemplu** 
- 
-<​code>​ 
-student@eim2016:​~$ telnet localhost 5554 
-Android Console: type '​help'​ for a list of commands 
-OK 
-redir add tcp:​2000:​4000 
-OK 
-redir list 
-tcp:​2000 ​ => 4000 
-OK 
-redir del tcp:2000 
-OK 
-redir list 
-no active redirections 
-OK 
-exit 
-Connection to host lost. 
-student@eim2016:​~$ 
-</​code>​ 
- 
-===== Mecanisme pentru Comunicația prin Rețea în Android ===== 
- 
-Comunicația între mai multe dispozitive poate fi realizată: 
-  - prin metode generale, disponibile la nivelul platformei Java: 
-    - sockeți TCP, a căror funcționalitate este implementată în pachetul [[https://​docs.oracle.com/​javase/​8/​docs/​api/​index.html|java.net]],​ oferind acces la cele mai multe operații ce implică programarea de rețea (atât server, cât și client); 
-    - clasa [[https://​docs.oracle.com/​javase/​8/​docs/​api/​java/​net/​HttpURLConnection.html|HttpURLConnection]],​ prin care este facilitat accesul la servere care folosesc protocolul ''​http'';​ 
-  - prin mecanisme specifice Android: 
-    - clasa [[http://​developer.android.com/​reference/​android/​net/​http/​AndroidHttpClient.html|HttpClient]] permite posibilitatea de a descărca un conținut disponibil la o anumită adresă Internet, identificată printr-un URL; 
-    - clasa [[http://​developer.android.com/​reference/​org/​json/​JSONObject.html|JSONObject]] este utilizată pentru gestiunea datelor în format JSON. 
- 
-<note important>​De regulă, o aplicație Android nu are acces la comunicația prin rețea decât prin intermediul unei permisiuni speciale: <code xml> 
-<​manifest ...> 
-  <!-- other application properties or components --> 
-  <​uses-permission 
-    android:​name="​android.permission.INTERNET"​ /> 
-  <!-- other application properties or components --> ​ 
-</​manifest>​ 
-</​code>​ 
-</​note>​ 
- 
-===== Programarea de Rețea folosind Sockeți TCP în Android ===== 
- 
-Folosirea unui socket TCP presupune comunicația între două entități: 
-  - un client care se conectează la o anumită adresă, pe un anumit port, pe care le cunoaște în prealabil; 
-  - un server care așteaptă să fie invocat, la o adresă și la un port. 
- 
-În Android (ca și în cazul platformei Java), clasa de bază pentru comunicația dintre client și server este [[http://​developer.android.com/​reference/​java/​net/​Socket.html|Socket]]. Aceasta pune la dispoziție un flux de intrare și un flux de ieșire prin intermediul cărora diferite entități, ale căror adrese IP sunt vizibile între ele, pot transmite diferite date, folosind protocolul TCP. Un socket este reprezentat de o asociere dintre o adresă și un port. 
- 
-"​Ascultarea"​ invocărilor este realizată prin intermediul clasei [[http://​developer.android.com/​reference/​java/​net/​ServerSocket.html|ServerSocket]]. În momentul în care este detectată o astfel de solicitare, este creată o nouă conexiune, reprezentată de un obiect de tip ''​Socket''​ prin care se va realiza comunicarea. Astfel, la nivelul serverului, fiecare client este identificat printr-o instanță proprie a unui obiect ''​Socket''​. 
- 
-==== Clientul ==== 
- 
-Conexiunea unui client la un server se poate realiza numai în situația în care sunt cunoscute adresa IP (sau denumirea, rezolvată apoi prin intermediul DNS) și portul la care acesta așteaptă să fie invocat. Aceste valori sunt transmise ca parametrii în constructorul obiectului ''​Socket''​. Ulterior, operațiile de comunicație (citire / scriere) sunt realizate prin operații pe fluxuri de intrare (''​InputStream''​) și pe fluxuri de ieșire (''​OutputStream''​). 
- 
-Așadar, comunicația prin intermediul unui obiect de tip ''​Socket''​ presupune următorii pași: 
- 
-**1.** deschiderea unui socket, prin transmiterea parametrilor de identificare a gazdei (adresă IP / denumire și port) ca parametrii ai constructorului unui obiect de tip ''​Socket''​ 
- 
-<code java> 
-String hostname = "​localhost";​ 
-int port = 2016; 
-Socket socket = new Socket(hostname,​ port); 
-</​code>​ 
- 
-<note warning>​În momentul în care se realizează o instanță a unui obiect de tip ''​Socket'',​ pot fi generate următoarele excepții: 
-  * ''​UnknownHostException'',​ în situația în care se folosește o denumire pentru gazdă și aceasta nu poate fi rezolvată de serverul DNS (alternativ,​ poate fi furnizată chiar adresa IP, dacă este cunoscută în prealabil); 
-  * ''​IOException'',​ atunci când: 
-    * conexiunea nu a fost acceptată de către server; 
-    * perioada de așteptare pentru realizarea operației a fost depășită:​ 
-    * s-a produs o întrerupere sau altă problemă neașteptată. 
-</​note>​ 
- 
-**2.** crearea unui flux de ieșire pentru a trimite date prin intermediul socket-ului TCP și trimiterea efectivă a datelor: 
- 
-<code java> 
-BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());​ 
-PrintWriter printWriter = new PrintWriter(bufferedOutputStrea,​ true); 
-</​code>​ 
- 
-<note important>​De remarcat faptul că obiectul ''​PrintWriter''​ primește în constructor un parametru de tip ''​boolean''​ prin care se indică dacă transmiterea efectivă a datelor este realizată sau nu în mod automat atunci când se întâlnește caracterul ''​\n''​ (newline).</​note>​ 
- 
-Metodele implementate de clasa [[http://​developer.android.com/​reference/​java/​io/​PrintWriter.html|PrintWriter]] sunt similare cu cele oferite de ''​PrintStream''​ (clasa folosită de metodele din ''​System.out''​),​ diferența fiind faptul că pot fi create mai multe instanțe pentru seturi de caractere Unicode diferite: 
-  * ''​print()'';​ 
-  * ''​println()'';​ 
-  * ''​printf()'';​ 
-  * ''​write()''​. 
- 
-Trimiterea efectivă a datelor este realizată atunci când se apelează metoda ''​flush()''​. 
- 
-**3.** crearea unui flux de intrare pentru a primi date prin intermediul socket-ului TCP și primirea efectivă a datelor: 
- 
-<code java> 
-InputStreamReader inputStreamReader = new InputStreamReader(socket.getInputStream());​ 
-BufferedReader bufferedReader = new BufferedReader(inputStreamReader);​ 
-</​code>​ 
- 
-<note important>​Un obiect de tip ''​BufferedReader''​ poate stoca maxim 8192 de caractere în zona de memorie tampon de care dispune.</​note>​ 
- 
-Metodele implementate de clasa [[http://​developer.android.com/​reference/​java/​io/​BufferedReader.html|BufferedReader]] sunt: 
-  * ''​read()''​ - pentru a primi un singur caracter (dacă este apelată fără parametrii) sau un tablou de caractere (de o anumită dimensiune, acestea fiind stocate într-un vector furnizat ca parametru, începând cu o anumită poziție); 
-  * ''​readLine()''​ - pentru a primi o linie. 
- 
-<note tip>​Metoda ''​readLine()''​ este blocantă, așteptând un mesaj terminat prin ''​\n''​ (newline). În situația în care conexiunea este terminată, se transmite ''​EOF'',​ iar metoda întoarce valoarea ''​null''​.</​note>​ 
- 
-<note tip>Pot fi utilizate obiecte de tipul ''​ObjectOutputStream''​ și ''​ObjectInputStream''​ pentru transmiterea de obiecte Java, atunci când entitățile care comunică rulează în contextul unei mașini virtuale de acest tip (JVM).</​note>​ 
- 
-Se recomandă ca obținerea de referințe către fluxul de intrare respectiv fluxul de ieșire asociate unui obiect de tip ''​Socket''​ să fie realizată prin intermediul unor metode statice definite în cadrul unor clase ajutător: 
- 
-<file java Utilities.java>​ 
-import java.io.BufferedReader;​ 
-import java.io.IOException;​ 
-import java.io.InputStreamReader;​ 
-import java.io.PrintWriter;​ 
-import java.net.Socket;​ 
- 
-public class Utilities { 
- 
-  public static BufferedReader getReader(Socket socket) throws IOException { 
-    return new BufferedReader(new InputStreamReader(socket.getInputStream()));​ 
-  } 
-  ​ 
-  public static PrintWriter getWriter(Socket socket) throws IOException { 
-    return new PrintWriter(socket.getOutputStream(),​ true); 
-  } 
- 
-} 
-</​file>​ 
- 
-**4.** închiderea obiectului ''​Socket'',​ atunci când acesta nu mai este necesar 
- 
-<code java> 
-socket.close();​ 
-</​code>​ 
- 
-În momentul în care se apelează metoda ''​close()'',​ sunt transmise și datele care erau stocate în zonele de memorie tampon. 
- 
-<note tip>​Metoda ''​close()''​ eliberează și resursele folosite de fluxul de intrare și fluxul de ieșire asociate, care sunt de asemenea închise.</​note>​ 
- 
-==== Utilizarea unui Fir de Execuție Dedicat pentru Comunicația prin Rețea ==== 
- 
-Începând cu nivelul de API 11 (Android 3.0 Honeycomb), nu este permisă comunicația prin rețea pe același fir de execuție folosit pentru interfața grafică și pentru interacțiunea cu utilizatorul (//eng.// UI Thread), întrucât unele operații realizate în acest context sunt blocante, astfel încât experiența folosirii aplicației Android ar fi afectată. O astfel de tentativă va fi sancționată prin generarea unei excepții de tip [[http://​developer.android.com/​reference/​android/​os/​NetworkOnMainThreadException.html|NetworkOnMainThreadException]]. Din acest motiv, orice operație ce presupune folosirea unor sockeți TCP trebuie realizată pe un fir de execuție dedicat. 
- 
-Trebuie avut în vedere faptul că pe firul de execuție dedicat comunicației prin rețea NU pot fi actualizate informațiile asociate controalelor grafice, excepția generată în această situație fiind [[http://​developer.android.com/​tools/​testing/​activity_testing.html#​UITestTroubleshooting|CalledFromWrongThreadException]]. Explicația este dată în mesajul care însoțește această excepție: numai firul de execuție în care a fost instanțiat un control grafic (obiect de tip ''​android.view.View''​) are dreptul de a realiza modificări asupra acestuia (//Only the original thread that created a view hierarchy can touch its views//). În această situație, accesul la firul de execuție pe care este gestionată interfața grafică poate fi obținut în mai multe moduri: 
-  - folosind metoda [[http://​developer.android.com/​reference/​android/​app/​Activity.html#​runOnUiThread%28java.lang.Runnable%29|runOnUiThread(Runnable)]],​ disponibilă în clasa ''​Activity'';​ 
-  - folosind una din metodele [[http://​developer.android.com/​reference/​android/​view/​View.html#​post%28java.lang.Runnable%29|post(Runnable)]] sau [[http://​developer.android.com/​reference/​android/​view/​View.html#​postDelayed%28java.lang.Runnable,​%20long%29|postDelayed(Runnable,​ long)]] disponibile:​ 
-    - în clasa ''​android.view.View'',​ pentru fiecare control grafic care se dorește a fi accesat; 
-    - în clasa ''​android.os.Handler'',​ dacă se dorește accesarea mai multor controale grafice (fiind necesar ca instanțierea obiectului să fie realizată tot pe firul care gestionează interfața grafică); 
-  - folosind un obiect de tip [[http://​developer.android.com/​reference/​android/​os/​AsyncTask.html|AsyncTask]],​ procesarea în rețea putând fi realizată în metoda ''​doInBackground()'',​ iar accesul la obiectele interfeței grafice fiind acordat în metodele ''​onProgressUpdate()''​ (invocată în mod automat de fiecare dată când se apelează metoda ''​publishProgress()''​) și ''​onPostExecute()''​. 
- 
-{{ :​laboratoare:​laborator06:​fir_de_executie_dedicat_comunicatie_prin_retea.png?​nolink&​600 }} 
- 
-=== Exemplu === 
- 
-Se dorește interogarea serverului National Institute of Standards & Technology, care oferă un serviciu de interogare a datei și orei curente, cu o precizie ridicată, conform [[https://​tools.ietf.org/​html/​rfc867|Daytime Protocol (RFC-867)]]. 
- 
-{{ :​laboratoare:​laborator06:​01day_time_protocol.png?​nolink&​400 }} 
- 
-În acest sens, se va deschide un socket TCP, prin interogarea serverului disponibil la adresa [[time-b.nist.gov|]],​ pe portul 13, în cadrul unui fir de execuție separat (clasa ''​NISTCommunicationThread''​). Întrucât nu este necesară decât operația de primire a unor date, se va crea doar un obiect de tip ''​BufferedReader'',​ citindu-se două linii (una fiind vidă - așadar ignorată, cealaltă conținând informațiile necesare, care se doresc a fi afișate). Întrucât modificarea conținutului unui control grafic nu poate fi realizată decât din contextul firului de execuție în care a fost creat, acesta va fi obținut prin parametrul metodei ''​post()''​ a obiectului respectiv, doar aici fiind permisă asocierea conținutului solicitat. În momentul în care datele au fost preluate, socket-ul TCP poate fi închis. Pe fiecare eveniment de tip apăsare a butonului se va crea un fir de execuție dedicat în care se va instanția un obiect de tip ''​Socket''​. 
- 
-De remarcat faptul că obiectele interfeței grafice sunt definite ca membrii protejați ai clasei activitate, întrucât accesul acestea trebuie să poată fi accesate și din clasele interne, fără a se mai obține referința către ele prin metoda ''​findViewById()'',​ care depreciază performanțele aplicației Android. 
- 
-<file java DayTimeProtocolActivity.java>​ 
-package ro.pub.cs.systems.eim.lab06.daytimeprotocol.views;​ 
- 
-import android.os.AsyncTask;​ 
-import android.support.v7.app.AppCompatActivity;​ 
-import android.os.Bundle;​ 
-import android.util.Log;​ 
-import android.view.View;​ 
-import android.widget.Button;​ 
-import android.widget.TextView;​ 
- 
-import java.io.BufferedReader;​ 
-import java.io.IOException;​ 
-import java.net.Socket;​ 
-import java.net.UnknownHostException;​ 
- 
-import ro.pub.cs.systems.eim.lab06.daytimeprotocol.R;​ 
-import ro.pub.cs.systems.eim.lab06.daytimeprotocol.general.Constants;​ 
-import ro.pub.cs.systems.eim.lab06.daytimeprotocol.general.Utilities;​ 
- 
-public class DayTimeProtocolActivity extends AppCompatActivity { 
- 
-  private Button getInformationButton;​ 
-  private TextView daytimeProtocolTextView;​ 
- 
-  private class NISTCommunicationAsyncTask extends AsyncTask<​Void,​ Void, String> { 
- 
-    @Override 
-    protected String doInBackground(Void... params) { 
-      String dayTimeProtocol = null; 
-      try { 
-        Socket socket = new Socket(Constants.NIST_SERVER_HOST,​ Constants.NIST_SERVER_PORT);​ 
-        BufferedReader bufferedReader = Utilities.getReader(socket);​ 
-        bufferedReader.readLine();​ 
-        dayTimeProtocol = bufferedReader.readLine();​ 
-        Log.d(Constants.TAG,​ "The server returned: " + dayTimeProtocol);​ 
-      } catch (UnknownHostException unknownHostException) { 
-        Log.d(Constants.TAG,​ unknownHostException.getMessage());​ 
-        if (Constants.DEBUG) { 
-          unknownHostException.printStackTrace();​ 
-        } 
-      } catch (IOException ioException) { 
-        Log.d(Constants.TAG,​ ioException.getMessage());​ 
-        if (Constants.DEBUG) { 
-          ioException.printStackTrace();​ 
-        } 
-      } 
-      return dayTimeProtocol;​ 
-    } 
- 
-    @Override 
-    protected void onPostExecute(String result) { 
-      daytimeProtocolTextView.setText(result);​ 
-    } 
-  } 
- 
-  private ButtonClickListener buttonClickListener = new ButtonClickListener();​ 
-  private class ButtonClickListener implements Button.OnClickListener { 
- 
-    @Override 
-    public void onClick(View view) { 
-      NISTCommunicationAsyncTask nistCommunicationAsyncTask = new NISTCommunicationAsyncTask();​ 
-      nistCommunicationAsyncTask.execute();​ 
-    } 
- 
-  } 
- 
-  @Override 
-  protected void onCreate(Bundle savedInstanceState) { 
-    super.onCreate(savedInstanceState);​ 
-    setContentView(R.layout.activity_day_time_protocol);​ 
- 
-    daytimeProtocolTextView = (TextView)findViewById(R.id.daytime_protocol_text_view);​ 
- 
-    getInformationButton = (Button)findViewById(R.id.get_information_button);​ 
-    getInformationButton.setOnClickListener(buttonClickListener);​ 
-  } 
-} 
-</​file>​ 
- 
-==== Serverul ==== 
- 
-Un sever poate "​aștepta"​ conexiuni de clienți prin intermediul unei instanțe a clasei [[http://​developer.android.com/​reference/​java/​net/​ServerSocket.html|ServerSocket]],​ care poate primi ca parametrii: 
-  * o adresă - dacă nu este furnizată se folosește adresa mașinii / dispozitivului pe care a fost rulată, putând fi obținută prin metoda ''​getInetAddress()'';​ 
-  * un port - dacă nu este specificat (sau se transmite valoarea 0), sistemul de operare va genera o astfel de valoare (care poate fi obținută ulterior prin metoda ''​getLocalPort()''​. 
- 
-<note tip>Se recomandă să se utilizeze un port cu o valoare în afara celor rezervate (0 - 1023). Totuși, folosirea acestor porturi este posibilă în cazul dispozitivele mobile pentru care există drepturi de root, după ce se acordă drepturi privilegiate aplicației Android.</​note>​ 
- 
-Pentru ca un client să se poată conecta la server, pe obiectul ''​ServerSocket''​ trebuie să se apeleze metoda [[http://​developer.android.com/​reference/​java/​net/​ServerSocket.html#​accept%28%29|accept()]],​ care se blochează ascultând invocările care ar putea fi realizate. În momentul în care este detectată o solicitare, metoda o tratează, returnând un obiect de tip ''​Socket''​ prin care este realizată comunicarea propriu-zisă. 
- 
-De obicei, un server invocă metoda ''​accept()''​ într-o buclă, atâta timp cât se află în execuție. În momentul în care a fost obținut un obiect de tip ''​Socket'',​ se recomandă ca acesta să fie transmis unui alt fir de execuție care să gestioneze comunicația cu clientul, în caz contrar putându-se înregistra latențe în tratarea altor solicitări de conexiune. Mai mult, având în vedere faptul că acestea sunt plasate într-un obiect de tip coadă (FIFO), în momentul în care aceasta este completată,​ nu vor mai fi acceptate alte cereri de conexiune, acestea fiind respinse cu motivul //​Connection refused//. 
- 
-{{ :​laboratoare:​laborator06:​comunicatia_client_server.png?​nolink&​800 }} 
- 
-==== Exemplu 1. Server cu un sigur fir de execuție ==== 
- 
-Se implementează o aplicație Android cu un singur câmp text prin care se controlează:​ 
-  * diferite operații asupra serverului: 
-    * pornire: se transmite șirul de caractere ''​Start Server'';​ 
-    * oprire: se transmite șirul de caractere ''​Stop Server'';​ 
-  * mesajul care va fi transmis clienților care se conectează. 
- 
-Astfel, serverul va accepta invocări de la clienți între momentele la care s-au introdus șirurile de caractere ''​Start Server'',​ respectiv ''​Stop Server''​. Cât timp serverul este activ (ascultă solicitări de la clienți), se pot specifica orice valoare în câmpul text, acesta fiind transmis prin intermediul canalului de comunicație. 
- 
-<file xml activity_single_threaded.xml>​ 
-<​LinearLayout xmlns:​android="​http://​schemas.android.com/​apk/​res/​android"​ 
-  xmlns:​tools="​http://​schemas.android.com/​tools"​ 
-  android:​layout_width="​match_parent"​ 
-  android:​layout_height="​match_parent"​ 
-  android:​orientation="​vertical"​ 
-  tools:​context="​ro.pub.cs.systems.eim.lab06.singlethreadedserver.views.SingleThreadedActivity"​ > 
-    ​ 
-  <​EditText 
-    android:​id="​@+id/​server_text_edit_text"​ 
-    android:​layout_width="​match_parent"​ 
-    android:​layout_height="​wrap_content"​ 
-    android:​inputType="​text"​ /> 
- 
-</​LinearLayout>​ 
-</​file>​ 
- 
-În momentul în care se pornește serverul, se deschide un obiect de tipul ''​ServerSocket''​. Ulterior, în cadrul unui ciclu, se așteaptă invocări de la clienți, prin intermediul metodei ''​accept()'',​ care deschide un obiect ''​Socket''​ prin intermediul căruia se transmite textul stocat în controlul grafic corespunzător (după ce se obține fluxul de ieșire atașat). Se impune închiderea canalului de comunicație după ce acesta nu mai este necesar. 
- 
-În momentul în care se oprește serverul, se închide obiectul de tipul ''​ServerSocket'',​ ceea ce va genera o excepție ''​java.net.SocketException''​ în cadrul metodei blocante ''​accept()'',​ invalidându-se și condiția care asigura execuția ciclului pe care erau tratate solicitările de conexiune provenite de la clienți. 
- 
-<​note>​Repornirea serverului, astfel încât să accepte din nou invocări de la clienți, presupune crearea unui nou fir de execuție (cel vechi nu poate fi refolosit).</​note>​ 
- 
-<file java SingleThreadedServer.java>​ 
-import java.io.IOException;​ 
-import java.io.PrintWriter;​ 
-import java.net.ServerSocket;​ 
-import java.net.Socket;​ 
- 
-import android.app.Activity;​ 
-import android.os.Bundle;​ 
-import android.text.Editable;​ 
-import android.text.TextWatcher;​ 
-import android.util.Log;​ 
-import android.view.Menu;​ 
-import android.view.MenuItem;​ 
-import android.widget.EditText;​ 
- 
-public class SingleThreadedActivity extends Activity { 
- 
-  private EditText serverTextEditText;​ 
-  
-  private ServerThread singleThreadedServer;​ 
-  
-  private ServerTextContentWatcher serverTextContentWatcher = new ServerTextContentWatcher();​ 
-  private class ServerTextContentWatcher implements TextWatcher { 
- 
-    @Override 
-    public void beforeTextChanged(CharSequence charSequence,​ int start, int count, int after) { 
-    } 
-    ​ 
-    @Override 
-    public void onTextChanged(CharSequence charSequence,​ int start, int before, int count) { 
-      if (charSequence.toString().equals(Constants.SERVER_START)) { 
-        singleThreadedServer = new ServerThread();​ 
-        singleThreadedServer.startServer();​ 
-      } 
-      if (charSequence.toString().equals(Constants.SERVER_STOP)) { 
-        singleThreadedServer.stopServer();​ 
-      } 
-    } 
-    ​ 
-    @Override 
-    public void afterTextChanged(Editable editable) { 
-    } 
-  ​ 
-  } 
-  ​ 
-  private class ServerThread extends Thread { 
-  ​ 
-    private boolean isRunning; 
-    ​ 
-    private ServerSocket serverSocket;​ 
-    ​ 
-    public void startServer() { 
-      isRunning = true; 
-      start(); 
-    } 
-    ​ 
-    public void stopServer() { 
-      isRunning = false; 
-      new Thread(new Runnable() { 
-        @Override 
-        public void run() { 
-          try { 
-            if (serverSocket != null) { 
-              serverSocket.close();​ 
-            } 
-            Log.v(Constants.TAG,​ "​stopServer() method invoked "​+serverSocket);​ 
-          } catch(IOException ioException) { 
-            Log.e(Constants.TAG,​ "An exception has occurred: "​+ioException.getMessage());​ 
-            if (Constants.DEBUG) { 
-              ioException.printStackTrace();​ 
-            } 
-          } 
-        } 
-      }).start(); 
-    } 
-    ​ 
-    @Override 
-    public void run() { 
-      try { 
-        serverSocket = new ServerSocket(Constants.SERVER_PORT);​ 
-        while (isRunning) { 
-          Socket socket = serverSocket.accept();​ 
-          Log.v(Constants.TAG,​ "​Connection opened with "​+socket.getInetAddress()+":"​+socket.getLocalPort());​ 
-          PrintWriter printWriter = Utilities.getWriter(socket);​ 
-          printWriter.println(serverTextEditText.getText().toString());​ 
-          socket.close();​ 
-          Log.v(Constants.TAG,​ "​Connection closed"​);​ 
-        } 
-      } catch (IOException ioException) { 
-        Log.e(Constants.TAG,​ "An exception has occurred: "​+ioException.getMessage());​ 
-        if (Constants.DEBUG) { 
-          ioException.printStackTrace();​ 
-        } 
-      } 
-    } 
-  } 
-  ​ 
-  @Override 
-  protected void onCreate(Bundle savedInstanceState) { 
-    super.onCreate(savedInstanceState);​ 
-    setContentView(R.layout.activity_single_threaded);​  
-    ​ 
-    serverTextEditText = (EditText)findViewById(R.id.server_text_edit_text);​ 
-    serverTextEditText.addTextChangedListener(serverTextContentWatcher);​ 
-  } 
- 
-} 
-</​file>​ 
- 
-{{ :​laboratoare:​laborator06:​02single_threaded_server.png?​nolink&​400 }} 
- 
-Prin intermediul utilitarului ''​nc''​ (apelat cu adresa Internet și portul serverului) se poate interoga mesajul care a fost transmis. De asemenea, comanda ''​time''​ măsoară timpul în care a fost executată operația respectivă. 
- 
-<​code>​ 
-student@eim2016:​~$ nc 192.168.56.101 2016 
-Hello, EIM Student! 
-student@eim2016:​~$ time nc 192.168.56.101 2016 
-Hello, EIM Student! 
- 
-real    0m0.009s 
-user    0m0.000s 
-sys     ​0m0.000s 
-</​code>​ 
- 
-Fiecare comunicație dintre client și server este tratată secvențial,​ ceea ce poate determina latențe semnificative în cazul în care sunt înregistrate mai multe solicitări de acest tip concomitent. ​ 
- 
-De asemenea, în momentul în care coada în care sunt stocate este completată,​ o cerere poate fi refuzată. 
- 
-==== Exemplu 2. Server cu fire de execuție dedicate pentru fiecare canal de comunicație în parte ==== 
- 
-Pentru ca serverul să poată trata corespunzător solicitările venite de la clienți, fără a se înregistra latențe semnificative sau cereri refuzate, mai ales în situația în care traficul prin canalul de comunicație cu fiecare dintre aceștia este intens, se impune definirea mai multor fire de execuție: 
-  * un fir de execuție pe care serverul așteaptă invocările;​ 
-  * câte un fir de execuție pentru comunicația cu fiecare client în parte. 
- 
-{{ :​laboratoare:​laborator06:​scalabilitate_invocari_clienti.png?​nolink&​800 }} 
- 
-Astfel, atunci când este înregistrată o solicitare venită din partea unui client (metoda ''​accept()''​ furnizează un obiect ''​Socket''​ corespunzător canalului de comunicație),​ se creează un fir de execuție care va gestiona comunicația dintre client și server, fără ca aceasta să aibă un impact asupra capacității serverului de a răspunde la alte cereri, venite din partea altor clienți, și asupra comunicației dintre acestea. 
- 
-<code java> 
-private class CommunicationThread extends Thread { 
-  private Socket socket; 
-  ​ 
-  public CommunicationThread(Socket socket) { 
-    this.socket = socket; 
-  } 
-  ​ 
-  @Override 
-  public void run() { 
-    try { 
-      Log.v(Constants.TAG,​ "​Connection opened with "​+socket.getInetAddress()+":"​+socket.getLocalPort());​ 
-      PrintWriter printWriter = Utilities.getWriter(socket);​ 
-      printWriter.println(serverTextEditText.getText().toString());​ 
-      socket.close();​ 
-      Log.v(Constants.TAG,​ "​Connection closed"​);​ 
-    } catch (IOException ioException) { 
-      Log.e(Constants.TAG,​ "An exception has occurred: "​+ioException.getMessage());​ 
-      if (Constants.DEBUG) { 
-        ioException.printStackTrace();​ 
-      } 
-    } 
-  } 
-} 
- 
-private class ServerThread extends Thread { 
-  private boolean isRunning; 
-  ​ 
-  private ServerSocket serverSocket;​ 
-  ​ 
-  public void startServer() { 
-    isRunning = true; 
-    start(); 
-  } 
-  ​ 
-  public void stopServer() { 
-    isRunning = false; 
-    new Thread(new Runnable() { 
-      @Override 
-      public void run() { 
-        try { 
-          if (serverSocket != null) { 
-            serverSocket.close();​ 
-          } 
-          Log.v(Constants.TAG,​ "​stopServer() method invoked "​+serverSocket);​ 
-        } catch(IOException ioException) { 
-          Log.e(Constants.TAG,​ "An exception has occurred: "​+ioException.getMessage());​ 
-          if (Constants.DEBUG) { 
-            ioException.printStackTrace();​ 
-          } 
-        } 
-      } 
-    }).start(); 
-  } 
-  ​ 
-  @Override 
-  public void run() { 
-    try { 
-      serverSocket = new ServerSocket(Constants.SERVER_PORT);​ 
-      while (isRunning) { 
-        Socket socket = serverSocket.accept();​ 
-        new CommunicationThread(socket).start();​ 
-      } 
-    } catch (IOException ioException) { 
-      Log.e(Constants.TAG,​ "An exception has occurred: "​+ioException.getMessage());​ 
-      if (Constants.DEBUG) { 
-        ioException.printStackTrace();​ 
-      } 
-    } 
-  } 
-} 
-</​code>​ 
- 
-===== Activitate de Laborator ===== 
- 
-**1.** În contul Github personal, să se creeze un depozit denumit '​Laborator06'​. 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 [[https://​www.github.com/​eim2016/​Laborator06|]]. ​ 
- 
-În urma acestei operații, directorul Laborator06 va trebui să se conțină directoarele ''​labtasks'',​ ''​samples''​ și ''​solutions''​. ​ 
- 
-<​code>​ 
-student@eim2016:​~$ git clone https://​www.github.com/​eim2016/​Laborator06.git 
-</​code>​ 
- 
-**3.** Să se încarce conținutul descărcat în cadrul depozitului '​Laborator06'​ de pe contul Github personal. <​code>​ 
-student@eim2016:​~$ cd Laborator06 
-student@eim2016:​~/​Laborator06$ git remote add Laborator06_perfectstudent https://​github.com/​perfectstudent/​Laborator06 
-student@eim2016:​~/​Laborator06$ git push Laborator06_perfectstudent master 
-</​code>​ 
- 
-**4.** Să se importe în mediul integrat de dezvoltare preferat (Android Studio sau Eclipse) proiectul ''​FTPServerWelcomeMessage''​ din directorul ''​labtasks''​. 
- 
-Se cere să se implementeze o aplicație Android care citește mesajul de întâmpinare transmis în momentul în care se realizează o conexiune către un server FTP.  
- 
-Adresa Internet a serverului FTP la care se dorește să se realizeze conexiunea va fi introdusă de utilizator, iar portul curent este 21 (reținut în ''​Constants.FTP_PORT''​). 
- 
-În cazul în care mesajul de întâmpinare conține mai multe linii, mesajul este precedat de șirul de caractere ''​220-''​ (reținut în ''​Constants.FTP_MULTILINE_START_CODE''​). Mesajul se încheie fie cu șirul de caractere ''​220'',​ fie cu un șir de caractere precedat de ''​220 ''​ (valori stocate în ''​Constants.FTP_MULTILINE_END_CODE1'',​ respectiv ''​Constants.FTP_MULTILINE_END_CODE2''​). 
- 
-{{ :​laboratoare:​laborator06:​03ftp_server_welcome_message.png?​nolink&​400 }} 
- 
-Operațiile care trebuie realizate pe metoda ''​doInBackground()''​ a firului de execuție ce gestionează comunicația cu serverul FTP sunt: 
-  - deschiderea unui socket care primește ca parametrii: 
-    - adresa Internet a serverului FTP (precizată anterior de utilizator, preluată din câmpul text ''​FTPServerAddressEditText'',​ transmis ca parametru al metodei - ''​params[0]''​) 
-    - portul 21 (preluat din ''​Constants.FTP_PORT''​). 
-  - obținerea fluxului de intrare atașat socket-ului,​ printr-un apel al metodei ''​Utilities.getBufferedReader()'';​ 
-  - citirea unei linii de pe fluxul de intrare: 
-    - dacă aceasta începe cu șirul de caractere ''​220-''​ (preluat din ''​Constants.FTP_MULTILINE_START_CODE''​),​ mesajul este analizat; 
-      - se citesc linii de pe fluxul de intrare atașat socket-ului cât timp valoarea: 
-        - nu este egală cu ''​220''​ (preluat din ''​Constants.FTP_MULTILINE_END_CODE1''​);​ 
-        - nu începe cu ''​220 ''​ (preluat din ''​Constants.FTP_MULTILINE_END_CODE2''​);​ 
-      - valoarea primită de la serverul FTP este inclusă în câmpul text ''​welcomeMEssageTextView''​ (informația se transmite, pe măsură ce este primită, prin metoda ''​publishProgress()''​ care face să se invoce automat metoda de callback ''​onProgressUpdate()''​ executată pe firul de execuție principal al aplicației Android, la care se oferă acces la controalele grafice); 
-    - altfel, mesajul este ignorat. 
-  - închiderea socket-ului. 
- 
-Să se verifice mesajul afișat în momentul în care se realizează o conexiune la serverul [[ftp.ngc.com|]]. 
- 
-**5.** Să se importe în mediul integrat de dezvoltare preferat (Android Studio sau Eclipse) proiectul ''​SingleThreadedServer''​ din directorul ''​labtasks''​. 
- 
-Acesta reprezintă o aplicație Android care implementează un server ce ascultă pe un port (2016, î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. 
- 
-<​code>​ 
-student@eim2016:​~$ nc 192.168.56.101 2016 
-Hello, EIM Student! 
-student@eim2016:​~$ time nc 192.168.56.101 2016 
-Hello, EIM Student! 
- 
-real    0m0.009s 
-user    0m0.000s 
-sys     ​0m0.000s 
-</​code>​ 
- 
-==== 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//. 
- 
-{{ :​laboratoare:​laborator06:​telnet01_windows81.png?​nolink&​300 }} 
- 
-{{ :​laboratoare:​laborator06:​telnet02_windows81.png?​nolink&​600 }} 
- 
-{{ :​laboratoare:​laborator06:​telnet03_windows81.png?​nolink }} 
- 
-{{ :​laboratoare:​laborator06:​telnet04_windows81.png?​nolink&​400 }} 
- 
-{{ :​laboratoare:​laborator06:​telnet05_windows81.png?​nolink&​400 }} 
- 
-<​code>​ 
-C:​\Users\Student>​ telnet 192.168.56.101 2016 
-Start Server 
- 
-Connection to host lost. 
-C:​\Users\Student>​ telnet 192.168.56.101 2016 
-Connecting To 192.168.56.101...Could not open connection to the host, on port 2016: Connect failed 
-</​code>​ 
- 
-===== ===== 
- 
-**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ă? 
- 
-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. 
- 
-<​code>​ 
-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) 
-</​code>​ 
- 
-O soluție ar putea fi eliberarea resurselor pe metoda ''​onDestroy()''​. 
- 
-<code java> 
-@Override 
-public void onDestroy() { 
-  super.onDestroy();​ 
-  if (serverThread != null) { 
-    serverThread.stopServer();​ 
-  } 
-} 
-</​code>​ 
- 
-**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. 
- 
-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. 
- 
-<code java> 
-try { 
-  Thread.sleep(3000);​ 
-} catch (InterruptedException interruptedException) { 
-  Log.e(Constants.TAG,​ interruptedException.getMessage());​ 
-  if (Constants.DEBUG) { 
-    interruptedException.printStackTrace();​ 
-  } 
-} 
-</​code>​ 
- 
-**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ă. 
- 
-**6.** Să se importe în mediul integrat de dezvoltare preferat (Android Studio sau Eclipse) 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;​ 
-  - 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. 
- 
-{{ :​laboratoare:​laborator06:​04client_server_communication.png?​nolink&​400 }} 
- 
-**a)** Să se implementeze funcționalitatea serverului. 
- 
-În clasa ''​ComunicationThread''​ din fragmentul asociat serverului: 
-  - 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''​ din fragmentul asociat serverului: 
-  - 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()'';​ 
-  - 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()'';​ 
-  - 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 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()'';​ 
-  - se închide socketul. 
- 
-<note important>​Accesul la controalele grafice trebuie să fie realizat numai din contextul firului de execuție asociat interfeței cu utilizatorul.</​note>​ 
- 
-**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''​ / ''​2016'',​ respectiv ''​localhost''​ / ''​2016''​. ​ 
- 
-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 2016, serverul fiind astfel simulat pe mașina fizică. 
- 
-<​code>​ 
-student@eim2016:​~$ ps a | while read x; do echo "​$x"​ | nc -l 2016; done 
-</​code>​ 
- 
-<note tip>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.</​note>​ 
- 
-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 2016. 
- 
-{{ :​laboratoare:​laborator06:​05client_server_communication.png?​nolink&​400 }} 
- 
-**7.** Să se importe în mediul integrat de dezvoltare preferat (Android Studio sau Eclipse) proiectul ''​PheasantGame''​ din directorul ''​labtasks''​. 
- 
-Acesta reprezintă o aplicație Android care implementează jocul Fazan în limba engleză, atât partea de server, cât și partea de client. 
- 
-Vor fi folosite următoarele **reguli de joc**: 
-  - clientul trimite un cuvânt serverului; 
-  - serverul verifică dacă nu s-a solicitat terminarea jocului, situație în care continuă analiza termenului respectiv; 
-  - dacă s-a primit un cuvânt care are prefixul așteptat, termenul existând în limba engleză, se caută un alt cuvânt care începe cu ultimele 2 litere ale termenului: 
-    - dacă există un astfel de cuvânt, acesta fiind transmis înapoi clientului; 
-    - altfel, se semnalează faptul că jocul s-a terminat; 
-  - altfel, se trimite înapoi cuvântul primit pentru a semnala faptul că există o problemă. 
- 
-{{ :​laboratoare:​laborator06:​06pheasant_game.png?​nolink&​400 }} 
- 
-Astfel, serverul va avea următoarea funcționare:​ 
-  - dacă se primește //End Game// (stocat în ''​Constants.END_GAME''​),​ pentru a se indica faptul că nu se mai dorește continuarea jocului sau clientul a fost închis (nu găsește nici un cuvânt cu prefixul solicitat), serverul își termină execuția; 
-  - altfel, serverul verifică dacă termenul care a fost primit reprezintă un cuvânt valid în limba engleză (accesând în acest sens pagina Internet [[http://​www.wordhippo.com/​what-is/​words-starting-with/​|]]) - se folosește metoda ''​Utilities.wordValidation()'';​ 
-    - dacă termenul este valid: 
-      - verifică dacă termenul începe cu prefixul solicitat (ultimele 2 litere ale cuvântului pe care l-a trimis anterior) - în acest sens, trebuie reținut cel mai recent cuvânt transmis / prefixul așteptat; 
-        - în caz afirmativ: 
-          - identifică prefixul cuvântului pe care trebuie să îl formeze (ultimele 2 litere ale cuvântului primit); 
-          - realizează o interogare la nivelul paginii Internet [[http://​www.wordhippo.com/​what-is/​words-starting-with/​|]] pentru a obține lista cuvintelor care respectă constrângerea respectivă - se folosește metoda ''​Utilities.getWordListStartingWith()''​ care furnizează un obiect ''​List<​String>'';​ 
-            - dacă lista conține unul sau mai multe cuvinte, face o alegere întâmplătoare și se transmite clientului; 
-            - dacă lista nu conține nici un cuvânt, transmite clientului șirul de caractere //End Game// (stocat în ''​Constants.END_GAME''​),​ pentru a indica faptul că a fost închis (nu găsește nici un cuvânt cu prefixul solicitat); 
-        - în caz negativ, transmite înapoi cuvântul pe care l-a primit; 
-    - dacă termenul nu este valid, trimite înapoi cuvântul pe care l-a primit. 
- 
-<note important>​Este posibil ca un cuvânt care există în limba engleză să nu fie marcat ca valid, datorită faptului că se inspectează numai prima pagină dintre cele care conțin lista de cuvinte precedate de un anumit prefix.</​note>​ 
- 
-Similar, clientul va avea următoarea funcționare:​ 
-  - dacă termenul din câmpul text ''​wordEditText''​ are mai mult de două caractere, este trimis clientului; 
-  - se primește o valoare de la server: 
-    - dacă se primește //End Game//, se invalidează controalele grafice, astfel încât jocul să nu poată fi continuat; 
-    - dacă s-a primit alt cuvânt decât cel trimis anterior, se determină prefixul cu care trebuie format termenul și se afișează în câmpul text ''​wordEditText'';​ 
-    - dacă s-a primit același cuvânt cu cel trimis anterior, înseamnă că termenul nu a fost corect, astfel încât trebuie format un cuvânt cu același prefix ca anterior, acesta fiind afișat în câmpul text ''​wordEdiTtext'';​ 
-  - altfel, se afișează un mesaj de eroare. 
- 
-Informațiile cu privire la șirurile de caractere care au fost trimise, respectiv primite, vor fi jurnalizate în câmpurile text ''​serverHistoryTextView''​ și ''​clientHistoryTextView''​. 
- 
-**8.** ​ Să se încarce modificările realizate în cadrul depozitului '​Laborator06'​ de pe contul Github personal, folosind un mesaj sugestiv. <​code>​ 
-student@eim2016:​~/​Laborator06$ git add * 
-student@eim2016:​~/​Laborator06$ git commit -m "​implemented taks for laboratory 06" 
-student@eim2016:​~/​Laborator06$ git push Laborator02_perfectstudent master 
-</​code>​ 
- 
-===== Resurse Utile ===== 
- 
-[[http://​developer.android.com/​training/​basics/​network-ops/​connecting.html|Connecting to the Network]]\\ ​ 
-[[http://​developer.android.com/​training/​basics/​network-ops/​managing.html|Managing Network Usage]]\\ ​ 
-[[http://​eu.wiley.com/​WileyCDA/​WileyTitle/​productCd-1118199545.html|Wei Meng LEE, Beginning Android 4 Application Development,​ Wiley, 2012]] - capitolul 10, //​Networking//,​ subcapitolul //How to connect to a Socket server//​\\ ​ 
-[[http://​www.amazon.com/​Professional-Android-4-Application-Development/​dp/​1118102274|Reto MEIER, Professional Android for Application Development,​ John Wiley & Sons, 2012]] - capitolul 16 (//​Bluetooth,​ NFC, Networks and Wi-Fi//), subcapitolele //Managing Network and Internet Connectivity//,​ //​Transferring Data using Wi-Fi Direct//​\\ ​ 
-[[http://​www.coreservlets.com/​android-tutorial/​|Android Programming Tutorials - Core Servlets]] - secțiunea //​Networking I: General Techniques//​\\ ​ 
-[[http://​android.rosedu.org/​wiki/​laborator-07-servicii-de-retea#​socketi|Dezvoltarea Aplicațiilor pentru Android]] - partea a II-a, Sockeți\\ ​ 
-[[http://​developer.android.com/​guide/​components/​processes-and-threads.html|Processes and Threads]]\\ ​ 
-[[http://​developer.android.com/​reference/​java/​net/​ServerSocket.html|ServerSocket]]\\ ​ 
-[[http://​developer.android.com/​reference/​java/​net/​Socket.html|Socket]] 
laboratoare/laborator06.txt · Last modified: 2017/02/20 20:54 (external edit)
CC Attribution-Share Alike 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0