
Un soclu este de fapt un nod abstract de comunicatie. Soclurile reprezinta o interfata de nivel scazut pentru comunicarea in retea. Soclurile permit comunicarea intre procese aflate pe acelasi calculator sau pe calculatoare diferite din retea. Mecanismul de socluri a fost definit prima data in BSD UNIX. Java suporta trei tipuri de socluri. Clasa Socket utilizeaza un protocol orientat pe conexiune (TCP), clasa DatagramSocket utilizeaza protocolul UDP la nivelul transport, care este un protocol neorientat pe conexiune. O alta varianta a DatagramSocket este MulticastSocket utilizat pentru a trimite date deodata la mai multi receptori. Soclurile utilizeaza fluxuri de date (streamuri) pentru a trimite si a receptiona mesaje.

Clientul trebuie sa cunoasca doua lucruri despre server: numele
serverului (utilizat pentru determinarea adresei IP al serverului) si numarul
portului la care acesta asculta cererile clientilor. Acelasi calculator
gazda poate oferi mai multe servicii, deci poate gazdui mai multe procese
de tip server. De exemplu poate fi server Mail, server FTP, server HTTP,
dar aceste aplicatii lucreaza cu diferite porturi, deci cererile adresate
acestor serveri vor fi receptionate pe diferite porturi.
Privind modalitatea de deservire a clientilor serverul prin constructie
poate fi server paralel (concurent) si server secvential.
Serverul concurent permite deservirea in paralel a mai multor clienti.
Aceasta paralelitate se poate realiza prin crearea a mai multor procese
fiu, cate unul pentru fiecare client sau prin crearea de fire de executie
pentru deservirea fiecarui clienti In Java noi vom utiliza cea de a doua
modalitate. De obicei utilizam server cu arhitectura paralela pentru servicii
la care deservirea unui client dureaza in timp.
In general o aplicatie server trebuie sa execute urmatorii pasi:
1. Serverul aloca un port de comunicare care va fi cunoscut de catre clientiO aplicatie client executa urmatoarele:
2. Cat timp ( mai sunt clienti la port )
{
stabilire conexiune cu client ( creare soclu )
deservire client ( flux de intrare, flux de iesire )
eliberare conexiune ( eliberare soclu )
}
1. Aloca un port de comunicare
2. Se conecteaza la server la portul cunoscut de dinainte
3. Se stabileste o conexiune prin care se trimit si se citesc date (soclu + fluxuri)
Socket(InetAddress address, int port, InetAddress localAddr, int localPort)
Socket(String host, int port)Creaza un soclu si care se conecteaza la adresa IP specificat prin obiectul InetAddress si portul specificat prin parametrul port. Ultimii doi parametri reprezinta adresa clientului si portul pe care acesta comunica .
Socket(String
host, int port, InetAddress localAddr, int localPort)
Creaza un soclu si care se conecteaza la calculatorul host pe portul specificat
prin parametrul port. Ultimii doi parametri reprezinta adresa clientului
si portul pe care acesta comunica
Metode principale:
void close()Inchide soclul.InetAddress getInetAddress()Returneaza adresa la care soclul este conectatInputStream getInputStream()Returneaza un stream de intrare pentru soclu.
InetAddress getLocalAddress()Returneaza adresa pe care soclul este creat.
int getLocalPort()Returneaza numarul portului local.
OutputStream getOutputStream()Returneza un stream de iesire pentru soclu.int getPort()
Returneaza portul la care soclul este conectat.
ServerSocket(int port)Metode principale:
Creaza un soclu server pe portul specificat.ServerSocket(int port, int backlog)
Creaza un soclu server pe portul specificat. Al doilea parametru indica lungimea cozii de asteptare.
Socket accept()Asculta conexiunile si le accepta.
void close()Inchide soclul.
InetAddress getInetAddress()Returneaza adresa locala al soclului server.
int getLocalPort()Returneaza portul la care serverul asteapta conexiunile.
Pentru rezolvarea acestei probleme construim doua pachete:
Incepen cu descrierea serverului. Serverul are o structura paralela,
permite deservirea a mai multor clienti. Pentru fiecare client se va crea
un obiect de tip ClientHanedle, un obiect care se ruleaza pe un fir de
executie separat si realizeaza deservirea clientului.
//Source file: c:\Rose\ClientServer\ServerPackage\Server.javapackage ServerPackage;
public class Server
{
public static void main(String[] args)
{
int port;
if( args.length <1 )
{
System.out.println( "Usage Server <portnumber>");
System.exit( 1 );
}
try{
port = Integer.parseInt( args[ 0 ] );
}
catch( NumberFormatException e )
{
port = 1024;
}
try{
java.net.ServerSocket ss = new java.net.ServerSocket( port );
while( true )
( new ClientHandle( ss.accept() )).start();
}
catch( java.io.IOException e )
{
System.out.println( "Server error ");
}
}
}//Source file: c:\Rose\ClientServer\ServerPackage\ClientHandle.java
package ServerPackage;
import java.net.Socket;
import java.net.SocketException;public class ClientHandle extends Thread
{
Socket client;public ClientHandle(Socket client) throws SocketException
{
this.client = client;
}public void run()
{
try{
java.io.InputStream in = client.getInputStream();
java.io.OutputStream out = client.getOutputStream();
java.io.BufferedReader myin = new java.io.BufferedReader(
new java.io.InputStreamReader( in));
java.io.PrintWriter myout = new java.io.PrintWriter( new
java.io.OutputStreamWriter( out ));
String request = myin.readLine();
System.out.println( "Request: "+request);
myout.println( request.toUpperCase());
myout.flush();
myin.close();
myout.close();
client.close();
}
catch( java.io.IOException e )
{
System.out.println("I/O error "+e );
}
}
}
//Source file: c:\Rose\ClientServer\ClientPackage\Client.javapackage ClientPackage;
public class Client
{
private String hostname;
private int hostport;
private String message;public Client(String hostname, int hostport, String message)
{
this.hostname = hostname;
this.hostport = hostport;
this.message = message;
doIt();
}public void doIt()
{
try{
java.net.Socket s = new java.net.Socket( hostname, hostport );
java.io.BufferedReader in = new java.io.BufferedReader( new
java.io.InputStreamReader(s.getInputStream()));
java.io.PrintWriter out = new java.io.PrintWriter( s.getOutputStream() );
out.println( message ); out.flush();
System.out.println("Answer from the server: "+in.readLine() );
s.close();
}
catch( java.io.IOException e )
{
System.out.println( "I/O error "+e );
}
}
public static void main(String[] args)
{
String hostname, message;
int hostport;
if( args.length < 3 )
{
System.out.println("Usage Client <hostname> <hostport> <message>");
System.exit( 1 );
}hostname = args[ 0 ];
message = args[ 2 ];
try{
hostport =Integer.parseInt( args[ 1 ] );
}
catch( NumberFormatException e )
{
hostport = 1024;
}
Client c = new Client( hostname, hostport, message );
}
}
import java.io.*;Utilizarea aplicatiei:
import java.net.*;
import java.util.*;public class TinyHttpd{
public static void main ( String args[] ) throws IOException
{ServerSocket ss = new ServerSocket( Integer.parseInt( args[0]));
while( true )
{
Socket s = ss.accept();
TinyHttpdConnection t = new TinyHttpdConnection( s );
t.start();
}
}
}
class TinyHttpdConnection extends Thread{
Socket client;
TinyHttpdConnection ( Socket client) throws SocketException{
this.client = client;
setPriority(NORM_PRIORITY -1);
}public void run(){
try{
BufferedReader in = new BufferedReader(
new InputStreamReader(client.getInputStream(),"8859_1"));
OutputStream out = client.getOutputStream();
PrintWriter pout = new PrintWriter(
new OutputStreamWriter(out,"8859_1"),true);
String request = in.readLine();
System.out.println("Request: "+request);
StringTokenizer st = new StringTokenizer( request );
if( (st.countTokens()>=2) && st.nextToken().equals("GET"))
{
if( (request = st.nextToken()).startsWith("/"))
request=request.substring( 1 );
if( request.endsWith("/") || request.equals(""))
request=request+"index.html";
try{
FileInputStream fis = new FileInputStream( request );
byte[] data = new byte[ fis.available()];
fis.read(data);
out.write(data);
out.flush();
}
catch( FileNotFoundException e ){
pout.println(e.toString());
}
}
else{
pout.println("400 Bad request");
}
client.close();
}
catch( IOException e )
{
System.out.println(e.toString());
}
}
}
java TinyHttpd 1234.
Numarul 1234 reprezinta portul la care serverul asteapta cererile. Dupa ce serverul este lansat in executie, dintr-un program de navigare (browser) se formuleaza o cerere de forma: http://127.0.0.1:1234/Index.html, daca exista in catalogul aplicatiei TyniHttpd fisierul Index.html si daca clientul si serverul se ruleaza pe acelasi calculator. 127.0.0.1 este adresa IP al localhost-ului. Daca aplicatia se ruleaza pe un calculator cu numele unu.doi.com atunci in locul adresei 127.0.0.1 se pune unu.doi.com.
Descrierea aplicatiei:
Aplicatia defineste doua clase. O clasa care rezolva o cerere pe un
fir de executie, TinyHttpdConnection si o clasa TinyHttpd care creaza soclul
server si intra in asteptarea clientilor. In momentul aparitiei unui client
( accept()) se creaza un nou fir de executie caruia se transmite
referinta la soclul returnat de catre accept(). Aceasta referinta la soclu
permite firului de executie sa extraga de aici fluxurile de intrare si
de iesire prin care se realizeaza comunicarea efectiva. Firul de executie
extrage din flux cu ajurorul unor operatii pe siruri de caractere, numele
fisierului cerut. Daca exista fisierul, acesta se deschide pentru citire
si datele citite vor fi scrise in soclu pentru a putea fi extrase
si de catre client.
Constructori:
DatagramPacket(byte[] buf, int length)
Construieste un obiect de tip DatagramPacket pentru primirea unui pachet de lungime lengthMetode:DatagramPacket(byte[] buf, int length, InetAddress address, int port)
Construieste un obiect de tip DatagramPacket pentru trimiterea unui pachet de lungime length pe un port specificat, la un host specificatDatagramPacket(byte[] buf, int offset, int length)
Construieste un obiect de tip DatagramPacket pentru primirea unui pachet de lungime length, specificand un offset in bufferDatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
Construieste un obiect de tip DatagramPacket pentru trimiterea unui pachet de lungime length pe un port specificat, la un host specificat, specificand si un offset
InetAddress getAddress()Returneaza adresa IP al calculatorului de la care se primiste sau la care se trimite datagrama
byte[] getData()Returneaza sirul de octeti care se trimite sau care se primeste
int getLength()Returneaza lungimea datelor care se trimit sau care se primesc
int getOffset()Returneaza offsetul datelor care se trimit sau care se primesc
int getPort()Returneaza numarul portului calculatorului la distanta la care se trimit datele sau de la care se primesc datele
void setAddress(InetAddress iaddr)Seteaza adresa.
void setData(byte[] buf)Seteaza bufferul pentru pachet
void setData(byte[] buf, int offset, int length)Seteaza bufferyl pentru pachet
void setLength(int length)Seteaza lungimea pachetului.
void setPort(int iport)Seteaza portul..
Clasa DatagramSocket
Constructori:
DatagramSocket()Construieste un soclu si leaga la un port liber pe calculatorul local.DatagramSocket(int port)Construieste un soclu si leaga la un portul specificat pe calculatorul local.
DatagramSocket(int port, InetAddress laddr)
Construieste un soclu si leaga la adresa si portul specificat
Metode:
void close()Inchide soclul.
void connect(InetAddress address, int port)Conecteaza soclul la o adresa si port la distanta.
void disconnect()Deconecteaza soclul.
InetAddrress getInetAddress()Returneaza adresa la distanta la care soclul este conectat.
InetAddress getLocalAddress()Returneaza adresa locala la care soclul este conectat.
int getLocalPort()Returneaza portul local la care soclul este legat.
int getPort()Returneaza portul la distanta la care soclul este legat.
void receive(DatagramPacket p)Primire de datagrama prin acest soclu.
void send(DatagramPacket p)Trimitere de datagrama prin acest soclu
Aplicatia este formata dintr-o parte client si una server. Clientul
este un aplet, iar serverul o aplicatie.
//Source file: c:\Rose\curs9\exemplu2\ClientPackage\MessageApplet.javapackage ClientPackage;
import java.applet.Applet;
public class MessageApplet extends Applet
{
private String myHost;
private int myPort;public void init()
{
myHost = getCodeBase().getHost();
myPort = Integer.parseInt( getParameter("PORT"));
}public void sendMessage(String message)
{
try{
byte data[] =message.getBytes();
java.net.InetAddress addr = java.net.InetAddress.getByName( myHost );
java.net.DatagramPacket packet = new java.net.DatagramPacket(
data, data.length, addr, myPort );
java.net.DatagramSocket ds = new java.net.DatagramSocket();
ds.send( packet );
ds.close();
}
catch( java.io.IOException e ){
System.out.println(e );
}
}
public void start()
{
sendMessage("Pagina incarcata");
}
}
Fisierul HTML:
<applet HEIGHT=10 WIDTH=10 CODE=MessageApplet.class>Apletul utilizeaza metoda getCodeBase() si getHost() pentru aflarea numelui host-ului care il gazduieste, iar numarul portului se citeste din fisierul HTML. Astfel vor fi initializate datele obiectului MessageApplet, myHost si myPort. Metodele start() si stop() respectiv init() sunt metode publice care vor fi utilizate de catre navigator. init() se apeleaza o singura data la incarcarea paginii, iar start() ori de cate ori se revine la pagina respectiva. Metoda init() initializeaza datele obiectului, iar start() si stop() apeleaza metoda sendMessage() pentru trimiterea datagramei. Prima data se creaza un obiect InetAddress, care este incarcat cu adresa hostului, dupa care se va crea un obiect de tip DatagramPacket car va contine atat adresa cat mesajul de trimis.
<param NAME="PORT" VALUE="1234"></applet>
byte data[] =message.getBytes();Dupa ce avem pachetul gata, pregatit pentru trimitere, mai trebuie sa cream un soclu prin care sa se trimita si putem expedia pachetul cu metoda send(). Dupa epedierea pachetului se va inchide soclul.
//conversie String --> sir de octeti
InetAddress addr = InetAddress.getByName( myHost );
//obtinerea adresei IP al hostului
DatagramPacket packet = new DatagramPacket( data, data.length, addr, myPort );
//crearea pachetului
DatagramSocket ds = new DatagramSocket();Partea server:
//Crearea soclu
ds.send( packet );
//Trimitere pachet
ds. close();
//Inchidere soclu
import java.net.*;
import java.io.*;
public class AppletServer{
public static void main( String args[]) throws IOException{
DatagramSocket s = new DatagramSocket( Integer.parseInt(args[0]));
while( true ){
byte data[] = new byte[1024];
DatagramPacket packet = new DatagramPacket( data,1024);
s.receive( packet );
String message = new String( packet.getData());
System.out.println("Hostname: "+packet.getAddress().getHostName()+" "+message);
}
}
}
package ServerPackage;
public class AppletServer
{
public static void main(String[]
args)
{
try
{
java.net.DatagramSocket s
= new java.net.DatagramSocket( Integer.parseInt(args[0]));
while( true ){
//Serverul
se pregateste pentru primirea unui pachet
byte data[]
= new byte[1024];
java.net.DatagramPacket
packet = new java.net.DatagramPacket( data,1024);
//Serverul
asteapta primirea unui pachet
s.receive(
packet );
//Se scot
datele din pachet si se afiseaza la iesirea standard
String message
= new String( packet.getData());
System.out.println("Hostname:
"+ packet.getAddress().getHostName()+" "+message);
}
}
catch( java.io.IOException e ){}
}
}
Aplicatia server creaza un soclu de tip DatagramSocket si
intra in asteptarea clientilor pe un port. Numarul portului se primeste
de la linia de comanda, fiind de tip String la primire se va converti la
intreg prin apelul metodei Integer.parseInt(args[0]). Dupa crearea
soclului se creaza un pachet gol pentru receptionarea datagramelor sosite.
Acest pachet este un obiect de tip DatagramPacket care are si o variabila
de tip sir de octeti, care se utilizeaza pentru stocarea datelor sosite,
insa acesta trebuie alocat inaintea sosirii datelor. Dupa ce s-au creat
aceste doua obiecte, soclul apeleaza metoda receive() si intra in
astepatarea datagramelor. In momentul sosirii unei datagrame, se extrage
din aceasta mesajul si se va afisa pe terminal.
protocol://hostname/nume_resursa sauprotocol: poate fi http, ftp, gopher etc.
protocol://hostname:port/nume_resursa
Java ne ofera mai multi constructori pentru crearea unui obiect URL care pot genera exceptia MalformedURLException in caz ca lipseste ceva din specificatia URL. In momentul construirii obiectului nu se verifica existenta resursei, deci exceptia se genereaza daca un parametru este transmis incorect.
URL(String spec)Exemplu de utilizare:URL(String protocol, String host, int port, String file)
URL(String protocol, String host, String file)
try{Un aplet poate comunica doar cu calculatorul pe care este stocat codul lui. Cu ajutorul metodei getDocumentBase() a clasei Applet putem afla numele acestui calculator si dupa acea putem cere accesul la o resursa de pe acel calculator:
URL url = new URL("http","www.uttgm.ro",3128, "index.html");
System.out.println( url.toString());
}
catch( MalformedException e ){
//Se trateaza exceptia
}
try{Daca dorim sa aducem datele asociate unui URL trebuie sa cerem obiectului URL referinta la un stream atasat acestuia. Referinta se obtine prin apelul metodei openStream(). Exemplul urmator afiseaza pe terminal continutul unui fisier HTML.
URL url = new URL("http",getDocumentBase(),"index.html");
System.out.println( url.toString());
}
catch( MalformedException e ){
//Se trateaza exceptia
}
try{
URL url = new URL("http://www.uttgm.ro/index.html");
BufferedReader bin = new BufferedReader( new
InputStreamReader(url.openStream()));
String line;
while( ( line = bin.readLine()) != null )
System.out.println( line );
}
catch (Exception e ){}
try{
URL url = new URL( getDocumentBase(),"preturi.dat");
}
catch( Exception e ){}
Lista de preturi este formatat a.i sa fie ptrivit pentru a fi incarcat intr-un obiect Properties:
Mere=7000
Pere=10000
Struguri=25000
Basnana=27000
Incarcarea fisierului intr-un obiect Properties se va realiza prin:
Properties preturi = new Properties();
try{
preturi.load( url.openStream());
}
catch( Exception e ){}




public class Linie_de_comanda
{
private String nume;
private int cantitate;
private double pretunitar;
public Linie_de_comanda(String
nume, int cantitate, double pretunitar)
{
this.nume= nume;
this.cantitate = cantitate;
this.pretunitar = pretunitar;
}
public String toString()
{
String s2 = Integer.toString( cantitate
);
String s3 = Double.toString( pretunitar
);
return nume+" : "+s2+" : "+s3;
}
}
//Source file: CanvasComanda.java
import java.awt.Canvas;
import java.util.Vector;
import java.awt.Graphics;
public class CanvasComanda extends Canvas
{
private Vector a;
public void redraw(java.util.Vector a)
{
this.a =a;
repaint();
}
public
void paint(Graphics g)
{
int i;
int x=80, y=0;
java.awt.Font f
= new java.awt.Font("Monospaced",java.awt.Font.PLAIN, 12 );
g.setFont( f );
java.awt.FontMetrics
fm = g.getFontMetrics( f );
int height = fm.getHeight();
y+=height;
g.drawString("Your
order: ",x,y);
for( i = 0; i<a.size();
i++ )
{
y+=height;
g.drawString(
a.elementAt(i).toString(), x, y );
}
}
}
//Source file: ApletComanda.java
import java.applet.Applet;
import java.awt.event.ActionListener;
import java.awt.Choice;
import java.awt.TextField;
import java.awt.Button;
import java.util.Properties;
import java.util.Vector;
import java.awt.event.ActionEvent;
public class ApletComanda extends Applet implements
ActionListener
{
private Vector a = new java.util.Vector();
public CanvasComanda comanda;
public Choice nume;
public TextField cantitate;
public Button adaugare;
public Button gata;
public Properties preturi;
public void init()
{
setLayout( new java.awt.BorderLayout()
);
java.awt.Panel p = new
java.awt.Panel();
p.setLayout( new java.awt.FlowLayout());
nume = new Choice();
preturi = new java.util.Properties();
try{
java.net.URL
url= new java.net.URL( getDocumentBase(),"preturi.dat");
preturi.load(
url.openStream());
}
catch( Exception e ){}
java.util.Enumeration e
= preturi.propertyNames();
while( e.hasMoreElements())
nume.addItem(
( String ) e.nextElement());
p.add( nume );
cantitate = new TextField(
5);
p.add( cantitate );
adaugare = new Button("Adaugare");
p.add( adaugare );
adaugare.addActionListener(
this );
gata = new Button( "Gata");
p.add(gata );
gata.addActionListener(
this );
add( p, "North");
comanda = new CanvasComanda();
add( comanda,"Center");
comanda.setSize(250,150);
comanda.redraw( a );
}
public
void actionPerformed(ActionEvent arg0)
{
String command = arg0.getActionCommand();
if( command.equals("Adaugare")){
String itemName
= nume.getSelectedItem();
int cant =
0;
double pret
= 0.0;
try{
cant
= Integer.parseInt( cantitate.getText());
pret
= (new Double( preturi.getProperty( itemName ))).doubleValue();
}
catch( NumberFormatException
e ){}
a.addElement(
new Linie_de_comanda(itemName, cant, pret ));
comanda.redraw(
a );
}
}
}