Você está na página 1de 57

Tarefa de Programao 1: Construindo um servidor Web multithreaded

Neste laboratrio, ser desenvolvido um servidor Web em duas etapas. No final, voc
ter construdo um servidor Web multithreaded, que ser capaz de processar mltiplas
requisies de servios simult!neas em paralelo. "oc dever demonstrar que seu
servidor Web # capaz de enviar sua home pa$e ao bro%ser Web.
&mplementaremos a vers'o (.) do *++,, definido na -./0(123, onde requisies
*++, separadas s'o enviadas para cada componente da p$ina Web. 4ste servidor
dever manipular mltiplas requisies simult!neas de servios em paralelo. &sso
si$nifica que o servidor Web # multithreaded. No thread principal, o servidor escuta
uma porta fi5a. 6uando recebe uma requisi'o de cone5'o +/,, ele a7usta a cone5'o
+/, atrav#s de outra porta e atende essa requisi'o em um thread separado. ,ara
simplificar esta tarefa de pro$rama'o, desenvolveremos o cdi$o em duas etapas. No
primeiro est$io, voc ir escrever um servidor multithreaded que simplesmente e5ibe o
contedo da mensa$em de requisi'o *++, recebida. 8ssim que esse pro$rama
funcionar adequadamente, voc adicionar o cdi$o necessrio para $erar a resposta
apropriada.
4nquanto voc desenvolve o cdi$o, pode testar seu servidor a partir de um bro%ser
Web. 9as lembre que o servidor n'o est atendendo pela porta padr'o :); lo$o, #
preciso especificar o nmero da porta 7unto < =-> que voc fornecer ao bro%ser Web.
,or e5emplo, se o nome da sua mquina # host.someschool.edu, seu servidor est
escutando a porta ?@:1, e voc quer recuperar o arquivo inde5.html, ent'o deve
especificar ao bro%ser a se$uinte =->A
http://host.someschool.edu:6789/index.html
Be voc omitir CA?@:1D, o bro%ser ir assumir a porta :), que, provavelmente, n'o ter
nenhum servidor < escuta.
6uando o servidor encontra um erro, ele envia uma mensa$em de resposta com a fonte
*+9> apropriada, de forma que a informa'o do erro se7a e5ibida na 7anela do
bro%ser.
(
Servidor Web em Java: Parte A
Nas etapas se$uintes, veremos o cdi$o para a primeira implementa'o do nosso
servidor Web. Bempre que voc vir o sinal CED, dever fornecer o detalhe que estiver
faltando.
Nossa primeira implementa'o do servidor Web ser multithreaded, e o processamento
de cada requisi'o de entrada ter um local dentro de um thread separado de e5ecu'o.
&sso permite ao servidor atender a mltiplos clientes em paralelo, ou desempenhar
mltiplas transferncias de arquivo a um nico cliente em paralelo. 6uando criamos um
novo thread de e5ecu'o, precisamos passar ao construtor de threads uma inst!ncia de
al$umas classes que implementa a interface Runnable. 4ssa # a raz'o de se definir uma
classe separada chamada HttpRequest. 8 estrutura do servidor Web # mostrada a
se$uirA
import java.io.*
import java.net.*
import java.util.*
public !inal class "eb#erver
$
public static void main%#trin& arv&'() thro*s +xception
$
, , ,
-
-
!inal class HttpRequest implements Runnable
$
, , ,
-
Normalmente, servidores Web processam requisies de servio recebidas atrav#s da
conhecida porta :). "oc pode escolher qualquer porta acima de ()F2, mas lembre0se
de usar o mesmo nmero de porta quando fizer requisies ao seu servidor Web a partir
do seu bro%ser.
.ublic static void main%#trin& arv&'() thro*s +xception
$
// /justar o n0mero da porta.
int port 1 6789
F
, , ,
-
8 se$uir, abrimos um socGet e esperamos por uma requisi'o de cone5'o +/,. /omo
estaremos atendendo a mensa$ens de requisi'o indefinidamente, colocamos a opera'o
de escuta dentro de um lao infinito. &sso si$nifica que precisaremos terminar o servidor
Web di$itando H/ pelo teclado.
// +stabelecer o soc2et de escuta.
3
// .rocessar a requisi45o de servi4o H66. em um la4o in!inito.
"hile %true) $
// +scutar requisi45o de conex5o 67..
3
, , ,
-
6uando uma requisi'o de cone5'o # recebida, criamos um ob7eto HttpRequest,
passando ao seu construtor uma referncia para o ob7eto #oc2et que representa nossa
cone5'o estabelecida com o cliente.
//7onstruir um objeto para processar a mensa&em de requisi45o H66..
HttpRequest request 1 ne* HttpRequest % 3 )
// 7riar um novo thread para processar a requisi45o.
6hread thread 1 ne* 6hread%request)
//8niciar o thread.
6hread.start%)
,ara que o ob7eto HttpRequest manipule as requisies de servio *++, de entrada
em um thread separado, criamos primeiro um novo ob7eto 6hread, passando ao seu
construtor a referncia para o ob7eto HttpRequest, ent'o chamamos o m#todo start%)
do thread.
8ps o novo thread ter sido criado e iniciado, a e5ecu'o no thread principal retorna
para o topo do loop de processamento da mensa$em. I thread principal ir ent'o
bloquear, esperando por outra requisi'o de cone5'o +/,, enquanto o novo thread
continua rodando. 6uando outra requisi'o de cone5'o +/, # recebida, o thread
J
principal realiza o mesmo processo de cria'o de thread, a menos que o thread anterior
tenha terminado a e5ecu'o ou ainda este7a rodando.
&sso completa o cdi$o em main%). ,ara o restante do laboratrio, falta o
desenvolvimento da classe HttpRequest.
Keclaramos duas variveis para a classe *ttp-equestA 7R9: e soc2et. Ke acordo com a
especifica'o *++,, precisamos terminar cada linha da mensa$em de resposta do
servidor com um carriage return L/-M e um line feed L>.M, assim definimos a 7R9: de
forma conveniente. 8 varivel soc2et ser usada para armazenar uma referncia ao
socGet de cone5'o. 8 estrutura da classe *ttp-equest # mostrada a se$uirA
!inal class HttpRequest implements Runnable
$
!inal static #trin& 7R9: 1 ;<r<n=
#oc2et soc2et
// 7onstrutor
public HttpRequest%#oc2et soc2et) thro*s +xception
$
this.soc2et 1 soc2et
-
// 8mplemente o m>todo run%) da inter!ace Runnable.
.ublic void run%)
$

-
private void processRequest%) thro*s +xception
$

-
-
,ara passar uma inst!ncia da classe HttpRequest para o construtor de 6hreads, a
HttpRequest deve implementar a interface Runnable. &sso si$nifica simplesmente que
devemos definir um m#todo pblico chamado run%) que retorna void. 8 maior parte do
processamento ocorrer dentro do processRequest%), que # chamado de dentro do
run%).
2
8t# este ponto, apenas repassamos as e5cees em vez de apanh0las. /ontudo, n'o
podemos repass0las a partir do run%), pois devemos aderir estritamente < declara'o
do run%) na interface Runnable, a qual n'o repassa e5ce'o al$uma. /olocaremos todo
o cdi$o de processamento no processRequest%), e a partir da repassaremos as
e5cees ao run%). Kentro do run%), e5plicitamente recolhemos e tratamos as e5cees
com um bloco trNOcatch.
// 8mplementar o m>todo run%) da inter!ace Runnable.
.ublic void run%)
$
tr? $
processRequest%)
- catch %+xception e) $
#?stem.out.println%e)
-
-
8$ora, vamos desenvolver o cdi$o de dentro do processRequest%). ,rimeiro
obtemos referncias para os trechos de entrada e sada do socGet. 4nt'o colocamos os
filtros 8nput#treamReader e @u!!eredReader em torno do trecho de entrada. No
entanto, n'o colocamos nenhum filtro em torno do trecho de sada, pois estaremos
escrevendo bNtes diretamente no trecho de sada.
.rivate void processRequest%) thro*s +xception
$
// Abter uma re!erBncia para os trechos de entrada e saCda do
soc2et.
8nput#tream is 1 3
DataAutput#tream os 1 3
// /justar os !iltros do trecho de entrada.
3
@u!!eredReader br 1 3
, , ,
-
8$ora estamos preparados para capturar mensa$ens de requisi'o dos clientes, fazendo
a leitura dos trechos de entrada do socGet. I m#todo read9ine%) da classe
3
@u!!eredReader ir e5trair caracteres do trecho de entrada at# encontrar um caracter
fim0de0linha, ou em nosso caso, uma seqPncia de caracter fim0de0linha /->..
I primeiro item disponvel no trecho de entrada ser a linha de requisi'o *++,. L"e7a
Be'o F.F do livro0te5to para a descri'o disso e dos se$uintes campos.M
// Abter a linha de requisi45o da mensa&em de requisi45o H66..
#trin& request9ine 1 3
// +xibir a linha de requisi45o.
#?stem.out.println%)
#?stem.out.println%request9ine)
8ps obter a linha de requisi'o do cabealho da mensa$em, obteremos as linhas de
cabealho. Kesde que n'o saibamos antecipadamente quantas linhas de cabealho o
cliente ir enviar, podemos obter essas linhas dentro de uma opera'o de loopin$.
// Abter e exibir as linhas de cabe4alho.
#trin& header9ine 1 null
"hile %%header9ine 1 br.read9ine%)).len&th%) E1 F) $
#?stem.out.println%header9ine)
-
N'o precisamos das linhas de cabealho, a n'o ser para e5ibi0las na tela; portanto,
usamos uma varivel strin$ temporria, header9ine, para manter uma referncia aos
seus valores. I loop termina quando a e5press'o
%header9ine 1 br.read9ine%)).len&th%)
che$ar a zero, ou se7a, quando o header9ine tiver comprimento zero. &sso acontecer
quando a linha vazia ao final do cabealho for lida.
Na pr5ima etapa deste laboratrio, iremos adicionar o cdi$o para analisar a
mensa$em de requisi'o do cliente e enviar uma resposta. 9as, antes de fazer isso,
vamos tentar compilar nosso pro$rama e test0lo com um bro%ser. 8dicione as linhas a
se$uir ao cdi$o para fechar as cadeias e cone5'o de socGet.
// :eche as cadeias e soc2et.
os.close%)
br.close%)
soc2et.close%)
?
8ps compilar o pro$rama com sucesso, e5ecute0o com um nmero de porta disponvel,
e tente contat0lo a partir de um bro%ser. ,ara fazer isso, di$ite o endereo &, do seu
servidor em e5ecu'o na barra de endereos do seu bro%ser. ,or e5emplo, se o nome da
sua mquina # host.someschool.edu, e o seu servidor roda na porta ?@:1, ent'o voc
deve especificar a se$uinte =->A
http://host.someschool.edu:6789/
I servidor dever e5ibir o contedo da mensa$em de requisi'o *++,. /heque se ele
est de acordo com o formato de mensa$em mostrado na fi$ura do *++, -equest
9essa$e da Be'o F.F do livro0te5to.
Servidor Web em Java: Parte B
4m vez de simplesmente encerrar a thread aps e5ibir a mensa$em de requisi'o *++,
do bro%ser, analisaremos a requisi'o e enviaremos uma resposta apropriada. &remos
i$norar a informa'o nas linhas de cabealho e usar apenas o nome do arquivo contido
na linha de requisi'o. Ke fato, vamos supor que a linha de requisi'o sempre especifica
o m#todo Q4+ e i$norar o fato de que o cliente pode enviar al$um outro tipo de
requisi'o, tal como *48K o ,IB+.
45tramos o nome do arquivo da linha de requisi'o com a a7uda da classe
#trin&6o2eniGer. ,rimeiro, criamos um ob7eto #trin&6o2eniGer que cont#m a strin$
de caracteres da linha de requisi'o. Be$undo, pulamos a especifica'o do m#todo, que
supusemos como sendo CQ4+D. +erceiro, e5tramos o nome do arquivo.
// +xtrair o nome do arquivo a linha de requisi45o.
#trin&6o2eniGer to2ens 1 ne* #trin&6o2eniGer%request9ine)
to2ens.next6o2en%) // pular o m>todoH que deve ser ;I+6=
#trin& !ileJame 1 to2ens.next6o2en%)
// /crescente um ;.= de modo que a requisi45o do arquivo esteja dentro
do diretKrio atual.
!ileJame 1 ;.= L !ileJame
@
/omo o bro%ser precede o nome do arquivo com uma barra, usamos um ponto como
prefi5o para que o nome do caminho resultante inicie dentro do diretrio atual.
8$ora que temos o nome do arquivo, podemos abrir o arquivo como primeira etapa para
envi0lo ao cliente. Be o arquivo n'o e5istir, o construtor :ile8nput#tream%) ir
retornar a :ileJot:ound+xception. 4m vez de retornar esta possvel e5ce'o e
encerrar a thread, usaremos uma constru'o trNOcatch para a7ustar a varivel booleana
!ile+xists para falsa. 8 se$uir, no cdi$o, usaremos este fla$ para construir uma
mensa$em de resposta de erro, melhor do que tentar enviar um arquivo n'o e5istente.
// /brir o arquivo requisitado.
:ile8nput#tream !is 1 null
@oolean !ile+xists 1 true
tr? $
!is 1 ne* :ile8nput#tream%!ileJame)
- catch %:ileJot:ound+xception e) $
!ile+xists 1 !alse
-
45istem trs partes para a mensa$em de respostaA a linha de status, os cabealhos da
resposta e o corpo da entidade. 8 linha de status e os cabealhos da resposta s'o
terminados pela de seqPncia de caracteres /->.. &remos responder com uma linha de
status, que armazenamos na varivel status9ine, e um nico cabealho de resposta,
que armazenamos na varivel content6?pe9ine. No caso de uma requisi'o de um
arquivo n'o e5istente, retornamos 404 Not Found na linha de status da mensa$em de
resposta e inclumos uma mensa$em de erro no formato de um documento *+9> no
corpo da entidade.
// 7onstruir a mensa&em de resposta.
#trin& status9ine 1 null
#trin& content6?pe9ine 1 null
#trin& entit?@od? 1 null
8! %!ile+xists) $
status9ine 1 3
content6?pe9ine 1 ;7ontentMt?pe: ; L
content6?pe% !ilename ) L 7R9:
- else $
status9ine 1 3
:
content6?pe9ine 1 3
entit?@od? 1 ;NH6O9P= L
;NH+/DPN68669+PJot :oundN/68669+PN/H+/DP= L
;N@ADQPJot :oundN/@ADQPN/H6O9P=
6uando o arquivo e5iste, precisamos determinar o tipo 9&94 do arquivo e enviar o
especificador do tipo 9&94 apropriado. .azemos esta determina'o num m#todo
privado separado chamado content6?pe%)H que retorna uma strin$ que podemos
incluir na linha de tipo de contedo que estamos construindo.
8$ora podemos enviar a linha de status e nossa nica linha de cabealho para o bro%ser
escrevendo na cadeia de sada do socGet.
// +nviar a linha de status.
os.*rite@?tes%status9ine)
// +nviar a linha de tipo de conte0do.
os.*riteb?tes%3)
// +nviar uma linha em branco para indicar o !im das linhas de
cabe4alho.
os.*rite@?tes%7R9:)
8$ora que a linha de status e a linha de cabealho com delimitador /->. foram
colocadas dentro do trecho de sada no caminho para o bro%ser, # hora de fazermos o
mesmo com o corpo da entidade. Be o arquivo requisitado e5istir, chamamos um
m#todo separado para enviar o arquivo. Be o arquivo requisitado n'o e5istir, enviamos a
mensa$em de erro codificada em *+9> que preparamos.
// +nviar o corpo da entidade.
8! %!ile+xists) $
send@?tes%!isH os)
!is.close%)
- else $
os.*rite@?tes%3)
-
8ps enviar o corpo da entidade, o trabalho neste thread est terminado; ent'o fechamos
as cadeias e o socGet antes de encerrarmos.
1
8inda precisamos codificar os dois m#todos que referenciamos no cdi$o acima, ou
se7a, o m#todo que determina o tipo 9&94, content6?pe%) e o m#todo que escreve o
arquivo requisitado no trecho de sada do socGet. ,rimeiro veremos o cdi$o para enviar
o arquivo para o cliente.
private static void send@?tes%:ile8nput#tream !isH Autput#tream os)
thro*s +xception
$
// 7onstruir um bu!!er de RS para comportar os b?tes no caminho
para o soc2et.
b?te'( bu!!er 1 ne* b?te'RFTU(
int b?tes 1 F
// 7opiar o arquivo requisitado dentro da cadeia de saCda do
soc2et.
"hile%%b?tes 1 !is.read%bu!!er)) E1 MR ) $
os.*rite%bu!!erH FH b?tes)
-
-
8mbos read%) e *rite%) repassam e5cees. 4m vez de pe$ar essas e5cees e
manipul0las em nosso cdi$o, iremos repass0las pelo m#todo de chamada.
8 varivel, bu!!er, # o nosso espao de armazenamento intermedirio para os bNtes em
seu caminho desde o arquivo para a cadeia de sada. 6uando lemos os bNtes do
:ile8nput#tream, verificamos se read%) retorna menos um LR(M, indicando que o final
do arquivo foi alcanado. Be o final do arquivo n'o foi alcanado, read%) retorna o
nmero de bNtes que foi colocado dentro do bu!!er. =samos o m#todo *rite%) da
classe Autput#tream para colocar estes bNtes na cadeia de sada, passando para ele o
nome do vetor dos bNtes, bu!!er, o ponto inicial nesse vetor, ), e o nmero de bNtes no
vetor para escrita, b?tes.
8 parte final do cdi$o necessria para completar o servidor Web # um m#todo que
e5amina a e5tens'o de um nome de arquivo e retorna uma strin$ que representa o tipo
9&94. Be a e5tens'o do arquivo for desconhecida, podemos retornar o tipo
application/octetMstream.
.rivate static #trin& content6?pe%#trin& !ileJame)
$
i!%!ilename.ends"ith%;.htm=) VV !ileJame.ends"ith%;.html=)) $
()
return ;text/html=
-
i!%3) $
3
-
o!%3) $
3
-
return ;application/octetMstream=
-
4st faltando um pedacinho deste m#todo. ,or e5emplo, nada # retornado para arquivos
Q&. ou S,4Q. "oc mesmo poder adicionar os tipos de arquivo que est'o faltando, de
forma que os componentes da sua home pa$e se7am enviados com o tipo de contedo
corretamente especificado na linha de cabealho do tipo de contedo. ,ara Q&.s, o tipo
9&94 # ima&e/&i! e, para S,4Qs, # ima&e/jpe&.
&sso completa o cdi$o para a se$unda fase de desenvolvimento do nosso servidor Web.
+ente rodar o servidor a partir do diretrio onde sua home pa$e est localizada e
visualizar os arquivos da home pa$e com o bro%ser. >embre0se de incluir um
especificador de porta na =->, de forma que o bro%ser n'o tente se conectar pela porta
:). 6uando voc se conectar ao servidor Web em e5ecu'o, e5amine as requisies
Q4+ de mensa$ens que o servidor Web receber do bro%ser.
((
Tarefa de Programao 2: Agente usurio de !orreio em Java
Neste laboratrio, voc implementar um a$ente usurio de correio que envia e0mail
para outros usurios. Bua tarefa # pro$ramar a intera'o B9+, entre o 9=8 e o
servidor B9+, local. I cliente prov uma interface $rfica de usurio, a qual deve
conter campos para os endereos do remetente e do destinatrio, para o assunto da
mensa$em e para a prpria mensa$em. 8 interface de usurio deve ser parecida comA
/om essa interface, quando se quer enviar um e0mail, # preciso preencher os endereos
completos do remetente e do destinatrio Le5emploA userWsomeschool.edu, e n'o
simplesmente userM. "oc poder enviar e0mail para apenas um destinatrio. +amb#m
ser necessrio informar o nome Lou endereo &,M do seu servidor de correio local. "e7a
6uerNin$ the KNB abai5o para mais informaes sobre como obter o endereo do
servidor de correio local.
6uando tiver encerrado a composi'o do e0mail, pressione Send para envi0lo.
(F
" !#digo
I pro$rama consiste de quatro classesA
9ail/lient 8 interface do usurio
9essa$e 8 mensa$em de e0mail
4nvelope 4nvelope B9+, que envolve a mensa$em
B9+,/onnection /one5'o ao servidor B9+,
"oc precisar completar o cdi$o na classe #O6.7onnection de modo que no fim
voc tenha um pro$rama capaz de enviar e0mail para qualquer destinatrio. I cdi$o
para a classe #O6.7onnection est no fim desta p$ina. I cdi$o para outras trs
classes # fornecido nesta p$ina.
Is locais onde voc dever completar o cdi$o est'o marcados com comentrios
/* :ill in */. /ada um deles requer uma ou mais linhas de cdi$o.
8 classe Oail7lient prov a interface do usurio e chama as outras classes quando
necessrio. 6uando o bot'o Send # pressionado, a classe Oail7lient constri um
ob7eto de classe Oessa&e para comportar a mensa$em de correio. I ob7eto Oessa&e
comporta os cabealhos e o corpo da mensa$em atual. 4le constri o envelope B9+,
usando a classe +nvelope. 4ssa classe compreende a informa'o B9+, do remetente e
do destinatrio, o servidor B9+, de domnio do remetente e o ob7eto Oessa&e. 4nt'o o
ob7eto Oail7lient cria o ob7eto #O6.7onnection, que abre uma cone5'o para o
servidor B9+,, e o ob7eto Oail7lient envia a mensa$em atrav#s dessa cone5'o. I
envio do e0mail acontece em trs fasesA
(. I ob7eto Oail7lient cria o ob7eto #O6.7onnection e abre a cone5'o para o servidor
B9+,.
F. I ob7eto Oail7lient envia a mensa$em usando a fun'o #O6.7onnection.send%).
J. I ob7eto Oail7lient fecha a cone5'o B9+,.
8 classe Oessa&e cont#m a fun'o isXalid%)H que # usada para verificar os endereos
do remetente e do destinatrio para se certificar de que h apenas um nico endereo e
que este cont#m o sinal T. I cdi$o fornecido n'o realiza nenhum outro tipo de
verifica'o de erro.
(J
C#digos de res$osta
,ara o processo bsico de envio de mensa$em, # necessrio implementar apenas uma
parte do B9+,. Neste laboratrio, voc precisar implementar somente os se$uintes
comandos B9+,A
Comando Cdigo de resposta
K8+8 J32
*4>I F3)
98&> .-I9 F3)
6=&+ FF(
-/,+ +I F3)
8 tabela acima tamb#m lista os cdi$os de resposta aceitos para cada um dos comandos
B9+, que voc precisar implementar. ,ara simplificar, pode0se presumir que qualquer
outra resposta do servidor indica um erro fatal e aborta o envio da mensa$em. Na
realidade, o B9+, distin$ue entre erros transientes Lcdi$os de resposta 255M e
permanentes Lcdi$os de resposta 355M, e o remetente # autorizado a repetir os
comandos que provocaram um erro transiente. "e7a o 8pndice 4 da -./0:F( para
mais detalhes.
8l#m disso, quando voc abre uma cone5'o para o servidor, ele ir responder com o
cdi$o FF).
NotaA 8 -./0:F( permite o cdi$o F3( como resposta ao comando -/,+ +I para
indicar que o destinatrio n'o # um usurio local. "oc pode verificar manualmente
com o comando telnet o que o seu servidor B9+, local responde.
%i!as
8 maior parte do cdi$o que voc precisar completar # similar ao cdi$o que voc
escreveu no laboratrio de servidor Web. "oc pode us0lo aqui para a7ud0lo.
,ara facilitar o debu$ do seu pro$rama, n'o inclua lo$o de incio o cdi$o que abre o
socGet, mas as se$uintes definiesA !rom#erver e to#erver. Kesse modo, seu
pro$rama envia os comandos para o terminal. 8tuando como um servidor B9+,, voc
precisar fornecer os cdi$os de resposta correto. 6uando seu pro$rama funcionar,
adicione o cdi$o que abre o socGet para o servidor.
(2
!rom#erver 1 ne* @u!!eredReader%ne* 8nput#treamReader%#?stem.in))
to#erver 1 #?stem.out
8s linhas para abrir e fechar o socGet, por e5emplo, as linhas connection 1 ... no
construtor e a linha connection.close%) na fun'o close%), foram e5cludas como
comentrios por default.
/omearemos completando a fun'o parseRepl?%), que ser necessria em vrios
lu$ares. Na fun'o parseRepl?%), voc deve usar a classe #trin&6o2eniGer para
analisar as strin$s de resposta. "oc pode converter uma strin$ em um inteiro da
se$uinte formaA
8nt i 1 8nte&er.parse8nt%ar&v'F()
Na fun'o send7ommand%), voc deve usar a fun'o *rite@?tes%) para escrever os
comandos para o servidor. 8 vanta$em de usar *rite@?tes%) em vez de *rite%) #
que a primeira converte automaticamente as strin$s em bNtes, que s'o o que o servidor
espera. N'o se esquea de encerrar cada comando com a strin$ /->..
"oc pode repassar e5cees assimA
thro* ne* +xception%)
"oc n'o precisa se preocupar com os detalhes, desde que as e5cees neste laboratrio
se7am usadas apenas para sinalizar um erro, e n'o para dar informa'o detalhada sobre o
que est errado.
&'er!(!ios o$!ionais
+ente fazer os se$uintes e5erccios opcionais para tornar seu pro$rama mais sofisticado.
,ara estes e5erccios, ser necessrio modificar outras classes tamb#m L9ail/lient,
9essa$e, e 4nvelopeM.
)erifi!ar o endereo do remetente* 8 classe #?stem cont#m informaes
sobre o nome de usurio, e a classe 8net/ddress cont#m m#todos para
encontrar o nome do hospedeiro local. =se0as para construir o endereo do
remetente para o 4nvelope em vez de usar o valor fornecido pelo usurio no
campo .rom do cabealho.
(3
Cabealhos adi!ionais* Is e0mails $erados possuem apenas quatro campos no
cabealho, .rom, +o, Bub7ect e Kate. 8dicione outros campos de cabealho de
acordo com a -./0:FF, por e5emplo, 9essa$e0&K, UeN%ords. "erifique a -./
para definies dos diferentes campos.
+,lti$los destinatrios* 8t# este ponto, o pro$rama permite apenas enviar e0
mail a um nico destinatrio. 9odifique a interface de usurio para incluir um
campo /c e modifique o pro$rama para enviar e0mail a dois destinatrios. ,ara
aumentar o desafio, modifique o pro$rama para enviar e0mail a um nmero
arbitrrio de destinatrios.
+aior verifi!ao de erros* I cdi$o fornecido presume que todos os erros que
ocorrem durante a cone5'o B9+, s'o fatais. 8dicione cdi$o para distin$uir
entre erros fatais e n'o fatais e adicione um mecanismo para sinaliz0los ao
usurio. /heque o -./ para saber o si$nificado dos diferentes cdi$os de
resposta. 4ste e5erccio pode requerer $randes modificaes nas funes
send% )H send7ommand% ) e parseRepl?% ).
Consultando o %-S
I KNB LKomain Name BNstemM armazena informaes em re$istros de recursos. Is
nomes para mapeamentos de endereo &, s'o armazenados nos re$istros de recursos do
tipo 8 L8ddressM. Is re$istros do tipo NB LNameBerverM $uardam informaes sobre
servidores de nomes e os re$istros do tipo 9V L9ail eVchan$eM dizem qual servidor
est manipulando a entre$a de correio no domnio.
I servidor que voc precisa encontrar # o que est manipulando o correio para o
domnio da sua escola. ,rimeiramente, # preciso encontrar o servidor de nomes da
escola e ent'o consult0lo pelo 9V0hospedeiro. Bupondo que voc est na Bomeschool
e que seu domnio # someschool.edu, voc pode fazer o se$uinteA
(. 4ncontrar o endereo de um servidor de nomes para o domnio do maior nvel .edu
LNB querNM
F. /onsultar o servidor de nomes de .edu , que buscar o servidor de nomes do domnio
someschool.edu , para conse$uir o endereo do servidor de nomes da Bomeschool.
LNB querNM
(?
J. ,er$untar ao servidor de nomes da Bomeschool pelos re$istros09V para o domnio
someschool.edu. L9V querNM
,er$unte ao administrador do seu sistema local sobre como realizar manualmente
per$untas ao KNB.
No =N&V, voc pode per$untar manualmente ao KNB com o comando nsloo2up. 8
sinta5e desse comando encontra0se a se$uir. Note que o ar$umento host pode ser um
domnio.
Normal querN nsloo2up host
Normal querN usin$ a $iven Berver nsloo2up host server
NB0querN nsloo2up Yt?pe1J# host
9V0querN nsloo2up Yt?pe1OZ host
8 resposta para o 9V0querN pode conter mltiplas contas de e0mail. /ada uma delas #
precedida por um nmero que ser o valor preferencial para este servidor. "alores
menores de preferncia indicam servidores preferidos de modo que voc possa usar o
servidor com o valor mais bai5o de preferncia.
S+TPConne!tion*.ava
4ste # o cdi$o para a classe B9+,/onnection que voc precisar completar. I cdi$o
para as outras trs classes # fornecido neste ponteiro.
8mport java.net.*
8mport java.io.*
8mport java.util.*
/**
* /bre uma conex5o #O6. para o servidor de correio e envia um eMmail.
*
*/
public class #O6.7onnection $
/* A soc2et para o servidor */
private #oc2et connection
(@
/* 6rechos para leitura e escrita no soc2et */
private @u!!eredReader !rom#erver
private DataAutput#tream to#erver
private static !inal int #O6.[.AR6 1 T\
private static !inal #trin& 7R9: 1 ;<r<n=
/* +stamos conectados3 ]samos close%) para determinar o que
!aGer.*/
private boolean is7onnected 1 !alse
/* 7rie um objeto #O6.7onnection. 7rie os soc2ets e os
trechos associados. 8nicie a conex5o #O6.. */
public #O6.7onnection%+nvelope envelope) thro*s 8A+xception $
// connection 1 /* .reencher */
!rom#erver 1 /* .reencher */
to#erver 1 /* .reencher */
/* .reencher */
/* 9er uma linha do servidor e veri!icar se o cKdi&o de
resposta > TTF. #e n5o !orH lance uma 8A+xception. */
/* .reencher */
/* #O6. handsha2e. .recisamos do nome da m^quina local.
+nvie o comando hands2ha2e do #O6. apropriado. */
#trin& localhost 1 /* .reencher */
send7ommand% /* .reencher*/ )
is7onnected 1 true
-
/* +nvie a mensa&em. +screva os comandos #O6. corretos na ordem
correta. J5o veri!ique de errosH apenas lanceMos ao chamador. */
public void send%+nvelope envelope) thro*s 8A+xception $
/* .reencher */
/* +nvie todos os comandos necess^rios para enviar a mensa&em.
7hame o send7ommand%) para !aGer o trabalho sujo. J5o apanhe
a exce45o lan4ada pelo send7ommand%). */
/* .reencher */
-
/* :eche a conex5o. .rimeiroH termine no nCvel #O6.H ent5o !eche o
soc2et. */
(:
public void cloce%) $
is7onnected 1 !alse
tr? $
send7ommand% /* .reencher */ )
// connection.close%)
- catch %8A+xception e) $
#?stem.out.println%;]nable to lose connection: ; L e)
is7onnected 1 true
-
-
/* +nvie um comando #O6. ao servidor. 7heque se o cKdi&o de resposta
est^ de acordo com o R:7 8TR. */
/* .reencher */
/* +screver o comando do servidor e ler a resposta do servidor. */
/* .reencher */
/* .reencher */
/* 7heque se o cKdi&o de resposta do servidor > o mesmo do
par_metro rc. #e n5oH envie um 8A+xception. */
/* .reencher */
/* /nalise a linha de resposta do servidor. Retorne o cKdi&o de
resposta. */
private int parseRepl?%strin& repl?) $
/* .reencher */
-
/* Destructor. :echa a conex5o se al&o de ruim acontecer. */
protected void !inaliGe%) thro*s 6hro*able $
is%is7onnected) $
close%)
-
super.!inaliGe%)
-
-
(1
Agente usurio de !orreio: verso sim$lifi!ada
4ste laboratrio est dividido em duas partes. Na primeira, voc dever usar o telnet
para enviar e0mail manualmente atrav#s de um servidor de correio B9+,. Na se$unda,
voc ter de escrever o pro$rama Sava que realiza a mesma a'o.
Parte 1: &nviando e/mail !om telnet
+ente enviar um e0mail para voc mesmo. &sso si$nifica que voc precisa saber o nome
do hospedeiro do servidor de correio do seu prprio domnio. ,ara encontrar essa
informa'o, voc pode consultar o KNB para buscar o re$istro 9V que mant#m
informaes sobre seu domnio de correio. ,or e5emplo, bobTsomeschool.edu possui o
domnio de correio someschool.edu. I comando a se$uir consulta o KNB para encontrar
os servidores de correio responsveis pela entre$a de correio neste domnioA
nsloo2up Yt?pe1OZ someschool.edu
,ara a resposta a este comando, pode haver vrios servidores de correio que entre$am e0
mail para as cai5as de correio no domnio someschool.edu. Buponha que o nome de um
deles # mx1.someschool.edu. Nesse caso, o se$uinte comando estabelecer a cone5'o
+/, a este servidor de correio. LNote que a porta nmero F3 # especificada na linha de
comando.M
telnet mxR.someschool.edu T\
Neste ponto, o pro$rama telnet permitir a voc entrar com os comando B9+, e e5ibir
as respostas do servidor de correio. ,or e5emplo, a seqPncia de comandos a se$uir
envia um e0mail da 8lice para o Wob.
H+9A alice
O/89 :RAO: NaliceWcrepes.!rP
R7.6 6A: NbobWsomeschool.eduP
D/6/
#]@`+76: hello
Hi @obH Ho*as the *eather3 /lice.
F)
.
b]86
I protocolo B9+, foi ori$inalmente pro7etado para permitir <s pessoas intera$irem
manualmente com os servidores de correio em modo de conversa'o. ,or essa raz'o, se
voc di$itar um comando com sinta5e incorreta, ou com ar$umentos inaceitveis, o
servidor retornar uma mensa$em reportando isso e permitir que voc tente
novamente.
,ara completar esta parte do laboratrio, voc deve enviar uma mensa$em de e0mail
para voc mesmo e verificar se ela che$ou.
Parte 2: &nviando e/mail !om Java
8 Sava fornece uma 8,& para intera$ir com o sistema de correio da &nternet, que #
chamado Sava9ail. No entando, n'o usaremos esta 8,&, pois ela esconde os detalhes do
B9+, e da pro$rama'o de socGets. 4m vez disso, voc escrever um pro$rama Sava
que estabelece uma cone5'o +/, com um servidor de correio atrav#s da interface de
socGet e enviar uma mensa$em de e0mail.
"oc pode colocar todo seu cdi$o dentro do m#todo principal de uma classe chamada
EmailSender. 45ecute seu pro$rama com o simples comando a se$uirA
java +mail#ender
&sso si$nifica que voc incluir em seu cdi$o os detalhes da mensa$em de e0mail que
voc est tentando enviar.
8qui est um esqueleto do cdi$o que voc precisar para escreverA
import java.io.*
import java.net.*
public class +mail#ender
$
public static void main%#trin&%) ar&s) thro*s +xception
$
// +stabelecer uma conex5o 67. com o servidor de correio.
F(
// 7riar um @u!!eredReader para ler a linha atual.
8nput#tream is 1 soc2et.&et8nput#tream%)
8nput#treamReader isr 1 ne* 8nput#treamReader%is)
@u!!eredReader br 1 ne* @u!!eredReader%isr)
// 9er os cumprimentos do servidor.
#trin& response 1 br.read9ine%)
#?stem.out.println%response)
i! %Eresponse.starts"ith%;TTF=)) $
6hro* ne* +xception%;TTF repl? not received !rom server.=)
-
// .e&ar uma re!erBncia para o trecho de saCda do soc2et.
Autput#tream os 1 soc2et.&etAuput#tream%)
// +nviar o comando H+9A e pe&ar a resposta do servidor.
#trin& comand 1 ;Helo /lice<r<n=
#?stem.out.println%command)
os.*rite%command.&et@?tes%;]#M/#788=))
response 1 br.read9ine%)
#?stem.out.println%response)
i! %Eresponse.starts"ith%=T\F=)) $
thro* ne* +xception%;T\F repl? not received !rom server.=)
-
// +nviar o comando O/89 :RAO.
// +nviar o comando R+7. 6A.
// +nviar o comando D/6/.
// +nviar o dados da mensa&em.
// 6erminar com uma linha de um 0nico perCodo.
// +nviar o comando b]86.
-
-
FF
Tarefa de Programao 0: 1aborat#rio de 2%P $inger
Neste laboratrio, voc ir estudar um simples servidor de ,in$ da &nternet escrito em
lin$ua$em Sava e implementar um cliente correspondente. 8 funcionalidade provida por
esses pro$ramas # similar < dos pro$ramas de ,in$ padr'o disponveis nos sistemas
operacionais modernos, e5ceto aqueles que usam o =K, em vez do &/9, L&nternet
/ontrol 9essa$e ,rotocolM para se comunicar. LSava n'o prov meios diretos para
intera$ir com o &/9,.M
I protocolo ,in$ permite a uma mquina cliente enviar um pacote de dados para uma
mquina remota, a qual retornar o dado para o cliente sem modificaes Luma a'o
conhecida como ecoM. 4ntre outros usurios, o protocolo ,in$ permite aos hospedeiros
determinarem o tempo de resposta de outras mquinas.
/omplete o cdi$o para o servidor de ,in$ abai5o. Beu trabalho ser escrever o cliente
,in$.
C#digo do servidor
I cdi$o a se$uir implementa por completo o servidor de ,in$. "oc precisar compilar
e e5ecutar este cdi$o. 4stude0o cuidadosamente, pois ele ir a7ud0lo a escrever seu
cliente de ,in$.
import.java.io.*
import.java.net.*
import.java.util.*
/*
* #ervidor para processar as requisi4ces de .in& sobre ]D..
*/
public class .in&#erver
$
private static !inal doubl> 9A##[R/6+ 1 F.d
private static !inal int /X+R/I+[D+9/Q 1 RFF //milliseconds
public static void main%#trin&'(ar&s) thro*s +xception
$
// Abter o ar&umento da linha de comando.
FJ
i! %ar&s.len&th E1R) $
#?stem.out.pritln%;Required ar&uments: port=)
return
-
int port 1 8nte&er.parse8nt%ar&s'F()
// 7riar um &erador de n0meros aleatKrios para uso em simula45o
de perda de pacotes e atrasos na rede.
Random random 1 ne* Random%)
// 7riar um soc2et de data&rama para receber e enviar pacotes ]D.
atrav>s da porta especi!icada na linha de comando.
Data&ram#oc2et soc2et 1 ne* Data&ram#oc2et%port)
// 9oop de processamento.
*hile%true)
// 7riar um pacote de data&rama para comportar o pacote
]D. // de che&ada.
Data&ram.ac2et request 1 ne* Data&ram.ac2et%ne*
b?te'RFTU(HRFTU)
// @loquear at> que o hospedeiro receba o pacote ]D..
#oc2et.receive%request)
// 8mprimir os dados recebidos.
printData%request)
// Decidir se respondeH ou simula perda de pacotes.
i!%random.nextDouble%) N 9A##[R/6+) $
#?stem.out.println%;Repl? not sent.=)
7ontinue
-
// #imular o atraso da rede.
6hread.sleep%%int)%random.nextDouble) * T * /X+R/I+[D+9/Q))
// +nviar resposta.
8net/ddress clientHost 1 request.&et/ddress%)
8nt client.ort 1 request.&et.ort%)
@?te'(bu! 1 request.&etData%)
Data&ram.ac2et repl? 1 ne* Data&ram.ac2et%bu!H bu!.len&thH
clientHostH client.ort)
F2
#oc2et.send%repl?)
#?stem.out.println%;Repl? sent.=)
-
-
/*
* 8mprimir o dado de .in& para o trecho de saCda padr5o.
*/
private static void printData%Data&ram.ac2et request) thro*s
+xception
$
// Abter re!erBncias para a ordem de pacotes de b?tes.
b?te'( bu! 1 request.&etData%)
// +nvolver os b?tes numa cadeia de entrada vetor de b?tesH de
modo que vocB possa ler os dados como uma cadeia de b?tes.
@?te/rra?8nput#tream bais 1 ne* @?te/rra?8nputstream%bu!)
// +nvolver a cadeia de saCda do vetor b?tes num leitor de
cadeia de entradaH de modo que vocB possa ler os dados como uma
cadeia de caracteres.
8nput#treamReader isr 1 ne* 8nput#treamReader%bais)
// +nvolver o leitor de cadeia de entrada num leitor com
armaGena&emH de modo que vocB possa ler os dados de caracteres
linha a linha. %/ linha > uma seqeBncia de caracteres
terminados por al&uma combina45o de <r e <n.)
@u!!eredReader br 1 ne* @u!!eredReader%isr)
// A dado da mensa&em est^ contido numa 0nica linhaH ent5o leia
esta linha.
#trin& line 1 br.read9ine%)
// 8mprimir o endere4o do hospedeiro e o dado recebido dele.
#?stem.out.println%
;Received !rom= L
request.&et/ddress%).&etHost/ddress%)L
;:= L
ne* #trin&%line))
F3
-
-
I servidor fica num loop infinito de escuta pela che$ada de pacotes =K,. 6uando um
pacote che$a, o servidor simplesmente envia o dado encapsulado de volta para o cliente.
Perda de $a!otes
I =K, prov aplicaes com servio de transporte n'o confivel, pois as mensa$ens
podem se perder pela rede devido a um overflo% na fila do roteador ou por outras
razes. 4m contraste a isso, o +/, fornece aplicaes com um servio de transporte
confivel, e cada pacote perdido # retransmitido at# que ele se7a recebido com sucesso.
8plicaes que usam o =K, para comunica'o precisam implementar al$uma
se$urana separadamente no nvel de aplica'o Lcada aplica'o pode implementar uma
poltica diferente, de acordo com necessidades especficasM.
Kevido ao fato de a perda de pacotes ser rara, ou at# mesmo ine5istente, em uma rede
tpica, o servidor neste laboratrio in7eta perda artificial para simular os efeitos da perda
de pacotes na rede. I servidor possui um par!metro >IBBX-8+4, que determina qual a
porcenta$em de pacotes deve ser perdida.
I servidor tamb#m possui outro par!metro, 8"4-8Q4XK4>8Y, que # usado para
simular o atraso de transmiss'o ao enviar um pacote pela &nternet. "oc deve a7ustar o
8"4-8Q4XK4>8Y com um valor positivo quando o cliente e o servidor forem estar
na mesma mquina, ou quando as mquinas estiverem muito perto fisicamente na rede.
"oc pode a7ustar o 8"4-8Q4XK4>8Y em ) LzeroM para encontrar o tempo de
transmiss'o verdadeiro dos seus pacotes.
Com$ilando e e'e!utando o servidor
,ara compilar o servidor, faa o se$uinteA
javac .in&#erver.java
,ara e5ecutar o servidor, faa o se$uinteA
java .in&#erver port
F?
onde port # o nmero da porta que o servidor escuta. >embre que voc deve usar um
nmero de porta maior do que ()F2, pois apenas os processos e5ecutando no modo root
LadministradorM possuem privil#$io de usar portas menores que ()F2.
NotaA Be voc obtiver um erro de classe n'o encontrada quando e5ecutar o comando
acima, voc precisar dizer para o Sava olhar no diretrio atual para resolver as
referncias de classe. Nesse caso, os comandos s'oA
java Yclasspath . .in&#erver port
Sua tarefa: o !liente
"oc deve escrever o cliente de modo que ele envie () requisies de ,in$ para o
servidor, separadas por apro5imadamente ( se$undo. /ada mensa$em cont#m uma
car$a til de dados que inclui a palavra ,&NQ, um nmero de seqPncia, e uma marca
de tempo. 8ps enviar cada pacote, o cliente espera um se$undo para receber a
resposta. Be um se$undo se passar sem uma resposta do servidor, ent'o o cliente pode
supor que esse pacote ou o pacote de resposta do servidor se perdeu pela rede.
Dica: Copie e cole o PingSerer! renomeie o c"digo para PingClient e ent#o
modifi$ue%o.
"oc deve escrever o cliente de modo que ele inicie com o se$uinte comandoA
java .in&7lient host port
onde hospedeiro # o nome do computador em que o servidor est sendo e5ecutado e
port # o nmero da porta que ele est escutando. Note que voc pode e5ecutar o cliente
e o servidor em diferentes mquinas ou na mesma.
I cliente deve enviar () ,in$s para o servidor. /omo o =K, # um protocolo n'o
confivel, al$uns dos pacotes enviados pelo cliente ou pelo servidor podem ser
perdidos. ,or essa raz'o, o cliente n'o pode esperar indefinidamente pela resposta a
uma mensa$em de ,in$. "oc deve fazer com que o cliente espere at# um se$undo por
uma resposta; se nenhuma resposta for recebida, ele presume que o pacote foi perdido
durante a transmiss'o. "oc precisar pesquisar a 8,& para o Kata$ramBocGet de modo
a descobrir como se a7usta o valor de tempo de e5pira'o num socGet de data$rama.
8o desenvolver seu cdi$o, voc deve e5ecutar o servidor de ,in$ em sua mquina e
testar seu cliente enviando pacotes para o hospedeiro local Lou, (F@.).).(M. 8ps o
F@
completo debu$ do seu cdi$o, voc deve ver como sua aplica'o se comunica atrav#s
da rede com um servidor de ,in$ sendo e5ecutado por um outro membro da classe.
3ormato das mensagens
8s mensa$ens de ,in$ neste laboratrio s'o formatadas de modo simples. /ada
mensa$em cont#m uma seqPncia de caracteres terminados por um caracter de retorno
LrM e um carter de mudana de linha LnM. 8 mensa$em cont#m a se$uinte strin$A
.8JI sequence[number time 7R9:
onde sequenceXnumber comea em ) LzeroM e pro$ride at# 1, para cada mensa$em
sucessiva de ,in$ enviada pelo cliente; time # o tempo do momento em que o cliente
enviou a mensa$em e /->. representa o retorno e linha de caracteres que finalizam a
linha.
&'er!(!ios o$!ionais
6uando voc terminar de escrever seu cdi$o, pode tentar resolver os se$uintes
e5erccios.
(M No ponto atual, o pro$rama calcula o tempo de transmiss'o de cada pacote e os
imprime individualmente. 9odifique isso para corresponder ao modo de funcionamento
do pro$ramas de ,in$ padres. "oc dever informar os -++s mnimo, m5imo e
m#dio. LfcilM
FM I pro$rama bsico envia um novo ,in$ imediatamente quando recebe uma resposta.
9odifique0o de modo que ele envie e5atamente ( ,in$ por se$undo, similar ao modo
como pro$ramas de ,in$ padres funcionam. KicaA =se as classes +imer e +imer+asG
em 7ava.util. LdifcilM
JM Kesenvolva duas novas classes, -eliable=dpBender e -eliable=dp-eceiver, que
ser'o usadas para enviar e receber dados de maneira confivel sobre =K,. ,ara fazer
isso, voc precisar pro7etar um protocolo Lcomo o +/,M em que o destinatrio dos
dados envia acGno%led$ements de volta ao remetente para indicar que o dado foi
recebido. "oc pode simplificar o problema apenas provendo um transporte
unidirecional de dados de aplica'o do remetente ao destinatrio. /omo seus
F:
e5perimentos podem ser feitos em um ambiente com pouca ou nenhuma perda de
pacotes &,, voc dever simular perda de pacotes. LdifcilM
F1
Tarefa de Programao 4: Pro'5 !a!he
Neste laboratrio, voc desenvolver um pequeno servidor Web pro5N que tamb#m
ser capaz de fazer cache de p$inas Web. 4ste ser um servidor pro5N bem simples,
que apenas entender requisies Q4+ simples, mas ser capaz de manipular todos os
tipos de ob7etos, n'o apenas p$inas *+9>, mas tamb#m ima$ens.
C#digo
I cdi$o est dividido em trs classesA
, .rox?7ache R compreende o cdi$o de inicializa'o do pro5N e o cdi$o para tratar
as requisies.
, HttpRequest R cont#m as rotinas para analisar e processar as mensa$ens que che$am
do cliente.
, HttpResponse R encarre$ada de ler as respostas vindas do servidor e process0las.
Beu trabalho ser completar o pro5N de modo que ele se7a capaz de receber requisies,
encaminh0las, ler as respostas e retorn0las aos clientes. "oc precisar completar a
classes .rox?7ache, HttpRequest, e HttpResponse. Is lu$ares onde voc precisar
preencher o cdi$o estar'o marcados com OZ ,reencher ZO. /ada lu$ar pode e5i$ir uma
ou mais linhas de cdi$o.
NotaA /onforme ser e5plicado abai5o, o pro5N utiliza KataIutputBtreams para
processar as respostas dos servidores. &sso ocorre porque as respostas s'o uma mistura
de te5to e dados binrios, e a nica cadeia de entrada em Sava que permite trabalhar
com ambos ao mesmo tempo # a KataIutputBtream. ,ara obter o cdi$o a ser
compilado, voc deve usar o ar$umento Rdeprecation para o compilador, conforme
se$ueA
javac Ydeprecation *.java
Be voc n'o usar o fla$ Rdeprecation, o compilador ir se recusar a compilar seu cdi$o.
J)
&'e!utando o $ro'5
,ara e5ecutar o pro5N faa o se$uinteA
java .rox?7ache port
onde port # o nmero da porta que voc quer que o pro5N escute pela che$ada de
cone5es dos clientes.
Configurando seu bro6ser
"oc tamb#m vai precisar confi$urar seu bro%ser Web para usar o pro5N. &sso depende
do seu bro%ser Web. No &nternet 45plorer, voc pode a7ustar o pro5N em C&nternet
IptionsD na barra /onnection em >8N settin$s. No Netscape Le bro%sers derivados,
como 9ozillaM, voc pode a7ustar o pro5N em 4dit ,references e ent'o selecionar
8dvanced e ,ro5ies.
4m ambos os casos, voc precisar informar o endereo do pro5N e o nmero da porta
que voc atribuiu a ele quando foi inicializado. "oc pode e5ecutar o pro5N e o bro%ser
na mesma mquina sem nenhum problema.
3un!ionalidades do $ro'5
I pro5N funciona da se$uinte formaA
(. I pro5N escuta por requisies dos clientes.
F. 6uando h uma requisi'o, o pro5N $era um novo thread para tratar a requisi'o e
cria um ob7eto *ttp-equest que cont#m a requisi'o.
J. I novo thread envia a requisi'o para o servidor e l a resposta do servidor dentro
de um ob7eto *ttp-esponse.
2. I thread envia a resposta de volta ao cliente requisitante.
Bua tarefa # completar o cdi$o que manipula o processo acima. 8 maior parte dos erros
de manipula'o em pro5N # muito simples, e o erro n'o # informado ao cliente. 6uando
ocorrem erros, o pro5N simplesmente pra de processar a requisi'o e o cliente
eventualmente recebe uma resposta por causa do es$otamento da temporiza'o.
J(
8l$uns bro%sers tamb#m enviam uma requisi'o por vez, sem usar cone5es paralelas.
4specialmente em p$inas com vrias ima$ens, pode haver muita lentid'o no
carre$amento delas.
Ca!hing
-ealizar o cachin$ das respostas no pro5N fica como um e5erccio opcional, pois isso
demanda uma quantidade si$nificativa de trabalho adicional. I funcionamento bsico
do cachin$ # descrito a se$uirA
(. 6uando o pro5N obt#m uma requisi'o, ele verifica se o ob7eto requisitado est no
cache; se estiver, ele retorna o ob7eto do cach, sem contatar o servidor.
F. Be o ob7eto n'o estiver no cache, o pro5N traz o ob7eto do servidor, retorna0o ao
cliente e $uarda uma cpia no cache para requisies futuras.
Na prtica, o pro5N precisa verificar se as respostas que possui no cache ainda s'o
vlidas e se s'o a resposta correta < requisi'o do cliente. "oc pode ler mais sobre
cachin$ e como ele # tratado em *++, na -./0F)?:. ,ara este laboratrio, isto #
suficiente para implementar a simples poltica acima.
%i!as de $rogramao
8 maior parte do cdi$o que voc precisar escrever est relacionada ao processamento
de requisies e respostas *++,, bem como o tratamento de socGets Sava.
=m ponto notvel # o processamento das respostas do servidor. 4m uma resposta
*++,, os cabealhos s'o enviados como linhas 8B/&&, separadas por seqPncias de
caracteres /->.. Is cabealhos s'o se$uidos por uma linha vazia e pelo corpo da
resposta, que pode ser dados binrios no caso de ima$ens por e5emplo.
I Sava separa as cadeias de entrada conforme elas se7am te5to ou binrio. &sso apresenta
um pequeno problema neste caso. 8penas Kata&nputstreams s'o capazes de tratar te5to
e binrio simultaneamente; todas as outras cadeias s'o puro te5to Le5emploA
Wuffered-eaderM, ou puro binrio Le5emploA Wuffered&nputBtreamM, e mistur0los no
mesmo socGet $eralmente n'o funciona.
JF
I Kata&nputstream possui um pequeno defeito, pois ele n'o # capaz de $arantir que o
dado lido possa ser corretamente convertido para os caracteres corretos em todas as
plataformas Lfun'o Kata&nputstream.read>ineLM M. No caso deste laboratrio, a
convers'o $eralmente funciona, mas o compilador ver o m#todo
Kata&nputstream.read>ineLM como CdeprecatedD LobsoletoM e se recusar a compil0lo
sem o fla$ Rdeprecation.
[ altamente recomendvel que voc utilize o Kata&nputBtream para ler as respostas.
&'er!(!ios o$!ionais
6uando voc terminar os e5erccios bsicos, tente resolver os se$uintes e5erccios
opcionais.
(. 9elhor tratamento de erros. No ponto atual, o pro5N n'o possui nenhum tratamento
de erro. &sso pode ser um problema especialmente quando o cliente requisita um ob7eto
que n'o est disponvel, desde que a resposta usual C2)2 Not .oundD n'o contenha
corpo de resposta e o pro5N presuma que e5iste um corpo e tente l0lo.
F. Buporte para o m#todo ,IB+. I pro5N simples suporta apenas o m#todo Q4+.
8dicione o suporte para ,IB+ incluindo o corpo de requisi'o enviado na requisi'o
,IB+.
J. 8dicione cachin$. 8dicione o funcionamento simples de cachin$ descrito acima. N'o
# preciso implementar nenhuma poltica de substitui'o ou valida'o. Bua
implementa'o deve ser capaz de escrever respostas ao disco Le5emploA o cacheM e
traz0las do disco quando voc obtiver um encontro no cache. ,ara isso, voc precisa
implementar al$uma estrutura interna de dados no pro5N para re$istrar quais ob7eto
est'o no cache e onde eles se encontram no disco. "oc pode colocar essa estrutura de
dados na memria principal; n'o # necessrio faz0la persistir aps processos de
desli$amento.
JJ
Tarefa de Programao 7: 8m$lementando um $roto!olo de trans$orte !onfivel
)iso geral
Neste laboratrio, voc escrever o cdi$o de nvel de transporte de envio e recep'o,
implementando um simples protocolo de transferncia de dados confivel. * duas
verses deste laboratrioA a vers'o protocolo bit alternante e a vers'o Qo0WacG0N. I
laboratrio ser divertido, visto que sua implementa'o ir diferir um pouco do que
seria e5i$ido numa situa'o do mundo real.
/omo voc provavelmente n'o possui mquinas standalone Lcom um IB que pode ser
modificadoM, seu cdi$o precisar ser e5ecutado num ambiente de hard%areOsoft%are
simulado. No entanto, a interface de pro$rama'o fornecida para suas rotinas, por
e5emplo, o cdi$o que poderia chamar suas entidades de cima e de bai5o # bem
pr5imo do que # feito num ambiente =N&V atual. LNa verdade, as interfaces de
soft%are descritas neste e5erccio s'o muito mais realistas do que remetentes e
destinatrios de loop infinito que muitos te5tos descrevem.M &nterrup'oOacionamento de
temporizadores ser'o tamb#m simulados, e interrupes feitas pelo temporizador far'o
com que seu temporizador manipule rotinas a serem ativadas.
9otinas :ue vo!; ir es!rever
Is procedimentos que voc escrever s'o para a entidade de envio L8M e para a entidade
de recep'o LWM. 8penas transferncia unidirecional de dados Lde 8 para WM # e5i$ida.
Naturalmente, o lado W dever enviar pacotes ao lado 8 para confirmar Lpositiva ou
ne$ativamenteM a recep'o do dado. Buas rotinas devem ser implementadas na forma
dos procedimentos descritos abai5o. 4sses procedimentos ser'o chamados pelos Le
chamar'oM procedimentos que escrevi com um ambiente de rede emulado. +oda a
estrutura do ambiente # mostrada na .i$ura >ab.J0( Lestrutura do ambiente emuladoM.
8 unidade de dados que trafe$a entre as camadas superiores e seus protocolos # uma
mensa$em, que # declarada comoA
#truct ms& $
7har data'TF(
-
J2
4ssa declara'o, e todas as outras estruturas de dados e rotinas de emulador, bem como
rotinas de ponta Le5emploA aquelas que voc precisa completarM est'o no arquivo
pro$F.c, descrito mais tarde. Bua entidade de envio receber dados em blocos de F)0
bNtes da camada 3; sua entidade de recep'o dever entre$ar blocos de F) bNtes de
dados recebidos corretamente para a camada 3 do lado destinatrio.
8 unidade de dados que trafe$a entre suas rotinas e a camada de rede # o pacote, que #
declarado comoA
struct p2t $
int seqnum
int ac2num
int chec2sum
char pa?load'TF(
-
Buas rotinas ir'o preencher o campo car$a til do dado da mensa$em vinda da camada
3. Is outros campos da mensa$em ser'o usados por seus protocolos para asse$urar
entre$a confivel.
8s rotinas que voc escrever est'o detalhadas abai5o. /onforme dito acima, tais
procedimentos no mundo real seriam parte do sistema operacional e chamados por
outros procedimentos no sistema operational.
A<out$ut=message>, onde messa&e # a estrutura do tipo ms&, contendo dados que
ser'o enviados para o lado W. 4sta rotina ser chamada sempre que a camada superior
do lado L8M tenha uma mensa$em a ser enviada. 4ste # o trabalho do seu protocolo para
asse$urar que os dados em tal mensa$em se7am entre$ues em ordem, e corretamente,
para a camada superior do lado destinatrio.
A<in$ut=$a!?et>, onde pac2et # a estrutura do tipo p2t. 4sta rotina ser chamada
sempre que um pacote enviado pelo lado W Le5emploA como resultado de um
tola?erd%) feito por um procedimento do lado WM che$a ao lado 8. pac2et # o pacote
Lpossivelmente corrompidoM enviado pelo lado W.
A<timeinterru$t=> 4sta rotina ser chamada quando o temporizador de 8 e5pirar
L$erando uma interrup'o no temporizadorM. "oc provavelmente usar esta rotina para
J3
controlar a retransmiss'o dos pacotes. "e7a starttimer%) e stoptimer%) abai5o para
ver como o temporizador # acionado e interrompido.
A<init=> 4sta rotina ser chamada apenas uma vez, antes de qualquer outra rotina do
lado 8 ser chamada. 4la pode ser usada para fazer qualquer inicializa'o requerida.
B<in$ut=$a!?et>, onde pac2et # a estrutura do tipo p2t. 4sta rotina ser chamada
sempre que um pacote enviado pelo lado 8 Le5emploA como resultado de um
tola?erd%) feito por um procedimento do lado 8M che$ar ao lado 8. pac2et # o pacote
Lpossivelmente corrompidoM enviado pelo lado 8.
B<init=> 4sta rotina ser chamada apenas uma vez, antes de qualquer outra rotina do
lado W ser chamada. 4la pode ser usada para fazer qualquer inicializa'o requerida.
8nterfa!es de soft6are
Is procedimentos descritos acima s'o aqueles que voc ir escrever. 8s rotinas a se$uir
foram escritas e podem ser chamadas pelas suas rotinasA
starttimer=!alling<entit5@ in!rement>, em que callin&[entit? ser C)D Lpara
acionar o temporizador do lado 8M ou C(D Lpara acionar o temporizador do lado WM, e
increment # um valor flutuante que indica a quantidade de tempo que se passou antes
de o temporizador ser interrompido. I temporizador de 8 dever ser acionado Lou
interrompidoM apenas pelas rotinas do lado 8, similarmente para o temporizador do lado
W. ,ara dar uma id#ia do valor apropriado de incremento a ser usado, um pacote
enviado dentro de uma rede leva em m#dia 3 unidades de tempo para che$ar ao outro
lado quando n'o h outras mensa$ens no meio.
sto$timer=!alling<entit5>, onde callin&[entit? ser C)D Lpara acionar o
temporizador do lado 8M ou C(D Lpara acionar o temporizador do lado WM.
tola5er0=!alling<entit5@$a!?et>, onde callin&[entit? ser C)D Lpara envio do lado
8M ou C(D Lpara envio do lado WM, e pac2et # a estrutura do tipo p2t. 8o chamar esta
rotina, um pacote ser enviado pela rede, destinado a outra entidade.
tola5er7=!alling<entit5@$a!?et>, onde callin&[entit? ser C)D Lentre$a do lado 8
para a camada 3M ou C(D Lentre$a do lado W para a camada 3M, e messa&e # a estrutura
do tipo ms&. 4m transferncia de dados unidirecional, voc poderia apenas ser chamado
J?
com callin&[entit? i$ual a C(D Lentre$a para o lado WM. 8o chamar esta rotina, os
dados passar'o para a camada 3.
" ambiente de rede simulado
=ma chamada ao procedimento tola?erd%) envia pacotes para o meio Le5emploA
dentro da camada de redesM. Beus procedimentos /[input%) e @[input%) s'o
chamados quando um pacote deve ser entre$ue do meio para a sua camada de protocolo.
I meio # capaz de corromper e perder pacotes. 4le n'o ir reordenar os pacotes.
6uando voc compilar seus procedimentos com os que s'o fornecidos aqui e e5ecutar o
pro$rama resultante, ser necessrio especificar os valores de acordo com o ambiente de
rede simuladoA
-,mero de mensagens $ara simular* 9eu emulador Le suas rotinasM ir parar
quando esse nmero de mensa$ens tiver sido passado para a camada 3, n'o importando
se todas as mensa$ens foram, ou n'o, entre$ues corretamente. 4nt'o, voc n'o precisa
se preocupar com mensa$ens n'o entre$ues ou n'o confirmadas que ainda estiverem no
seu remetente quando o emulador parar. Note que se voc a7ustar esse valor para C(D,
seu pro$rama ir terminar imediatamente, antes de as mensa$ens serem entre$ues ao
outro lado. >o$o, esse valor deve ser sempre maior do que C(D.
Perdas* "oc dever especificar a probabilidade de perda de pacotes. =m valor de ),(
si$nifica que um em cada dez pacotes Lna m#diaM ser perdido.
Corrom$imento* "oc dever especificar a probabilidade de perda de pacotes. =m
valor de ),F si$nifica que um em cada cinco pacotes Lna m#diaM ser corrompido. Note
que os campos de contedo da car$a til, seqPncia, acG, ou checGsum podem ser
corrompidos. Beu checGsum dever ent'o incluir os campos de dados, seqPncia e acG.
Tra!ing* 87ustar um valor de tracin$ de C(D ou CFD imprimir informaes teis sobre
o que est acontecendo dentro da emula'o Le5emploA o que ocorre com pacotes e
temporizadoresM. =m valor de tracin$ de C)D desli$a esta op'o. =m valor maior do que
CFD e5ibir todos os tipos de mensa$ens especiais que s'o prprias para a depura'o do
meu emulador. I valor CFD pode ser til para voc fazer o debu$ do seu cdi$o.
9antenha em mente que implementaes reais n'o provem tais informaes sobre o
que est acontecendo com seus pacotes.
J@
+Adia de tem$o entre mensagens do remetente da !amada 7* "oc pode a7ustar
este valor para qualquer valor positivo diferente de zero. Note que quanto menor o valor
escolhido, mais rpido os pacotes che$ar'o ao seu remetente.
)erso $roto!olo bit alternante deste laborat#rio
"oc escrever os procedimentos /[output%)H /[input%)H /[timerinterrupt%)H
/[init%)H @[input%) e @[init%) que, 7untos, implementar'o uma transferncia
pare0e0espere Le5emploA o protocolo bit alternante referido como rdtJ.) no te5toM
unidirecional de dados do lado 8 para o lado W. Seu $roto!olo dever usar mensagens
ACB e -ACB*
4scolha um valor bem alto para a m#dia de tempo entre mensa$ens da camada 3 do
remetente, assim ele nunca ser chamado enquanto ainda tiver pendncias, isto #,
mensa$ens n'o confirmadas que ele este7a tentando enviar ao destinatrio. Bu$iro que se
escolha um valor de (.))). -ealize tamb#m uma verifica'o em seu remetente para ter
certeza de que quando /[output%) # chamado, n'o ha7a nenhuma mensa$em em
tr!nsito. Be houver, voc pode simplesmente i$norar LdescartarM os dados que est'o
sendo passados para a rotina /[output%).
/oloque seus procedimentos em um arquivo chamado pro$F.c. "oc precisar da
vers'o inicial deste arquivo, contendo as rotinas de emula'o que escrevemos para voc,
e as chamadas para seus procedimentos. ,ara obter este pro$rama, acesse
httpAOO$aia.cs.umass.eduOGuroseOtransportOpro$F.c.
4ste laboratrio pode ser feito em qualquer mquina com suporte a /. 4le n'o faz uso
de caractersticas =N&V. L"oc pode simplesmente copiar o arquivo pro$F.c em
qualquer mquina e sistema operacional de sua escolhaM.
-ecomendamos0lhe que tenha em m'os uma lista$em do cdi$o, um documento de
pro7eto e uma sada de amostra. ,ara sua sada de amostra, seus procedimentos podem
imprimir uma mensa$em sempre que um evento ocorrer no seu remetente ou
destinatrio La che$ada de uma mensa$emOpacote, ou uma interrup'o de temporizadorM
bem como qualquer a'o tomada como resposta. "oc pode querer ter uma sada para
uma e5ecu'o at# o ponto Lapro5imadamenteM quando () mensa$ens tiverem sido
confirmadas L8/UM corretamente pelo destinatrio, uma probabilidade de perda de ),(,
J:
e uma probabilidade de corrup'o de ),J, e um nvel de trace de F. 8note sua impress'o
com uma caneta colorida mostrando como seu protocolo recuperou corretamente uma
perda ou corrup'o de pacote.
N'o dei5e de ler as Cdicas teisD para este laboratrio lo$o aps a descri'o da vers'o
QoXWacG0N.
)erso Co/Ba!?/- deste laborat#rio
"oc escrever os procedimentos /[output%)H /[input%)H /[timerinterrupt%)H
/[init%)H @[input%) e @[init%) e 7untamente implementar uma transferncia
unidirecional de dados do lado 8 para o lado W, com tamanho de 7anela i$ual a :. Beu
protocolo dever usar mensa$ens 8/U e N8/U. /onsulte a vers'o protocolo bit
alternante acima para informaes sobre como obter e emulador de rede.
[ altamente recomendvel que voc implemente primeiro o laboratrio mais fcil Lbit
alternanteM e ent'o estenda seu cdi$o para implementar o mais difcil LQo0WacG0NM.
8credite, isso n'o ser perda de tempo\ No entanto, al$umas novas consideraes para
o seu cdi$o Qo0WacG0N Lque n'o se aplicam ao protocolo bit alternanteM s'oA
A<out$ut=message>, onde messa&e # uma estrutura do tipo ms& contendo dados para
serem enviados ao lado W.
8$ora sua rotina /[output%) ser chamada al$umas vezes quando houver pendncias,
mensa$ens n'o confirmadas no meio R implicando a necessidade de voc ter um buffer
para mensa$ens mltiplas em seu remetente. "oc tamb#m precisar do buffer devido <
natureza do Qo0WacG0NA al$umas vezes seu remetente ser chamado mas n'o ser capaz
de enviar a nova mensa$em porque ela cai fora da 7anela.
4nt'o voc dever se preocupar em armazenar um nmero arbitrrio de mensa$ens.
"oc pode ter um valor finito para o nmero m5imo de buffers disponveis em seu
remetente Lpara 3) mensa$ensM e pode simplesmente abortar Ldesistir e sairM se todos os
3) buffers estiverem em uso de uma vez LNota: usando os valores acima, isso nunca
dever acontecerM. No mundo real, naturalmente, deveria haver uma solu'o mais
ele$ante para o problema de buffer finito.
A<timerinterru$t=> 4sta rotina ser chamada quando o temporizador de 8 e5pirar
L$erando uma interrup'o de temporizadorM. >embre que voc possui apenas um
J1
temporizador, e pode ter muitas pendncias e pacotes n'o confirmados no meio; ent'o,
pense um pouco em como usar este nico temporizador.
/onsulte a vers'o protocolo bit alternante para uma descri'o $eral do que voc precisa
ter em m'os. "oc pode querer ter uma sada para uma e5ecu'o que se7a $rande o
bastante de modo que pelo menos F) mensa$ens se7am transferidas com sucesso do
remetente ao destinatrio Le5emploA o remetente recebe o 8/U para estas mensa$ensM,
uma probabilidade de perda de ),F, e uma probabilidade de corrup'o de ),F, e um nvel
de trace de F, e um tempo m#dio entre che$adas de (). "oc pode anotar sua impress'o
com uma caneta colorida mostrando como seu protocolo recuperou corretamente uma
perda ou corrup'o de pacote.
,ara !rAdito e'tra, voc pode implementar transferncia bidirecional de mensa$ens.
Nesse caso, as entidades 8 e W operam tanto como remetente quanto como destinatrio.
"oc tamb#m pode sobrepor confirmaes nos pacotes de dados Lou pode escolher n'o
faz0loM. ,ara fazer meu emulador entre$ar mensa$ens da camada 3 para sua rotina
WXoutputLM, # preciso trocar o valor declarado de W&K&-4/+&IN8> de ) para (.
%i!as ,teis
Soma de verifi!ao* "oc pode usar qualquer solu'o de soma de verifica'o que
quiser. >embre que o nmero de seqPncia e o campo acG tamb#m podem ser
corrompidos. Bu$erimos uma soma de verifica'o como o do +/,, que consiste na
soma dos valores LinteiroM dos campos de seqPncia e do campo acG adicionada < soma
caracter por caracter do campo car$a til do pacote Le5emploA trate cada caracter como
se ele fosse um inteiro de : bits e apenas adicione0os 7untosM.
Note que qualquer CestadoD compartilhado entre suas rotinas deve estar na forma de
variveis $lobais. Note tamb#m que qualquer informa'o que seus procedimentos
precisam salvar de uma invoca'o para a pr5ima tamb#m deve ser uma varivel $lobal
Lou estticaM. ,or e5emplo, suas rotinas precisam manter uma cpia de um pacote para
uma possvel retransmiss'o. Nesse caso, seria provavelmente uma boa id#ia uma
estrutura de dados ser uma varivel $lobal no seu cdi$o. Note, no entanto, que se uma
das suas variveis # usada pelo seu lado remetente, essa varivel n'o deve ser acessada
pela entidade do lado destinatrio, pois, no mundo real, a comunica'o entre entidades
conectadas apenas por um canal de comunica'o n'o compartilha variveis.
2)
* uma varivel $lobal flutuante chamada time, que voc pode acessar de dentro do
seu cdi$o para au5ili0lo com dia$nsticos de mensa$ens.
C"+&C& S8+P1&S* 87uste as probabilidades de perda e corrup'o para zero e
teste suas rotinas. 9elhor ainda, pro7ete e implemente seus procedimentos para o caso
de nenhuma perda ou corrup'o. ,rimeiro, faa0as funcionar; ent'o trate o caso de uma
dessas probabilidades n'o ser zero e, finalmente, de ambas n'o serem zero.
%ebugging* -ecomendamos que voc a7uste o nvel de trace para F e coloque vrios
printf em seu cdi$o enquanto faz a depura'o de seus procedimentos.
-,meros aleat#rios* I emulador $era perda de pacotes e erros usando um $erador de
nmeros aleatrios. Nossa e5perincia passada # que $eradores de nmeros aleatrios
podem variar bastante de uma mquina para outra. "oc pode precisar modificar o
cdi$o do $erador de nmeros aleatrios no emulador que estamos fornecendo. Nossas
rotinas do emulador possuem um teste para ver se o $erador de nmeros aleatrios em
sua mquina funcionar com o nosso cdi$o. Be voc obtiver uma mensa$em de erroA
[ provvel que o $erador de nmeros aleatrios em sua mquina se7a diferente
daquele que este emulador espera. .avor verificar a rotina 7imsrandLM no cdi$o
do emulador. Kesculpe.
ent'o voc saber que precisa olhar como os nmeros aleatrios s'o $erados na rotina
7imsrandLM; ve7a os comentrios nessa rotina.
DEA
6uando pensamos neste laboratrio em nosso curso de introdu'o de redes, os
estudantes enviaram vrias questes. Be voc estiver interessado em v0las, acesseA
httpAOO$aia.cs.umass.eduOGuroseOtransportOpro$rammin$Xassi$nmentX68.htm.
2(
Tarefa de Programao F: 8m$lementando um algoritmo
)iso geral
Neste laboratrio, voc escrever um con7unto distribudo de procedimentos que
implementam um roteamento de vetor de dist!ncias assncrono distribudo para a rede
mostrada na .i$ura >ab.20(.
3igura 1ab*4/1: +opolo$ia de rede e custos dos enlaces para o laboratrio de
roteamento de vetor de dist!ncias
Tarefa bsi!a
9otinas :ue vo!; ir es!rever* ,ara a parte bsica da tarefa, voc escrever as
se$uintes rotinas que e5ecutar'o assincronamente dentro do ambiente emulado que
escrevemos para esta tarefa.
,ara o n C)D, voc escrever as rotinasA
rtinit0() 4sta rotina ser chamada uma vez no incio da emula'o. rtinitF%) n'o
possui ar$umentos. 4la dever inicializar a tabela de dist!ncias no n C)D para refletir os
custos (, J e @ para os ns (, F e J, respectivamente. Na .i$ura (, todos os linGs s'o
bidirecionais, e os custos em ambas as direes s'o idnticos. 8ps inicializar a tabela
de dist!ncia, e qualquer outra estrutura de dados necessria para sua rotina do n C)D,
ela dever ent'o enviar aos vizinhos diretamente conectados Lneste caso, (, F, e JM o
custo dos seus caminhos de menor custo para todos os outros ns da rede. 4sta
2F
informa'o de custo mnimo # enviada para os ns vizinhos em um pacote de rotina
atrav#s da chamada rotina tola?erT%), conforme descrita abai5o. I formato do pacote
de rotina # descrito abai5o tamb#m.
rtupdate0()(struct rtpkt *rcvdpkt). 4sta rotina ser chamada quando o n C)D
receber um pacote de rotina enviado por um de seus vizinhos diretamente conectados. I
par!metro *rcvdp2t # um ponteiro para o pacote que foi recebido.
rtupdateF%) # o Ccora'oD do al$oritmo de vetor de dist!ncias. I valor que ele recebe
num pacote de rotina vindo de al$um outro n i cont#m o custo do caminho mais curto
de i para todos os outros ns da rede. rtupdateF%) usa estes valores recebidos para
atualizar sua prpria tabela de dist!ncia Lcomo especificado pelo al$oritmo de vetor de
dist!nciasM. Be o seu prprio custo mnimo para outro n mudar como resultado da
atualiza'o, o n C)D informa a todos os vizinhos diretamente conectados sobre essa
mudana em custo mnimo enviando para eles um pacote de rotina. -essaltamos que no
al$oritmo de vetor de dist!ncias, apenas os ns diretamente conectados ir'o trocar
pacotes de rotina. ,ortanto, os ns ( e F se comunicam entre si, mas os ns ( e J n'o.
/onforme vimos em aula, a tabela de dist!ncia dentro de cada n # a principal estrutura
de dados usada pelo al$oritmo de vetor de dist!ncias. "oc achar conveniente declarar
a tabela de dist!ncia como uma disposi'o 20por02 de int]s, onde a entrada 'iHj( na
tabela de dist!ncia no n C)D # o custo atual do n C)D para o n i pelo vizinho direto &.
Be C)D n'o estiver diretamente conectado ao &, voc pode i$norar essa entrada.
=saremos a conven'o de que o valor inteiro 111 # infinito.
8 .i$ura >ab.20F fornece uma vis'o conceitual do relacionamento dos procedimentos
dentro do n C)D.
-otinas similares s'o definidas para os ns (, F e J. ,ortanto, voc escrever :
procedimentos ao todoA rtinitF%)H rtinitR%)H rtinitT%)H rtinitd%)H
rtupdateF%)H rtupdateR%)H rtupdateT%)H rtupdated%)
2J
1ab*4/2: -ela'o entre procedimentos dentro do n ).
8nterfa!es de soft6are
Is procedimentos descritos acima s'o os que voc ir escrever. 4screvemos as
se$uintes rotinas que podem ser chamadas pelas suas rotinasA
tola?erT%struct rtp2t p2tTsend)
onde rtp2t # a se$uinte estrutura, que 7 est declarada para voc. I
procedimento tola?erT%) # definido no arquivo pro$J.c
extern struct rtp2t $
int sourceid /* id do nK que est^ enviando o p2tH FH RH T ou
d */
int destid /* id do roteador para o qual o p2t est^ sendo
enviado %deve ser um viGinho imediato) */
int mincost'U( /* custo mCnimo para o nK F ... d */
-
Note que tola?erT%) # passado como estrutura, n'o como um ponteiro para
uma estrutura.
printdtF%)
imprimir a tabela de dist!ncia para o n C)D. 4la # passada como um ponteiro
para uma estrutura do tipo distance[table. printdtF%) e a declara'o da estrutura
para a tabela de dist!ncia do n C)D est declarada no arquivo nodeF.c. -otinas de
22
impress'o similares est'o definidas para voc nos arquivos nodeR.c, nodeT.c e
noded.c.
" ambiente de rede simulado
Beus procedimentos rtinitF%)H rtinitR%)H rtinitT%)H rtinitd%) e
rtupdateF%)H rtupdateR%)H rtupdateT%)H rtupdated%) enviam pacotes de rotina
Lcu7o formato est descrito acimaM dentro do meio. I meio entre$ar os pacotes em
ordem e sem perda para o destinatrio especfico. 8penas ns diretamente conectados
podem se comunicar. I atraso entre o remetente e o destinatrio # varivel Le
desconhecidoM.
6uando voc compilar seus procedimentos e meus procedimentos 7untos e e5ecutar o
pro$rama resultante, dever especificar apenas um valor relativo ao ambiente de rede
simuladoA
Tra!ing* 87ustar um valor de tracin$ de ( ou F imprimir informaes teis sobre o
que est acontecendo dentro da emula'o Le5emploA o que ocorre com pacotes e
temporizadoresM. =m valor de tracin$ de C)D desli$a esta op'o. =m valor maior do que
F e5ibir todos os tipos de mensa$ens de uso prprio para depura'o do meu emulador.
=m valor i$ual a F pode ser til para voc fazer o debu$ do seu cdi$o. 9antenha em
mente que implementaes reais n'o provem tais informaes sobre o que est
acontecendo com seus pacotes.
Tarefa bsi!a
"oc escrever os procedimentos rtinitF%)H rtinitR%)H rtinitT%)H rtinitd%)
e rtupdateF%)H rtupdateR%)H rtupdateT%)H rtupdated%) que 7untos
implementar'o uma computa'o distribuda assncrona das tabelas de dist!ncia para a
topolo$ia e os custos mostrados na .i$ura (.
"oc dever colocar seus procedimentos para os ns ) at# J em arquivos chamados
nodeF.c, ... noded.c. N'o # permitido declarar nenhuma varivel $lobal que se7a
visvel fora de um dado arquivo / Le5emploA qualquer varivel $lobal que voc definir
em nodeF.c. pode ser acessada somente dentro do nodeF.cM. &sso o forar a cumprir
convenes que deveriam ser adotadas se voc fosse e5ecutar os procedimentos em
23
quatro ns distintos. ,ara compilar suas rotinasA cc pro&d.c nodeF.c nodeR.c
nodeT.c noded. "erses de prottipos desses arquivos encontram0se emA node).c,
node(.c, nodeF.c, nodeJ.c. "oc pode adquirir uma cpia do arquivo pro$J.c emA
httpAOO$aia.cs.umass.eduOGuroseOnet%orGOpro$J.c.
4sta tarefa pode ser feita em qualquer mquina que tenha suporte a /. 4la n'o utiliza
caractersticas =N&V.
/omo sempre, a maior parte dos instrutores espera que voc possua uma lista de
cdi$os, um documento de pro7eto e uma sada de amostra.
,ara sua sada de amostra, seus procedimentos devem imprimir uma mensa$em sempre
que seus procedimentos rtinitF%)H rtinitR%)H rtinitT%)H rtinitd%) ou
rtupdateF%)H rtupdateR%)H rtupdateT%)H rtupdated%) forem chamados,
fornecendo o tempo Ldisponvel pela minha varivel $lobal cloc2timeM. ,ara
rtupdateF%)H rtupdateR%)H rtupdateT%) e rtupdated%)H voc deve imprimir a
identidade do remetente do pacote de rotina que est sendo passado para sua rotina, se a
tabela de dist!ncia for, ou n'o, atualizada, o contedo da tabela de dist!ncia Lvoc pode
usar minha rotina de impress'oM e uma descri'o de todas as mensa$ens enviadas para
os ns vizinhos como resultado de cada atualiza'o da tabela de dist!ncia.
8 sada de amostra deve ser uma lista$em de sada com um valor de +-8/4 i$ual a F.
Kestaque a tabela de dist!ncia final produzida em cada n. Beu pro$rama ser e5ecutado
at# que n'o ha7a mais nenhum pacote de rotina em tr!nsito na rede. Nesse ponto, o
emulador terminar.
Tarefa avanada
"oc escrever dois procedimentos, rtlin2handlerF%int lin2idH int ne*cost) e
rtlin2handlerR%int lin2idH int ne*cost), que ser'o chamados se Le quandoM o
custo do linG entre ) e ( mudar. 4ssas rotinas devem ser definidas nos arquivos
nodeF.c e nodeR.c, respectivamente. 8s rotinas levar'o o nome LidM do n vizinho no
outro lado do linG cu7o custo foi mudado, e o novo custo do linG. Note que quando o
custo de um linG muda, suas rotinas dever'o atualizar a tabela de dist!ncia e podem Lou
n'oM precisar enviar pacotes de roteamento atualizados aos ns vizinhos.
2?
,ara completar a parte avanada da tarefa, voc precisar mudar o valor da constante
>&NU/*8NQ4B Llinha J no pro&d.cM para (. ,ara sua informa'o, o custo do linG
ser alterado de ( para F) no tempo ()))) e ent'o retornar para ( no tempo F)))).
Buas rotinas ser'o invocadas nesses instantes.
[ altamente recomendvel que voc implemente primeiro a tarefa bsica e ent'o estenda
seu cdi$o para implementar a tarefa avanada.
DEA
6uando pensamos neste laboratrio em nosso curso de introdu'o de redes, os
estudantes enviaram vrias questes. Be voc estiver interessado em v0las, acesseA
httpAOO$aia.cs.umass.eduOGuroseOnet%orGOpro$rammin$Xassi$nmentX68.htm.
2@
Tarefa de Programao G: Streaming v(deo !om 9TSP e 9TP
" !#digo
Neste laboratrio, voc implementar um servidor de streamin$ vdeo e cliente que se
comunica usando o protocolo de flu5o contnuo em tempo real L-+B,M e envia dados
usando o protocolo de tempo real L-+,M. Bua tarefa # implementar o protocolo -+B, no
cliente e implementar o empacotamento -+, no servidor.
.orneceremos o cdi$o que implementa o protocolo -B+, no servidor, o
desempacotamento -+, no cliente e trataremos de e5ibir o vdeo transmitido. "oc n'o
precisa me5er neste cdi$o.
Classes
45istem quatro classes nesta tarefa.
/lient
4sta classe implementa o cliente e a interface de usurio que voc usar para
enviar comandos -+B, e que ser utilizada para e5ibir o vdeo. 8bai5o vemos
como # a interface. 'oc( deer) implementar as a*+es $ue s#o tomadas $uando
os ,ot+es s#o pressionados.
Berver
4sta classe implementa o servidor que responde <s requisies -+B, e
encaminha o vdeo de volta. 8 intera'o -+B, 7 est implementada e o servidor
chama as rotinas na classe R6.pac2et para empacotar os dados de vdeo. "oc
n'o precisa me5er nesta classe.
-+,pacGet
4sta classe # usada para manipular os pacotes -+,. 4la possui rotinas separadas
para tratar os pacotes recebidos no lado cliente que 7 # dado e voc n'o precisa
modific0lo Lmas ve7a os 45erccios opcionaisM. "oc dever completar o
primeiro construtor desta classe para implementar o empacotamento -+, dos
dados de vdeo. I se$undo construtor # usado pelo cliente para desempacotar os
dados. "oc n'o precisa modific0lo tamb#m.
2:
"ideoBtream
4sta classe # usada para ler os dados de vdeo do arquivo em disco. "oc n'o
precisa modificar esta classe.
&'e!utando o !#digo
8ps completar o cdi$o, voc pode e5ecut0lo da se$uinte formaA
,rimeiro, inicie o servidor com o comandoA
java #erver server[port
onde server[port # a porta onde seu servidor escuta as cone5es -+B, que che$am. 8
porta -+B, padr'o # a 332, mas voc deve escolher um nmero de porta maior que
()F2.
4nt'o, inicie o cliente com o comandoA
java 7lient server[name server[port video[!ile
onde server[name # o nome da mquina onde o servidor est sendo e5ecutado,
server[port # a porta que o servidor est escutando, e video[!ile # o nome do
arquivo que voc quer requisitar Lfornecemos um arquivo de e5emplo movie.Ojpe&M. I
formato do arquivo est descrito no 8pndice.
I cliente abre uma cone5'o com o servidor e abre uma 7anela como estaA
21
"oc pode enviar comandos -+B, para o servidor pressionando os botes. =ma
intera'o normal -+B, acontece assimA
(. I cliente envia B4+=,. 4sse comando # usado para a7ustar os par!metros de
sess'o e de transporte.
F. I cliente envia ,>8Y. &sso inicia a reprodu'o.
J. I cliente pode enviar ,8=B4 se ele quiser pausar durante a reprodu'o.
2. I cliente envia +48-KIWN. &sso termina a sess'o e fecha a cone5'o.
I servidor sempre responde a todas as mensa$ens que o cliente envia. Is cdi$os de
resposta s'o e5atamente os mesmos do *++,. I cdi$o F)) indica que a requisi'o foi
bem0sucedida. Neste laboratrio, voc n'o precisa implementar nenhum outro cdi$o de
resposta. ,ara mais informaes sobre o -+B,, ve7a a -./0FJF?.
1* Cliente
Bua primeira tarefa # implementar o -+B, do lado cliente. ,ara fazer isso, voc deve
completar as funes que s'o chamadas quando o usurio clica nos botes da interface
3)
de usurio. ,ara cada bot'o na interface, h uma fun'o manipuladora do cdi$o. "oc
deve implementar as se$uintes aes em cada fun'o manipuladora.
6uando o cliente # iniciado, ele tamb#m abre o socGet -+B, para o servidor. =se este
socGet para enviar todas as requisies -+B,.
SETUP
/rie um socGet para receber os dados -+, e a7ustar o tempo de e5pira'o no
socGet para 3 milisse$undos.
4nvie uma requisi'o B4+=, para o servidor. "oc deve inserir o cabealho de
+ransporte onde voc especificar a porta para o socGet de dados -+, que voc
criou.
>eia a resposta do servidor e analise o cabealho de Bess'o na resposta para obter
o &K da sess'o.
PLAY
4nvie uma requisi'o ,>8Y. "oc deve inserir o cabealho de sess'o e usar o &K
de sess'o fornecido na resposta ao B4+=,. N'o coloque cabealho de +ransporte
nesta requisi'o.
>eia a resposta do servidor.
PAUSE
4nvie uma requisi'o ,8=B4. "oc deve inserir o cabealho de Bess'o e usar o
&K de sess'o fornecido na resposta ao B4+=,. N'o coloque cabealho de
+ransporte nesta requisi'o.
>eia a resposta do servidor.
TEARDOWN
4nvie uma requisi'o +48-KIWN. "oc deve inserir o cabealho de Bess'o e
usar o &K de sess'o fornecido na resposta ao B4+=,. N'o # preciso colocar
cabealho de +ransporte neste requisi'o.
>eia a resposta do servidor.
3(
NotaA "oc deve inserir o cabealho /Beq em cada requisi'o que voc enviar. I valor
do cabealho /Beq # um numero que ser incrementado de ( a cada requisi'o que voc
enviar.
&'em$lo
8qui est uma intera'o de amostra entre cliente e servidor. 8s requisies dos clientes
s'o marcadas com /A e as respostas dos servidores com BA. Neste laboratrio, tanto o
cliente quanto o servidor s'o bem simples. 4les n'o precisam ter rotinas de anlise
sofisticadas e esperam sempre encontrar os campos do cabealho na ordem que voc
ver abai5o, ou se7a, numa requisi'o, o primeiro cabealho # o /Beq, e o se$undo # ou
o de +ransporte Lpara B4+=,M ou o de Bess'o Lpara todas as outras requisiesM. Na
resposta, /Beq # novamente o primeiro e de Bess'o # o se$undo.
7: #+6]. movie.Ojpe& R6#./R.F
7: 7#eq: R
7: 6ransport: R6./]D. client[port1 T\FFF
#: R6#./R.F TFF AS
#: 7#eq: R
#: #ession: RTdU\6
7: .9/Q movie.Ojpe& R6#./R.F
7: 7#eq: T
7: #ession: RTdU\6
#: R6#./R.F TFF AS
#: 7#eq: T
#: #ession: RTdU\6
7: ./]#+ movie.Ojpe& R6#./R.F
7: 7#eq: d
7: #ession: RTdU\6
#: R6#./R.F TFF AS
#: 7#eq: d
#: #ession: RTdU\6
7: .9/Q movie.Ojpe& R6#./R.F
3F
7: 7#eq: U
7: #ession: RTdU\6
#: R6#./R.F TFF AS
#: 7#eq: U
#: #ession: RTdU\6
7: 6+/RDA"J movie.Ojpe& R6#./R.F
7: 7#eq: \
7: #ession: RTdU\6
#: R6#./R.F TFF AS
#: 7#eq: \
#: #ession: RTdU\6
&stado do !liente
=ma das diferenas entre *++, e -+B, # que no -+B, cada sess'o possui um estado.
Neste laboratrio, voc precisar manter o estado do cliente atualizado. I cliente muda
de estado quando ele recebe uma resposta do servidor de acordo com o se$uinte
dia$rama.
F. Bervidor
No servidor, voc precisar implementar o empacotamento dos dados de vdeo em
pacotes -+,. ,ara isso, ser necessrio criar o pacote, a7ustar os campos no cabealho
do pacote e copiar a car$a til Le5emploA um quadro de vdeoM dentro do pacote.
6uando o servidor recebe a requisi'o ,>8Y do cliente, ele aciona um temporizador
que # ativado a cada ())ms. Nesses tempos, o servidor ler um quadro de vdeo do
3J
arquivo e o enviar para o cliente. I servidor cria um ob7eto -+,pacGet, que # o
encapsulamento -+, do quadro de vdeo.
I servidor chama o primeiro construtor da classe -+,pacGet para realizar o
encapsulamento. Bua tarefa # escrever essa fun'o. "oc precisar fazer o se$uinteA Las
letras em parnteses se referem aos campos no formato do pacote -+, abai5oM
(. 87uste o campo -+,0version L"M. "oc deve a7ust0lo para F.
F. 87uste os campos paddin$ L,M, e5tension LVM, number of contributin$ sources
L//M, e marGer L9M. +odos eles s'o a7ustados em zero neste laboratrio.
J. 87uste o campo car$a til L,+M. Neste laboratrio, usamos 9S,4Q e o tipo para
ele # F?.
2. 87uste o nmero de seqPncia. I servidor fornece esse nmero de seqPncia
como ar$umento :ramenb para o construtor.
3. 87uste a marca de tempo. I servidor fornece este nmero como ar$umento 6ime
para o construtor.
?. 87uste o identificador da fonte LBB-/M. 4ste campo identifica o servidor. "oc
pode usar o valor inteiro que dese7ar.
/omo n'o temos nenhuma outra fonte de contribui'o Lcampo // ^^ )M, o campo
/B-/ n'o e5iste. I comprimento do cabealho do pacote # de (F bNtes, ou as trs
primeiras linhas do dia$rama abai5o.
32
F R T d
F R T d U \ 6 7 8 9 F R T d U \ 6 7 8 9 F R T d U \ 6 7 8 9 F R
LMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLML
VX1TV.VZV 77 VOV .6 V sequence number V
LMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLML
V timestamp V
LMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLML
V s?nchroniGation source %##R7) identi!ier V
LMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLML
V contributin& source %7#R7) identi!iers V
V .... V
LMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLMLML
"oc deve preencher o cabealho na disposi'o header da classe -+,pacGet. "oc
tamb#m precisar copiar a car$a til Lfornecida como ar$umento dataM para a varivel
pa?load. I comprimento da car$a til # dado no ar$umento data[len&th.
I dia$rama acima est na ordem de bNte de rede Ltamb#m conhecido como bi$0endianM.
8 Sava "irtual 9achine usa a mesma ordem de bNte, ent'o voc n'o precisa transformar
seu cabealho de pacote na ordem de bNte de rede.
,ara mais detalhes sobre -+,, ve7a a -./0(::1.
+ani$ulando os bits
8qui est'o al$uns e5emplos de como a7ustar e verificar bits individuais ou $rupo de
bits. Note que no formato do cabealho do pacote -+,, os menores nmeros de bit se
referem a maiores ordens de bits, ou se7a, o bit nmero ) de um bNte # FH@, e o bit
nmero @ # ( Lou FH)M. Nos e5emplos abai5o, os nmeros de bit se referem aos nmeros
no dia$rama acima.
/omo o campo header da classe R6.pac2et # um vetor do tipo b?te, voc precisar
a7ustar o cabealho de um bNte por vez, que # um $rupo de : bits. I primeiro bNte
possui os bits )0@, o se$undo bNte possui os bits :0(3, e assim por diante. 4m Sava, um
int tem JF bits ou 2 bNtes.
,ara a7ustar o nmero n na varivel m?b?te do tipo bNteA
m?b?te 1 m?b?te V R NN %7 Y n)
33
,ara a7ustar os bits n e n-1 para o valor de !oo na varivel m?b?teA
m?b?te 1 m?b?te V !oo NN %7 Y n)
Note que !oo deve ter um valor que possa ser e5presso com F bits, ou se7a, ), (, F ou J.
,ara copiar um !oo inteiro de (?0bits em F0bNtes, bR e bTA
bR 1 !oo PP 8
bT 1 !oo f Fx::
8ps fazer isso, bR ter : bits de maior ordem de !oo e bT ter : bits de menor ordem
de !oo.
"oc pode copiar um inteiro de JF0bits em 20bNtes de maneira similar.
Be voc n'o se sente confortvel com o a7uste de bits, pode encontrar mais informaes
no Sava +utorial.
&'em$lo de bit
Buponha que dese7amos preencher o primeiro bNte do cabealho do pacote -+, com os
se$uintes valoresA
" ^ F
, ^ )
V ^ )
// ^ J
4m binrio, isso poderia ser representado como
R F V F V F V F F R R
X1T . Z 77 1 d
Tg7 . . . . . . . TgF
&'er!(!ios o$!ionais
4m vez do servidor normal fornecido a voc, use a classe chamada :un2?#erver
Lfaa tamb#m o do%nload da classe .unGNBerver_(.classM, por e5emplo, e5ecute0o com
java :un2?#erver server[port. I que acontece no clienteE 45plique o porqu.
3?
/alcule as estatsticas sobre a sess'o. "oc precisar calcular a ta5a de perda de
pacotes -+,, a ta5a de dados de vdeo Lem bits ou bNtes por se$undoM e qualquer outra
estatstica interessante que voc conse$uir encontrar.
8 interface de usurio no cliente possui 2 botes para as 2 aes. Be voc compar0la
a um transdutor padr'o, tal como -eal,laNer ou transdutor Windo%s, voc ver que eles
possuem apenas J botes para as mesmas aes, chamadas, ,>8Y, ,8=B4 e B+I,
Lcorrespondendo e5atamente ao +48-KIWNM. N'o h nenhum bot'o de B4+=,
disponvel para o usurio. Kado que o B4+=, # mandatrio numa intera'o -+B,,
como voc implementaria issoE [ apropriado enviar +48-KIWN quando o usurio
clica no bot'o stopE
8t# aqui, o cliente e o servidor implementam apenas o mnimo necessrio de
interaes -+B, e ,8=B4. &mplemente o m#todo K4B/-&W4, que # usado para passar
informaes sobre o media stream. 6uando o servidor recebe uma requisi'o
K4B/-&W4, ele envia de volta um arquivo de descri'o de sess'o que diz ao cliente que
tipos de streams est'o na sess'o e quais codificaes est'o sendo utilizadas.
A$;ndi!e
.ormato do 9S,4Q L9otion S,4QM proprietrio do laboratrio.
Neste laboratrio, o servidor encaminha um vdeo codificado com um formato de
arquivo proprietrio 9S,4Q. 4ste formato armazena o vdeo como concatena'o de
ima$ens codificadas em S,4Q, com cada ima$em sendo precedida por um cabealho de
30bNtes que indica o tamanho em bits da ima$em. I servidor analisa o bitstream do
arquivo 9S,4Q para e5trair as ima$ens S,4Q no caminho. I servidor envia as ima$ens
ao cliente em intervalos peridicos. I cliente ent'o e5ibe as ima$ens S,4Q individuais
conforme elas v'o che$ando do servidor.
3@

Você também pode gostar