Programarea în reţea – Socket-uri
1. Scopul lucrării Scopul acestei lucrări este insuşirea tehnicilor de programare în reţea utilizând socket-uri. 2. onsideraţii teoretice alculatoarele conectate conectate in reţea reţea comunică între ele utilizând utilizând protocoalele protocoalele !P "!ransport ontrol Protocol# Protocol# şi $%P "$ser %atagram Protocol# con&orm diagramei'
(igura 1. )i*elele de omunicare în reţea Pentru realizarea unor programe care comunică in reţea în +a*a, se utilizează clasele din pachetul +a*a.net . cest pachet o&eră clasele necesare pentru realizarea unor programe de reţea independente independente de sistemul sistemul de operare. n ta/elul următor sunt prezentate principalele clase care sunt utilizate pentru construirea unor programe de reţea. Class
Scop
URL
0eprezintă un $0
URLConnection
0eturnează continutul adresat de o/iectele
Socket
rează un socket !P
ServerSocket
rează un socket ser*er !P
DatagramSocket
rează un socket $%P
DatagramPacket
0eprezintă o datagrama trimisă printr-un o/iect
URL
DatagramSocket InetAddress
0eprezintă numele unui pc din reţea, respecti* P-ul corespunzător
a*a o&eră două a/ordări di&erite pentru realizarea de programe de reţea. ele două a/ordări sunt asociate cu clasele' - Socket, %atagramSocket şi Ser*erSocket - $0, $03ncoder şi $0onnection Programarea prin socket-uri reprezintă o a/ordare de ni*el +os, prin care, două calculatoare pot &i conectate pentru a realiza schim/ de date. a principiu de /aza, programarea prin socketuri &ace posi/ilă comunicarea în mod &ull-duple4 între client şi ser*er. omunicarea se &ace prin &lu4uri de octeţi. Pentru ca comunicarea să se des&ăşoare corespunzător, programatorul *a tre/ui să implementeze un protocol de comunicaţie "reguli de dialog#, pe care clientul şi ser*erul îl *or urma. Definitia socket-ului: $n socket reprezintă un punct de cone4iune întro reţea !P5P. ând două programe a&late pe două calculatoare în reţea doresc să comunice, &iecare dintre ele utilizează un socket. $nul dintre programe "ser*erul# *a deschide un socket si *a aştepta cone4iuni, iar celălalt program "clientul#, se *a conecta la ser*er şi ast&el schim/ul de in&ormaţii poate începe. Pentru a sta/ili o cone4iune, clientul *a tre/ui să cunoască adresa destinaţiei " a pc-ului pe care este deschis socket-ul# şi portul pe care socketul este deschis.
Principalele operaţii care sunt ´ de socket-uri sunt' - conectare la un alt socket - trimitere date - recepţionare date - inchidere cone4iune - acceptare cone4iuni
3. Desfăşurarea lucrării 3.1. Program client-server Pentru realizarea unui program client-ser*er se utilizează clasele Ser*erSocket şi Socket. Programul ser*er *a tre/ui să deschidă un port şi să aştepte cone4iuni. n acest scop este utilizată clasă Ser*erSocket. n momentul în care se crează un o/iect Ser*erSocket se speci&ică portul pe care se *a iniţia aşteptarea. nceperea ascultării portuli se &ace apelând metoda accept"#. n momentul în care un client s-a conectat, metoda accept"# *a returna un o/iect Socket. a rândul său clientul pentru a se conecta la un ser*er, *a tre/ui să creeze un o/iect de tip Socket, care *a primi ca parametri adresa ser*erului şi portul pe care acesta aşteaptă cone4iuni. tât la ni*elul ser*erului cât şi la ni*elul clientului, odată create o/iectele de tip Socket, se *or o/ţine &lu4urile de citire şi de scriere. n acest scop se utilizeaza metodele getnputStream"# şi get6uptuStream"#. n listingul următor este prezentat programul ser*er. import +a*a.net.78
import +a*a.io.78 pu/lic class Ser*erSimplu 9 pu/lic static *oid main"String:; args# thro9 String line=??8 ss = ne< Ser*erSocket"1@AA#8 BBcreaza o/iectul ser*ersocket socket = ss.accept"#8 BBincepe asteptarea peportul 1@AA BBin momentul in care un client s-a conectat ss.accept"# returneaza BBun socket care identi&ica cone4iunea BBcreaza &lu4urile de intrare iesire Cu&&ered0eader in = ne< Cu&&ered0eader" ne< nputStream0eader"socket.getnputStream"###8
PrintDriter out = ne< PrintDriter" ne< Cu&&eredDriter"ne< 6utputStreamDriter" socket.get6utputStream"###,true#8
Icatch"34ception e#9e.printStack!race"#8I &inall>9 ss.close"#8 i&"socketE=null# socket.close"#8 I I I
Programul client este prezentat în listingul următor' import +a*a.net.78 import +a*a.io.78 pu/lic class lientSimplu 9 pu/lic static *oid main"String:; args#thro 9 BBcreare o/iect address care identi&ica adresa ser*erului netddress address =netddress.getC>)ame"?localhost?#8 BBse putea utiliza *arianta alternati*a' netddress.getC>)ame"?12J.A.A.1?# socket = ne< Socket"address,1@AA#8
Cu&&ered0eader in = ne< Cu&&ered0eader" ne< nputStream0eader" socket.getnputStream"###8
BB 6utput is automaticall> &lushed BB /> PrintDriter' PrintDriter out = ne< PrintDriter" ne< Cu&&eredDriter" ne< 6utputStreamDriter" socket.get6utputStream"###,true#8 &or"int i = A8 i K 1A8 i HH# 9 out.println"?mesa+ ? H i#8 String str = in.readine"#8 BBtrimite mesa+ S>stem.out.println"str#8 BBasteapta raspuns I out.println"?3)%?#8 BBtrimite mesa+ care determina ser*erul sa inchida cone4iunea
I catch "34ception e4# 9e4.printStack!race"#8I &inall>9 socket.close"#8 I I I
Pentru *eri&icare se *a starta ser*erul, după care se *a starta clientul.
3.2 Server multifir nalizând programul ser*er prezentat în secţiunea anterioarăm se o/ser*ă că acesta poate ser*i doar un singur client la un moment dat. Pentru ca ser*erul să poată ser*i mai mulţi clienţi simultan, se *a utiliza programarea multi&ir. deea de /ază este simplă, şi anume, ser*erul *a aştepta cone4iuni prin apelarea metodei accept"#. n momentul in care un client s-a conectat şi metoda accept"# a returnat un Socket, se *a crea un &ir de e4ecuţie care *a ser*i respecti*ul clientul, iar se*erul *a re*eni în aşteptare. n listingul următor este prezentat un ser*er multi&ir – capa/il de a ser*i mai multi clienţi simultan. import +a*a.io.78 import +a*a.net.78 pu/lic class Ser*erLulti&ir 9 pu/lic static &inal int P60! = 1@AA8 *oid startSer*er"# 9 Ser*erSocket ss=null8 tr> 9 ss = ne< Ser*erSocket"P60!#8
9 Socket socket = ss.accept"#8 ne< !ratarelient"socket#8 I Icatch"634ception e4# 9 S>stem.err.println"?3roare '?He4.getLessage"##8 I &inall> 9 tr>9ss.close"#8Icatch"634ception e42#9I I I pu/lic static *oid main"String args:;# 9 Ser*erLulti&ir sm& = ne< Ser*erLulti&ir"#8 sm&.startSer*er"#8 I
I class !ratarelient e4tends !hread 9 pri*ate Socket socket8 pri*ate Cu&&ered0eader in8 pri*ate PrintDriter out8 !ratarelient"Socket socket#thro 9 stem.out.println"?3choing' ? H str#8 out.println"str#8 IBB.stem.out.println"?closing...?#8 I catch"634ception e# 9S>stem.err.println"?6 34ception?#8I &inall> 9 tr> 9 socket.close"#8 Icatch"634ception e# 9S>stem.err.println"?Socket not closed?#8I
I
IBB.run I
3.3. Server HTTP n această sectiune este creat un ser*er G!!P care poate răspunde la cereri M3!. (unctia main"# din cadrul clasei GttpSer*er startaează un &ir de e4ecuţie. n cadrul acestui &ir se instanţiază un o/iect Ser*erSocket şi se incepe ascultarea portului NA, care este portul standard pentru protocolul G!!P. n momentul în care apare o cerere "un client se conectează pe portul NA# metoda accept"# *a returna un o/iect Socket. n continuare se crează un o/iect Preces0eFuest "care este de tip &ir de e4cutie#, care *a primi ca parametru, o/iectul Socket returnat de metoda accept"#. %upă crearea o/iectului Proces0eFuest, ser*erul re*ine în aşteptare şi *a putea ser*i alţi clienţi. lasa Proces0eFuest implementează o *ersiune simpli&icată a protocolului G!!P. n cadrul constructorului clasei Proces0eFuest se crează &lu4urile de intrare 5 ieşire, după care este startat &irul de e4ecuţie. n cadrul &irului de e4ecuţie este analizată cererea primită de la client , şi în cazul în care aceasta este o cerere *alidă de tip M3!, atunci se *a transmite către client resursa solicitată. import +a*a.io.78 import +a*a.net.78 class GttpSer*er e4tends !hread 9 BBportul standard pri*ate &inal static int P60! = NA8 pri*ate &inal String inionte4t=?c'BtempBSer*erG!!PBstem.out.println"?Start ser*er http.?#8 ss = ne< Ser*erSocket"P60!#8 ali*e=true8 start"#8 I pu/lic *oid run"#9 9 S>stem.out.println"?Ser*er asteapta...?#8 ne< Proces0eFuest"ss.accept"#,inionte4t#8 Icatch"634ception e#9S>stem.err.println"?30603 6)3!03'?He.getLessage"##8I BB..reia /ucla de asteptare dupa ce am creat un &ir pentru client I
S>stem.out.println"?S!6P S30O30?#8 I pu/lic static *oid main"String:; args#thro9 ne< GttpSer*er"#8 Icatch"34ception e#9e.printStack!race"#8I I I import +a*a.net.78 import +a*a.io.78 import +a*a.util.78 class Proces0eFuest e4tends !hread 9 pri*ate PrintDriter outStr8 pri*ate Cu&&ered0eader inStr8 pri*ate Socket s8 pri*ate %ata6utputStream dout8 pri*ate String inionte4t8 Proces0eFuest"Socket s, String ionte4t#9 tr>9 outStr = ne< PrintDriter"ne< 6utputStreamDriter"s.get6utputStream"##,true#8 inStr = ne< Cu&&ered0eader"ne< nputStream0eader"s.getnputStream"###8 dout = ne< %ata6utputStream"s.get6utputStream"##8 inionte4t = ionte4t8 this.s = s8 start"#8 Icatch"634ception e# 9S>stem.err.println"?30603 6)3!03' ?He.getLessage"##8I I pu/lic *oid run"#9 tr>9 String &ile)ame=null8 String reFuest = inStr.readine"#8 S>stem.out.print"reFuest#8 i&"reFuest.lastnde46&"?M3!?#==A# &ile)ame = interpretM3!"reFuest#8 else thro< ne< 34ception"?C$?#8 />te:; data = read(ile"&ile)ame#8 dout.9 tr>9s.close"#8Icatch"34ception e#9I I I
pri*ate String interpretM3!"String rFst# throstem.err.println"?30303'?H&ile)#8 return &ile)8 I &ile) = &ile)H tmp8 S>stem.err.println"?30303'?H&ile)#8 return &ile)8 I pri*ate />te:; read(ile"String &ile)# throte:; data = ne< />te:&str.a*aila/le"#;8 &str.read"data#8 return data8 I I
Pentru *eri&icarea programului anterior se *a modi&ica *aria/ila inionte4tm din cadrul clasei G!!PSer*er, ast&el încât aceasta să indice calea corectă catre conte4tul iniţial "directorul unde se a&lă toate resursele pe care clientul le poate accesa#.
3.5 Trimiterea obiectelor prin socet-uri Lecanismul de serializare pune la dispoziţia programatorului o metodă prin care un o/iect poate &i sal*at pe disc şi restaurat atunci cand este ne*oie. !ot prin acelaşi mecanism un o/iect poate &i transmis la distanta catre o altă maşină utilizând socketurile. Pentru a putea serializa un o/iect acesta *a tre/ui să implementeze inter&aţa Serializable. Pentru scrierea şi citirea o/iectelor serializate se utilizează &lu4urile de intrare B ieşire ' ObjectInputStream si ObjectOutputStream. istingul următor prezintă modul in care se poate serializa B deserializa un o/iect. import +a*a.io.78 import +a*a.net.78 pu/lic class Serial!est e4tends !hread9
pu/lic *oid run"#9
tr>9 Ser*erSocket ss = ne< Ser*erSocket"1@JJ#8 Socket s = ss.accept"#8 6/+ectnputStream ois = ne< 6/+ectnputStream"s.getnputStream"##8 Pers p = "Pers#ois.read6/+ect"#8 S>stem.out.println"p#8 s.close"#8 ss.close"#8 Icatch"34ception e#9e.printStack!race"#8I
I pu/lic static *oid main"String:; args# thro)ame"?localhost?#,1@JJ#8 6/+ect6utputStream oos = ne< 6/+ect6utputStream"s.get6utputStream"##8 Pers p = ne< Pers"?lin?,1R#8 oos.
I I
class Pers implements Serializa/le9 String nume8 int *arsta8 Pers"String n, int *#9 nume = n8 *arsta = *8 I pu/lic String toString"#9 return ?Persoana' ?HnumeH? *asrta' ?H*arsta8 I I
34istă situaţii în care în momentul in care se sel*eaza starea unui o/iect prin serializare, nu se doreşte sal*are tuturor starilor o/iectului, respecti* nu se doreşte sal*area sau transmiterea anumitor parametri ai o/iectului. Pentru a /loca serializarea unui atri/ut al unui o/iect serializa/il se utilizează cu*antul cheie transient.
a. Plecând de la listingul anterior relizati un program client şi un program ser*er care transmit între ele o/iecte de tip Pers.
b. dăugaţi în cadrul clasei Pers în &aţa liniei V String nume; V cu*ântul cheie transient şi o/ser*aţi e&ectul acestei modi&icări.
3.!. Server "e timp #$DP% n cadrul acestei secţiuni este construit un ser*er de timp care *a trimite la cerere data curentă către clienţii care solicită acest lucru. %e asemenea este construit şi clientul care accesează ser*iciile ser*erului de timp. a ni*elul ser*erului se creează un o/iect %atagramSocket, care *a primi ca parametru portul pe care ser*erul *a începe ascultarea. DatagramSocket socket = new DatagramSocket!"##$; n continuare se construieşte un o/iect %atagramPacket, care *a &i utilizat de către ser*er pentru a recepţiona cererea de la client. 6 dată construit o/iectul %atagramPacket, ser*erul *a începe ascultarea portului 1@JJ, prin in*ocarea metodei recei*e"#. b%te&' buf = new b%te&()*'; Datagram+acket packet = new Datagram+acketbuf,buf.lengt$; socket.receiepacket$; n momentul în care un client doreşte să apeleze la ser*iciile ser*erului, acesta *a trimite un pachet către ser*er. Ser*erul citeşte din cadrul pachetului portul şi adresa clientului, şi îi *a trimite acestuia un pachet ce conţine data curentă. Inet/00ress a00ress = packet.get/00ress$; int port = packet.get+ort$; buf = new Date$$.toString$$.get1%tes$; packet = new Datagram+acketbuf,buf.lengt,a00ress,port$; socket.sen0packet$; $n client, pentru a se conecta la ser*er, tre/uie să creeze un o/iect %atagramSocket, şi să trimită un pachet către ser*er. Spre deose/ire de ser*er, clientul nu este o/ligat să speci&ice nici un port în momentul creierii o/iectului %atagramSocket, întrucât se atri/uie automat un port li/er respecti*ului o/iect. import +a*a.io.78 import +a*a.net.78 import +a*a.util.78 pu/lic class !imeSer*er e4tends !hread9 /oolean running=true8 pu/lic !imeSer*er"# 9start"#8I pu/lic *oid run"#9 tr>9 %atagramSocket socket = ne< %atagramSocket"1@JJ#8 te:; /u& = ne< />te:2WX;8 %atagramPacket packet = ne< %atagramPacket"/u&,/u&.length#8 socket.recei*e"packet#8 BBciteste adresa si portul clientului netddress address = packet.getddress"#8
int port = packet.getPort"#8 BBtrimite un repl> catre client /u& = ""ne< %ate"##.toString"##.getC>tes"#8 packet = ne< %atagramPacket"/u&,/u&.length,address,port#8 socket.send"packet#8 I Icatch"34ception e4#9e4.printStack!race"#8I I pu/lic static *oid main"String:; args# 9 !imeSer*er timeSer*er1 = ne< !imeSer*er"#8 I
I import +a*a.io.78 import +a*a.net.78 import +a*a.util.78 pu/lic class lient 9 pu/lic static *oid main"String:; args# 9 tr>9 %atagramSocket socket = ne< %atagramSocket"#8 />te:; /u& = ne< />te:2WX;8 %atagramPacket packet = ne< %atagramPacket"/u&,/u&.length, netddress.getC>)ame"?localhost?#,1@JJ#8 socket.send"packet#8 packet = ne< %atagramPacket"/u&,/u&.length#8 socket.recei*e"packet#8 S>stem.out.println"ne< String"packet.get%ata"###8 Icatch"34ception e4#9e4.printStack!race"#8I I I
&. 'ntrebări si probleme a. n ce scop sunt utilizate clasele Ser*erSocket şi SocketY /. e clasă este utilizată în +a*a pentru a identi&ica un calculator din reţea Y c. $tilizând programarea prin socketuri construiţi un program simplu de tip chat.