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

String hostname = "localhost";
int port = 2000;
Socket socket = new Socket(hostname, port);

2. crearea unui output stream pentru a trimite date prin intermediul socket-ului TCP și trimiterea efectivă a datelor:

BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
PrintWriter printWriter = new PrintWriter(bufferedOutputStrea, true);

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).

Metodele implementate de clasa 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:

Trimiterea efectivă a datelor este realizată atunci când se apelează metoda flush() sau automat la întâlnirea caracterului '\n', în funcție de argumentele cu care a fost invocat constructorul.

3. crearea unui flux de intrare pentru a primi date prin intermediul socket-ului TCP și primirea efectivă a datelor:

InputStreamReader inputStreamReader = new InputStreamReader(socket.getInputStream());
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

Metodele implementate de clasa 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.

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

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).


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:

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);
  }

}

4. închiderea obiectului Socket, atunci când acesta nu mai este necesar

socket.close();

În momentul în care se apelează metoda close(), sunt transmise și datele care erau stocate în zonele de memorie tampon.