Diferență între revizuiri ale paginii „Socket-uri de rețea”
(Pagină nouă: Limbajul Java, prin setul de biblioteci API, pune la dispoziție suport nativ pentru aplicații în rețea. Cel mai simplu mod de a realiza comunicația între două mașini este prin...) |
|||
(Nu s-au afișat 13 versiuni intermediare efectuate de același utilizator) | |||
Linia 17: | Linia 17: | ||
În cazul socket-urilor conectate, se stabilește mai întâi o legătură între două socket-uri aparținînd unor procese distincte (posibil plasate în stații diferite, conectate printr-o rețea de comunicație). Transmisia pe socluri conectate folosește protocolul TCP ([http://en.wikipedia.org/wiki/Transmission_Control_Protocol Transmission Control Protocol]) care este un protocol fiabil (cu verificări ale datelor transmise şi recepționate), spre deosebire de protocolul UDP care este un protocol nefiabil. | În cazul socket-urilor conectate, se stabilește mai întâi o legătură între două socket-uri aparținînd unor procese distincte (posibil plasate în stații diferite, conectate printr-o rețea de comunicație). Transmisia pe socluri conectate folosește protocolul TCP ([http://en.wikipedia.org/wiki/Transmission_Control_Protocol Transmission Control Protocol]) care este un protocol fiabil (cu verificări ale datelor transmise şi recepționate), spre deosebire de protocolul UDP care este un protocol nefiabil. | ||
+ | |||
+ | Pentru ca mai multe aplicații să poată avea acces la rețea în același timp, pachetele pentru fiecare aplicație trebuie să fie separate. Acest lucru se face cu ajutorul [http://en.wikipedia.org/wiki/Port_number porturilor de comunicații]. Un port este reprezentat de un număr pe 16 biți și fiecare aplicație folosește un port unic pentru a comunica în rețea. O conexiune are două porturi: cel local, alocat de sistemul de operare la inițierea conexiunii, și cel al server-ului, care este constant, în funcție de aplicație. Fiecare protocol standard are asociat un port conform unei liste menținute de cei de la [http://en.wikipedia.org/wiki/Internet_Assigned_Numbers_Authority Internet Assigned Numbers Authority]. Ca exemple: | ||
+ | * port 20 - File Transfer Protocol (date) | ||
+ | * port 21 - File Transfer Protocol (control) | ||
+ | * port 21 - Secure Shell (SSH) | ||
+ | * port 25 - Simple Mail Transfer Protocol (SMTP) | ||
+ | * port 80 - Hyper Text Transfer Protocol (HTTP) | ||
+ | * etc. | ||
+ | |||
+ | [http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers Lista completă] este disponibilă pe http://www.wikipedia.org. | ||
+ | |||
+ | Într-un sistem de operare Linux, porturile 0 - 1023 sunt rezervate de kernel și nu sunt accesibile unui utilizator. Din Linux, pentru a verifica ce porturi sunt deschise (utilizate), se poate folosi comanda ''nmap'' (daca este instalată): | ||
+ | <syntaxhighlight lang="bash"> | ||
+ | nmap arh.pub.ro | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == Socket de client == | ||
+ | |||
+ | În general, o conexiunea de rețea se realizează dinspre o stație (host) numită ''client'', către o altă stație, numită ''server''. Pentru ca un client să se poată conecta la un server, are nevoie de două informații: adresa serverului și portul pe care ascultă aplicația la care se dorește conectarea. Clasa care realizează acest lucru în Java se numește [http://docs.oracle.com/javase/7/docs/api/java/net/Socket.html java.net.Socket]. Orice obiect de tip '''Socket''', după instanțiere, are asociate două ''stream''-uri: | ||
+ | * un '''InputStream''' de pe care se pot citi date venite de la celălalt capăt al conexiunii, accesibil cu metoda '''Socket.getInputStream()'''; | ||
+ | * un '''OutputStream''' pe care se pot scrie date pentru a fi trimise la celălalt capăt al conexiunii, accesibil cu metoda '''Socket.getOutputStream()'''. | ||
+ | |||
+ | <syntaxhighlight lang="java"> | ||
+ | import java.net.*; | ||
+ | import java.io.*; | ||
+ | |||
+ | public class Client{ | ||
+ | |||
+ | public static void main(String[] _args){ | ||
+ | try{ | ||
+ | //connect to arh.pub.ro on the HTTP port | ||
+ | Socket _clientSocket = new Socket("arh.pub.ro", 80); | ||
+ | OutputStream _outputStream = _clientSocket.getOutputStream(); | ||
+ | InputStream _inputStream = _clientSocket.getInputStream(); | ||
+ | |||
+ | _outputStream.write("GET /java.txt\n".getBytes()); | ||
+ | |||
+ | int _char; | ||
+ | while((_char = _inputStream.read()) != -1){ | ||
+ | System.out.print((char)_char); | ||
+ | } | ||
+ | |||
+ | _clientSocket.close(); | ||
+ | }catch(IOException _ioe){ | ||
+ | System.out.println("Communication problem: " + _ioe.getMessage()); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == Socket de server == | ||
+ | |||
+ | Rolul unui socket de server este de a accepta conexiuni pe la unul sau mai multi clienți. Clasa care implementează acest comportament este [http://docs.oracle.com/javase/7/docs/api/java/net/ServerSocket.html java.net.ServerSocket]. Pentru ca un '''ServerSocket''' să poată funcționa, are nevoie de o singură informație, și anume portul pe care să accepte conexiuni. Pentru a efectua o conectare concretă, clasa conține o metodă numită ''accept()''. | ||
+ | |||
+ | <div class="regula"><font color="#ff0000">Atenție:</font> Metoda ''ServerSocket.accept()'' este blocantă! Asta implică faptul că odată intrat în metodă, programul se blochează până în momentul în care un client încearcă să se conecteze. În acel moment abia, metoda ''accept()'' întoarce un obiect de tip '''Socket''' asociat acelei conexiuni. În același timp, un client nu se poate conecta atâta timp cât serverul nu este blocat în metoda ''accept()''.</div> | ||
+ | |||
+ | |||
+ | <syntaxhighlight lang="java"> | ||
+ | import java.net.*; | ||
+ | import java.io.*; | ||
+ | |||
+ | public class Server{ | ||
+ | |||
+ | public static void main(String[] _args){ | ||
+ | try{ | ||
+ | // start a new server on port 9000 (must be higher or equal to 1024) | ||
+ | ServerSocket _serverSocket = new ServerSocket(9000); | ||
+ | |||
+ | // listen to an incoming connection and creating a communication socket | ||
+ | // accept() is blocking | ||
+ | System.out.print("Listening for inc. connections... "); | ||
+ | Socket _socket = _serverSocket.accept(); | ||
+ | System.out.println("connected!"); | ||
+ | |||
+ | // get the streams associated with the socket | ||
+ | // output one always first | ||
+ | OutputStream _outputStream = _socket.getOutputStream(); | ||
+ | InputStream _inputStream = _socket.getInputStream(); | ||
+ | |||
+ | // read the input char by char until newline char (\n) | ||
+ | // we could do the same thing easier using an InputStreamReader | ||
+ | // and a BufferedReader | ||
+ | String _command = ""; | ||
+ | char _char; | ||
+ | while((_char = (char)_inputStream.read()) != '\n'){ | ||
+ | _command = _command + _char; | ||
+ | } | ||
+ | |||
+ | // send a response depending on the received string | ||
+ | if(_command.equals("GET /java.txt")){ | ||
+ | _outputStream.write("Congratulations, you have received a message from a Java server!\n".getBytes()); | ||
+ | }else{ | ||
+ | _outputStream.write("Congratulations, you still received a message from a Java server, even though you sent the wrong text!\n".getBytes()); | ||
+ | } | ||
+ | |||
+ | _socket.close(); | ||
+ | _serverSocket.close(); | ||
+ | }catch(IOException _ioe){ | ||
+ | System.out.println("Communication problem: " + _ioe.getMessage()); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Puteți verifica funcționarea acestui server modificând clientul pentru a se conecta la "localhost", pe portul 9000 și rulând ambele programe pe același calculator, în console diferite. | ||
+ | |||
+ | Pentru a putea programa un server care să administreze mai mulți clienți simultan, trebuie obligatoriu create mai multe fire de execuție (thread-uri). |
Versiunea curentă din 24 august 2012 10:19
Limbajul Java, prin setul de biblioteci API, pune la dispoziție suport nativ pentru aplicații în rețea. Cel mai simplu mod de a realiza comunicația între două mașini este prin utilizarea socket-urilor (soclurilor) de comunicație. Socket-urile sunt mecanisme de comunicație în rețea (dezvoltate la Universitatea Berckley din California) care pot folosi orice protocol de comunicație, deși, în general, se folosesc protocoalele din stiva TCP/IP.
Orice calculator conectat la Internet trebuie să aibă o adresă prin care să fie identificat, adresă care se numește adresă IP (Internet Protocol). O adresă IPv4 (versiunea 4) este formată din 4 octeți, iar reprezentarea ei externă se face printr-o secvență de 4 numere separate prin puncte (ex: 192.168.0.1). Pe lângă această reprezentare, se mai pot folosi nume simbolice grupate în domenii, iar corespondențele dintre adresele IP și numele simbolice sunt stocate într-o bază de date distribuită, accesată prin serviciul numelor de domenii (DNS – Domain Name Service).
În Java, clasele și interfețele de programare în rețea sunt cuprinse în pachetul java.net. O adresă IP se reprezintă prin clasa InetAddress care conține atât adresa IP (în format numeric) cât și adresa simbolică corespunzătoare. Această clasă nu are un constructor public, un obiect din această clasă se poate crea la apelul uneia din metodele statice ale clasei: getByName(), getLocalHost() sau getAllByName() care retunează referința la obiectul creat. De exemplu, prototipul metodei getByName() este următorul:
public InetAddress getByName (String host) throws UnknownHostException;
Argumentul metodei (host) este un șir de caractere care reprezintă numele stației dat ca nume simbolic (de exemplu "arh.pub.ro") sau ca formă textuală a adresei IP ("141.85.252.94").
Socket-urile se pot folosi într-unul din două moduri posibile:
- socluri neconectate;
- socluri conectate (orientate pe conexiune).
În cazul soclurilor neconectate, se transmit pachete în protocolul UDP (User Datagram Protocol) între procese între care nu există o legătură permanentă, iar pachetele de date (datagramele - datagrams) sunt independente între ele și conțin adresa destinației pentru a putea fi rutate.
În cazul socket-urilor conectate, se stabilește mai întâi o legătură între două socket-uri aparținînd unor procese distincte (posibil plasate în stații diferite, conectate printr-o rețea de comunicație). Transmisia pe socluri conectate folosește protocolul TCP (Transmission Control Protocol) care este un protocol fiabil (cu verificări ale datelor transmise şi recepționate), spre deosebire de protocolul UDP care este un protocol nefiabil.
Pentru ca mai multe aplicații să poată avea acces la rețea în același timp, pachetele pentru fiecare aplicație trebuie să fie separate. Acest lucru se face cu ajutorul porturilor de comunicații. Un port este reprezentat de un număr pe 16 biți și fiecare aplicație folosește un port unic pentru a comunica în rețea. O conexiune are două porturi: cel local, alocat de sistemul de operare la inițierea conexiunii, și cel al server-ului, care este constant, în funcție de aplicație. Fiecare protocol standard are asociat un port conform unei liste menținute de cei de la Internet Assigned Numbers Authority. Ca exemple:
- port 20 - File Transfer Protocol (date)
- port 21 - File Transfer Protocol (control)
- port 21 - Secure Shell (SSH)
- port 25 - Simple Mail Transfer Protocol (SMTP)
- port 80 - Hyper Text Transfer Protocol (HTTP)
- etc.
Lista completă este disponibilă pe http://www.wikipedia.org.
Într-un sistem de operare Linux, porturile 0 - 1023 sunt rezervate de kernel și nu sunt accesibile unui utilizator. Din Linux, pentru a verifica ce porturi sunt deschise (utilizate), se poate folosi comanda nmap (daca este instalată):
nmap arh.pub.ro
Socket de client
În general, o conexiunea de rețea se realizează dinspre o stație (host) numită client, către o altă stație, numită server. Pentru ca un client să se poată conecta la un server, are nevoie de două informații: adresa serverului și portul pe care ascultă aplicația la care se dorește conectarea. Clasa care realizează acest lucru în Java se numește java.net.Socket. Orice obiect de tip Socket, după instanțiere, are asociate două stream-uri:
- un InputStream de pe care se pot citi date venite de la celălalt capăt al conexiunii, accesibil cu metoda Socket.getInputStream();
- un OutputStream pe care se pot scrie date pentru a fi trimise la celălalt capăt al conexiunii, accesibil cu metoda Socket.getOutputStream().
import java.net.*;
import java.io.*;
public class Client{
public static void main(String[] _args){
try{
//connect to arh.pub.ro on the HTTP port
Socket _clientSocket = new Socket("arh.pub.ro", 80);
OutputStream _outputStream = _clientSocket.getOutputStream();
InputStream _inputStream = _clientSocket.getInputStream();
_outputStream.write("GET /java.txt\n".getBytes());
int _char;
while((_char = _inputStream.read()) != -1){
System.out.print((char)_char);
}
_clientSocket.close();
}catch(IOException _ioe){
System.out.println("Communication problem: " + _ioe.getMessage());
}
}
}
Socket de server
Rolul unui socket de server este de a accepta conexiuni pe la unul sau mai multi clienți. Clasa care implementează acest comportament este java.net.ServerSocket. Pentru ca un ServerSocket să poată funcționa, are nevoie de o singură informație, și anume portul pe care să accepte conexiuni. Pentru a efectua o conectare concretă, clasa conține o metodă numită accept().
import java.net.*;
import java.io.*;
public class Server{
public static void main(String[] _args){
try{
// start a new server on port 9000 (must be higher or equal to 1024)
ServerSocket _serverSocket = new ServerSocket(9000);
// listen to an incoming connection and creating a communication socket
// accept() is blocking
System.out.print("Listening for inc. connections... ");
Socket _socket = _serverSocket.accept();
System.out.println("connected!");
// get the streams associated with the socket
// output one always first
OutputStream _outputStream = _socket.getOutputStream();
InputStream _inputStream = _socket.getInputStream();
// read the input char by char until newline char (\n)
// we could do the same thing easier using an InputStreamReader
// and a BufferedReader
String _command = "";
char _char;
while((_char = (char)_inputStream.read()) != '\n'){
_command = _command + _char;
}
// send a response depending on the received string
if(_command.equals("GET /java.txt")){
_outputStream.write("Congratulations, you have received a message from a Java server!\n".getBytes());
}else{
_outputStream.write("Congratulations, you still received a message from a Java server, even though you sent the wrong text!\n".getBytes());
}
_socket.close();
_serverSocket.close();
}catch(IOException _ioe){
System.out.println("Communication problem: " + _ioe.getMessage());
}
}
}
Puteți verifica funcționarea acestui server modificând clientul pentru a se conecta la "localhost", pe portul 9000 și rulând ambele programe pe același calculator, în console diferite.
Pentru a putea programa un server care să administreze mai mulți clienți simultan, trebuie obligatoriu create mai multe fire de execuție (thread-uri).