Translate to English Translate to Spanish Translate to French Translate to German Google-Translate-Portuguese to Italian Translate to Russian Translate to Chinese Translate to Japanese

Rating: 1.8/5 (293 votos)





Total de visitas: 21317
atakes arquivos
atakes arquivos


programas para manuseio de arquivos e invasao de dados, sendo necessario mascaras de burlwe para ,nao dectacao de ip. toda ´pratica de acesso a comp. alheios é de toda responsabilidade de qem a pratica . nao resposabilizado pelo fornecedor do arquivo, informamos qe procure saber muito bem programacao e manuseio de link de redes (trafego para efetuar o mesmo ).procure efetuar antes em meros bobos . para qem sabe efetuar em mcros comp. efetuar criacao de invasores por compilacao e camuflagem de trojan , hamachi. temida. best.satan.brutuus,telnet:um dos principais para acesso remoto . saber usalo de grande valia. saber comandos de cmd . protecao de redes . a sua mesma para nao ser descoberto ;e nao ser likado . ou nukado .se conseguir baixar:protheus é otimo destrui dor de site. mais dificil de encontrar junto com hidra(assembly). o programa de anonimos tambem e bom o orbit ,mais traz em sim um virus de log . para remotamente te vistoriar . thc scan é otimo para aquele qe e esperto e ambicioso . é para poucos,pois e por tom de discagem para conf qe aceita e apos invasao por ip . em comandos. https://www.metropoledigital.ufrn.br/aulas_avancado/web/disciplinas/desenv_web/aula_02.html Mapeamento é a associação de uma pasta compartilhada em uma rede de computadores a uma letra de unidade disponível em um computador local. Pois alguns usuários devem acessar as mesmas unidades e pastas compartilhadas com freqüência. Embora a unidade ou a pasta compartilhada possa ser facilmente acessada quando pesquisada em Meus locais de rede, essa tarefa pode tornar-se desnecessariamente repetitiva para a conexão com compartilhamentos comuns. Para simplificar o processo de pesquisa de unidades e pastas compartilhadas, você pode estabelecer uma conexão semipermanente com um recurso compartilhado, mapeando-o no Windows Explorer. Esse recurso mapeado é conhecido como unidade mapeada ou unidade de rede. Após mapear um recurso, você poderá acessá-lo da mesma forma que acessaria um volume local. Para mapear uma unidade de rede você deve fazer o seguinte: No Windows Explorer, clique em Ferramentas e, em seguida, clique em Mapear unidade de rede. Escolha uma letra de unidade disponível para atribuir ao compartilhamento e na caixa Pasta, digite o caminho da pasta compartilhada (por exemplo,ComputerDicasusers). Marque a opção Reconectar-se durante o Logon e clique emConcluir. Máquinas virtuais Terminal remoto Captura de pacotes Protocolo ARP: Mapeando endereços de Rede para endereços de Enlace. Protocolos TCP e UDP Tradução de endereços de rede - NAT Protocolo DHCP: distribuindo automaticamente configurações IP para as estações em uma LAN Sistema de Nomes de Domínio (DNS) Autenticação e compartilhamento de arquivos – Parte I Autenticação e compartilhamento de arquivos: parte II Autenticação e compartilhamento de arquivos: parte III Web: protocolo HTTP e servidor Apache Proxy e servidor Squid Correio eletrônico Gerenciamento de redes – SNMP, RMON e CACTI Voltar Imprimir Topo Redes de Computadores Aula 4 – Protocolo ARP: Mapeando endereços de Rede para endereços de Enlace. Autores: Marcos Cesar Madruga Alves Pinheiro (Professor) Carlos Markennede Crescêncio deLima (Assistente) Renato Gondim Sarmento (Assistente) João Paulo Confessor (Assistente) Contato: marcos@dimap.ufrn.br markennedy@dimap.ufrn.br joaopaulo@dimap.ufrn.br renato@dimap.ufrn.br Nesta aula você aprenderá um protocolo que é de fundamental importância para que a pilha de protocolos TCP/IP possa ser utilizada com qualquer tecnologia de rede física, como Ethernet ou as redes sem fio 802.11, por exemplo. Verá que este protocolo se chama ARP (Address Resolution Protocol, ou seja, Protocolo de Resolução de Endereços), e tem como principal função descobrir o endereço MAC da máquina para qual um dado pacote IP deve ser entregue. Você aprenderá porque isso é necessário e como essa tarefa é realizada. • Aprender que o protocolo ARP é utilizado para traduzir endereços de rede para endereços de enlace, e que normalmente traduz um endereço IP para um endereço Ethernet. • Entender por que a tradução de endereços de rede para endereços de enlace é necessária. • Entender como a tradução de endereços de rede para endereços de enlace é realizada. • Utilizar uma ferramenta para monitorar o processo de tradução de endereços. Por que o protocolo ARP é necessário Você sabe que nas redes que utilizam o protocolo IP, como é o caso da Internet e da maioria das redes das empresas atualmente, os dados são transmitidos entre as máquinas em pacotes IP, e que cada pacote contém o endereço IP da máquina que está enviando o pacote e o endereço IP da máquina para a qual ele deve ser entregue. A Figura 1 mostra o pacote IP que é gerado quando uma máquina A quer enviar dados para uma máquina C. Naturalmente os demais campos do cabeçalho IP e os cabeçalhos das camadas de transporte e de aplicação foram omitidos. Figura 1 - Transmissão usando um pacote IP Acontece, como você também já sabe, que não existe nenhuma placa de rede que fale IP, que é um protocolo da camada de rede. As placas de rede implementam a camada de enlace, portanto, comunicam-se enviando quadros no formato definido pela sua tecnologia, como, por exemplo, Ethernet ou 802.11. Naturalmente, do mesmo modo que os pacotes IP contêm campos para os endereços IP, os quadros também contêm os endereços de enlace (chamados endereços MAC) da máquina que gerou o quadro e da máquina para a qual ele deve ser entregue. Lembre-se de que endereço MAC é o mesmo que endereço da placa de rede. O pacote IP é transmitido dentro da parte de dados do quadro utilizado pela placa de rede. Assim sendo, para que o pacote IP seja entregue à máquina desejada, precisamos colocar no campo de endereço MAC de destino do quadro o endereço MAC da máquina que possui o IP com quem queremos falar. O problema é que o usuário não sabe o endereço MAC da máquina com quem quer falar, ele conhece apenas o endereço IP dela! A Figura 2 mostra como o pacote IP da Figura 1 realmente precisaria ser transmitido. Vamos assumir que a rede utilizada é da tecnologia Ethernet. Figura 2 - Enviando um pacote IP dentro do Ethernet Veja que o campo Endereço Ethernet de origem é preenchido com o endereço da placa de rede da máquina local (de onde o pacote será enviado), mas o endereço Ethernet de destino não é conhecido. Portanto, é necessário que exista algum mecanismo automático para se descobrir o endereço Ethernet associado a um endereço IP. Esse mecanismo existe e é o ARP (Protocolo para Resolução de Endereços, do inglês Address Resolution Protocol). Embora o ARP seja um protocolo genérico que é capaz de traduzir endereços de qualquer protocolo de rede para endereços de qualquer protocolo de enlace, normalmente seu uso mais frequente é traduzir endereços IP para endereços Ethernet, ou endereços IP para endereços MAC 802.11 nas redes sem fio. Nos nossos exemplos iremos utilizar a tradução de endereços IP para Ethernet, mas os mesmos procedimentos explicados se aplicam para outros protocolos e tecnologias de enlace. Em nossos exemplos utilizamos endereços Ethernet que tenham algo em comum com o endereço IP da máquina, para que fique mais fácil de você lembrar. O último byte do endereço Ethernet, por exemplo, normalmente será igual ao último byte do endereço IP. Mas lembre-se de que, na realidade, os endereços Ethernet não mantêm nenhuma relação com o IP. Quando você compra uma placa de rede ela já tem seu endereço Ethernet! Para finalizar esta seção, veja na Figura 3 onde o protocolo ARP se situa na pilha de protocolos TCP/IP. Figura 3 - Posição do ARP na pilha de protocolos TCP/IP Funcionamento do ARP Vamos agora entender como o ARP consegue descobrir o endereço Ethernet associado a um endereço IP. Antes dessa explicação é importante que você se lembre que uma máquina pode enviar um quadro Ethernet para uma única outra máquina ou para todas as outras máquinas da rede. No primeiro caso, o endereço da placa de rede da máquina de destino é colocado no campo de endereço de destino do quadro, e este quadro é entregue apenas à máquina de destino. No seguindo caso, o campo endereço de destino do quadro é preenchido com o endereço especialFF:FF:FF:FF:FF:FF, chamado “Endereço de Broadcast”, fazendo com que o quadro seja entregue a todas as máquinas da rede. O ARP se aproveita dessa capacidade de enviar um quadro para todas as máquinas da rede para resolver o seu problema de forma muito simples. Ele apenas envia uma mensagem em broadcast (ou seja, para todas as máquinas da rede) dizendo: “Por favor, quem tem o endereço IP X.X.X.X, informe qual é seu endereço Ethernet”. Evidentemente, X.X.X.X é substituído pelo endereço IP para o qual ela quer descobrir o Ethernet. Como todas as máquinas recebem esta pergunta, a máquina que tiver o endereço IP X.X.X.X responde informando seu Ethernet. É importante observar que essa resposta não é enviada em broadcast! Ela é enviada diretamente para quem fez a pergunta. Isso é possível porque o endereço de quem fez a pergunta está no quadro Ethernet que o destino recebeu, no campo endereço de origem. A mensagem ARP é transmitida dentro da parte de dados do Ethernet, portanto, o campo tipo do quadro Ethernet contém o código do protocolo ARP, que é 0x0806 (em hexadecimal). Além disso, naturalmente, o pacote IP só será transmitido após o envio do quadro ARP e do recebimento da resposta. Ainda sobre o valor do campo tipo, nos nossos exemplos, quando mostrarmos um quadro utilizaremos a palavra “ARP” ao invés do código numérico, pois fica mais fácil de entender. Mas lembre-se que de fato é utilizado o valor numérico! A Figura 4 mostra como ocorre a comunicação mostrada na Figura 2, onde a máquina 10.1.1.1 quer transmitir um pacote IP para a máquina 10.1.1.3, e, para isso, utiliza o ARP para descobrir seu endereço Ethernet. Na Figura 4(a) a máquina “A” envia uma mensagem ARP em broadcast perguntando quem tem o IP 10.1.1.3. Veja que o campo tipo do quadro Ethernet contém a identificação do protocolo ARP, e que todas as outras máquinas da rede, no caso, B e C, recebem o quadro, uma vez que ele foi enviado para o endereço de broadcast (FF:FF:FF:FF:FF:FF). Figura 4(a) - Envio de requisição ARP para descobrir o endereço Ethernet Na Figura 4(b) vemos que apenas a máquina C responde à mensagem ARP, pois ela possui o endereço IP para o qual se deseja descobrir o endereço Ethernet. As outras máquinas, no caso a máquina B, descartam a mensagem ARP. Você pode observar ainda que apenas a máquina A recebe esta resposta, pois o quadro é enviado diretamente para o seu endereço (AA:AA:AA:01:01:01). Figura 4(b) - Envio de resposta ARP informando o endereço Ethernet Finalmente, na Figura 4(c), vemos que após receber a resposta ARP informando qual é o endereço Ethernet (AA:AA:AA:00:00:03) associado ao endereço IP de C (10.1.1.3), o pacote IP que precisa ser enviado para ela é colocado dentro de um quadro Ethernet e enviado. Figura 4(c) - Envio do pacote IP após utilização do ARP 1. Quais os endereços colocados nos camposendereço de origem e endereço de destino de um quadro Ethernet contendo uma mensagem de requisição ARP? 2. Qual o valor do campo de tipo de um quadro Ethernet contendo uma mensagem de resposta ARP? Formato das Mensagens ARP Na seção anterior nós descrevemos as mensagem ARPs contendo apenas dois campos: um para o endereço IP e outro para o endereço Ethernet. Na verdade, como o ARP pode funcionar com vários protocolos de camada de rede e vários protocolos de camada de enlace, as mensagens ARP possuem campos para identificar quais protocolos são utilizados. Antes de vermos esses campos, saiba que existem apenas dois tipos de mensagem, uma para perguntar pelo endereço Ethernet associado a um IP, chamada ARP Request, e outra, chamada ARP Reply, para responder essa requisição, onde a máquina informa o Ethernet associado ao IP. Além disso, as duas possuem exatamente os mesmos campos, sendo diferenciadas pelo valor contido em um dos campos do quadro, que indica o tipo de operação. A Tabela 1 mostra os campos de uma mensagem ARP e o significado de cada um. Nome do Campo Tamanho (em bytes) Descricao Tipo Hardware 2 Código da tecnologia da camada de enlace (Ex: Ethernet. 0x0001 - hexadecimal) Tipo Protocolo 2 Código do Protocolo de Rede (Ex: IP. 0x0800 - hexadecimal) Tam End. Hw 1 Tamanho do endereço de enlace (em bytes). Se for Ethernet, contém o valor 6. Tam End. Proto 1 Tamanho do endereço de Rede (em bytes). Se for IP, contém o valor 4. Operação 2 Código do tipo de mensagem: 0x0001 para o ARP Request, 0x0002 para o ARP Reply. Endereço Hardware Origem - Endereço de Enlace da origem. O tamanho deste campo é o valor do campo “Tam End. Hw” Endereço Protocolo Origem - Endereço de Rede da origem. O tamanho deste campo é o valor do campo “Tam End. Proto” Endereço Hardware destino - Endereço de Enlace da origem. Se for Ethernet, o tamanho é 6 bytes. Endereço Protocolo Destino - Endereço de Enlace da origem. Se for IP, o tamanho é 4 bytes. Tabela 1 - Campos do quadro ARP Para entendermos melhor como os campos de endereço são utilizados, vamos ver os valores que realmente são enviados nos campos de endereço das mensagens ARP trocadas entre as máquinas A e C do exemplo mostrado na sessão anterior – Figuras 3(a) e 3(b). As duas mensagens são enviadas em quadros Ethernet contendo no campo de tipo o código do ARP (0x0806 – Hexadecimal), sendo que o ARP Resquest é enviado para o endereço de broadcast Ethernet, enquanto o ARP reply é enviado diretamente para o endereço Ethernet de A. Arp Request Enviada por A ARP Reply Enviada por C Endereço Hardware Origem AA:AA:AA:00:00:01 AA:AA:AA:00:00:03 Endereço Protocolo Origem 10.1.1.1 10.1.1.3 Endereço Hardware destino 00:00:00:00:00:00:00 AA:AA:AA:00:00:01 Endereço Protocolo Destino 10.1.1.3 10.1.1.1 Tabela 2 - Valores dos campos de endereço para a comunicação entre A e C Observe que no ARP Resquest o endereço IP de origem é preenchido com zeros, pois é exatamente este endereço que desejamos descobrir. 1. Em um quadro que contém uma mensagem ARP, qual campo devemos olhar para saber se é uma mensagem ARP Request ou ARP Reply? Cache ARP Embora o modelo descrito na seção anterior resolva o problema da resolução de endereços, ele apresenta dois efeitos colaterais negativos. O primeiro ocorre pelo fato de enviar mensagens pela rede, pois isso faz com que haja um tempo (atraso) até que a resposta do ARP chegue, causando um atraso no envio do pacote que se deseja transmitir. O segundo efeito negativo se deve ao fato das mensagens serem transmitidas a todas as máquinas na rede. Isso aumenta o volume de tráfego nessa rede, podendo levar a uma redução no seu desempenho. O ideal é que a maior parte da capacidade de transmissão de uma rede seja destinada aos dados das aplicações dos usuários. Quanto mais informações para outras finalidades a rede transmitir, como é o caso do ARP, menos banda de rede sobre para os usuários. Para minimizar os dois problemas citados, o ARP utiliza uma tabela (chamada de cache arp) onde ele guarda as informações que já descobriu. As duas principais colunas desta tabela são endereço IP e endereço MAC. Desse modo, cada linha desta tabela contém um endereço IP e o seu endereço MAC (tipicamente um endereço Ethernet ou 802.11). Usando a tabela, o funcionamento do ARP fica da seguinte maneira: quando uma máquina precisa enviar um pacote para um determinada endereço IP, ela primeiro verifica se este endereço IP já se encontra na tabela ARP. Se ele existir lá, basta obter seu endereço Ethernet a partir da tabela. Neste caso, nenhuma mensagem é enviada. Caso o endereço IP não exista na tabela, aí sim, é enviada a mensagem ARP (ARP request) pela rede. Quando a resposta do ARP chegar (ARP Reply), além de ser utilizada para o envio do pacote IP, é inserida uma entrada na tabela cache associando o IP e o Ethernet. A Figura 5 mostra um fluxograma que descreve o funcionamento do ARP. Figura 5 - Fluxograma do ARP usando a tabela cache Veja que o fluxograma mostra que pode existir uma situação onde enviamos uma requisição ARP e não recebemos resposta. Isso é normal, e acontecerá sempre que tentarmos acessar um endereço IP para o qual não existe nenhuma máquina na nossa rede. Isso acontecerá, por exemplo, quando a máquina que você tenta acessar está desligada. Nesses casos, embora não tenha sido mostrado no fluxograma, também será inserida uma entrada na tabela cache para o endereço IP, mas não haverá um endereço Ethernet associado. Essa entrada servirá apenas para vermos que já tentamos nos comunicar com aquele IP e não conseguimos resposta. Portanto, sempre que alguém precisar se comunicar com esse IP, uma nova requisição ARP será enviada pela rede! É importante observar que as entradas cadastradas na tabela cache não podem ficar lá para sempre. Para entender por que, suponha, por exemplo, que descobrimos que o endereço IP 200.1.1.1 está associado ao endereço Ethernet AA:AA:AA:01:01:01 e inserimos esta entrada na tabela cache. Descobrimos também que o endereço IP 200.2.2.2 está associado ao endereço Ethernet AA:AA:AA:02:02:02 e inserimos esta entrada na tabela cache. Podem acontecer as seguintes duas situações que levariam a um erro: • depois de algum tempo, as máquinas podem ter sido reinicializadas e terem obtido outros endereços IP. Suponha que após a reinicialização a máquina com endereço Ethernet AA:AA:AA01:01:01, obteve-se o endereço IP 200.2.2.2 e a máquina com endereço Ethernet AA:AA:AA:02:02:02 obteve o endereço IP 200.1.1.1. Se você usasse as informações que estão na tabela cache, quando tentasse transmitir um pacote para o endereço IP 200.1.1.1 você o enviaria para o endereço Ethernet AA:AA:AA:01:01:01, o que está errado! Como esse endereço MAC agora está associado ao IP 200.2.2.2, você enviaria o pacote para a máquina errada! • a placa de rede da máquina 200.1.1.1 que possuía endereço Ethernet AA:AA:AA01:01:01 pode ter dado algum problema e ter sido trocada por outra. Como o endereço Ethernet é um endereço gravado na própria placa, a nova placa de rede terá outro endereço Ethernet! Portanto, se continuasse usando as informações armazenadas na tabela cache inicialmente, você não conseguiria mais se comunicar com a máquina 200.1.1.1. Para minimizar os dois problemas citados, cada linha da tabela cache possui uma terceira coluna, que guarda uma informação de tempo, chama timestamp. Essa coluna contém o instante do tempo em que a entrada foi inserida na tabela. Assim, se depois de certo tempo em que foi inserida a entrada não for utilizada, ela é removida. Além disso, mesmo as entradas utilizadas são removidas depois de certo tempo (que naturalmente é maior que o das entradas não utilizadas). Por fim, saiba que a tabela cache de ARP é normalmente chamada apenas de “Tabela ARP”. 1. Para que serve a Tabela ARP? Manipulando a cache Podemos facilmente listar o conteúdo da tabela ARP a partir de comandos do sistema operacional. O comando para listar o conteúdo da tabela é: arp –n. Vamos usar este comando vendo alguns exemplos práticos. Quando tudo dá certo Supondo que temos uma rede como a mostrada na Figura 3. Vamos ver o que acontece quando a máquina A (10.1.1.1) vai enviar um ping para a máquina C (10.1.1.3). Suponha que a máquina A acabou de ser ligada e ainda não se comunicou com ninguém. Desse modo, antes de executar o comando ping na máquina A podemos ver que sua tabela ARP está vazia, digitando o comando arp –n. Veja na Figura 6 que nada é mostrado. Figura 6 - Tabela ARP vazia A seguir, o usuário da máquina A realiza o comando ping para a máquina C. Como podemos ver na Figura 7, a máquina C enviou as respostas, de modo que o comando foi bem sucedido. Figura 7 - Comando Ping executado com sucesso Como a máquina A precisou descobrir o Ethernet associado ao endereço de C (10.1.1.3) para poder lhe enviar os pacotes, ela lhe enviou primeiro uma mensagem ARP e colocou a resposta na Tabela ARP (cache). Podemos verificar isso digitando o comando arp –n na máquina A, conforme mostrado na Figura 8. Figura 8 - Tabela ARP contendo o endereço Ethernet associado ao IP 10.1.13 A coluna Address mostra o endereço IP ao qual o Ethernet mostrado na coluna HWaddress está associado. A colunaHWtype diz que o tipo do endereço da coluna HWaddress é um endereço Ethernet, e a coluna Iface diz qual das placas de rede da máquina A está conectada na rede onde a máquina C se encontra (lembre-se que se a máquina A fosse um roteador ela teria mais de uma placa de rede). Como já explicamos antes, essa entrada fica guardada na Tabela ARP por certo tempo (tipicamente dois minutos) e depois é apagada. Durante o tempo em que ela está na cachê, se a máquina A tentar se comunicar com a máquina C, não será necessário enviar uma nova mensagem ARP. Depois que ela for excluída a próxima tentativa de comunicação com C irá gerar novamente uma mensagem ARP e a resposta será incluída na Cache. O processo continua sempre desse modo, com as informações sendo incluídas e apagadas da cache. Portanto, se esperássemos aproximadamente 2 minutos e executássemos o comando arp –n novamente na máquina A, veríamos que sua tabela ARP estaria vazia novamente. Otimização do ARP Normalmente uma máquina que recebe um pacote IP de outra envia pacotes de volta para esta máquina. Assim sendo, quando uma máquina recebe uma requisição ARP ela pode já inserir o endereço IP e o Ethernet da máquina que lhe enviou o quadro na sua tabela cache. Isso evita que ela precise enviar uma requisição ARP para a máquina de origem quando tentar se comunicar com ela. Desse modo, no exemplo que mostramos – a máquina A enviando um ping para a máquina C –, a máquina C, ao receber a requisição ARP perguntando pelo seu IP (10.1.1.3), já insere o endereço IP (10.1.1.1) e o Ethernet (AA:AA:AA:00:00:01) da máquina A na sua cache. Assim, quando C for responder ao ping, o endereço Ethernet de A já vai ser conhecido e não será necessário enviar uma nova requisição ARP. Na Figura 9, mostramos a tabela ARP da máquina C após receber o ping de A. Veja que foi inserida uma entrada para o IP e Ethernet de A. Figura 9 - Tabela Cache da Máquina C após receber ping de A Talvez você esteja se perguntando: “Espere aí, como tenho certeza se essa entrada foi inserida automaticamente quando C recebeu o ARP ou após ele tentar enviar a resposta do ping?” Se você pensou isso, tem razão. Apenas olhando a tabela não dá para saber, pois quando C tentasse enviar a resposta do ping, ele iria primeiro enviar uma mensagem ARP para A, de modo que a tabela seria preenchida do mesmo jeito! Para termos certeza que C não enviou nenhuma mensagem de requisição ARP, basta capturarmos os quadros transmitidos. Isso pode ser feito em A, em C, ou em qualquer máquina da rede, uma vez que os quadros são enviados em broadcast. Vamos capturar na máquina B – veja Figura 4(c). Na Figura 10, que mostra os quadros capturados durante o envio e a resposta do ping de A para C, você pode ver que em nenhum momento C envia mensagens de requisição ARP para A! Ele envia apenas a resposta ao pedido feito por A. A linha 4 da Figura 10 mostra a mensagem de ARP Request (requisição), onde 10.1.1.1 perguntou quem tem (Who-has) o IP 10.1.1.3. A linha 5, mostra o ARP Reply (resposta), onde 10.1.1.3 responde dizendo que o endereço Ethernet é (is-at) AA:AA:AA:00:00:03. As demais 6 linhas representam os três pacotes ping trocados entre as máquinas. Desse modo, as linhas 6 e 7 são referentes ao primeiro pacote ping (veja a indicação seq 1), e mostram, respectivamente, a requisição ping enviada de 10.1.1.1 para 10.1.1.3 e a resposta enviada de 10.1.1.3 para 10.1.1.1. Figura 10 - Captura de quadros na rede durante a comunicação entre A e C Quando não obtemos resposta Vamos ver agora como fica a tabela ARP quando tentamos nos comunicar com alguém que não responde a mensagem ARP (provavelmente porque a máquina está desligada, ou porque digitamos um endereço IP que não pertence a ninguém da rede). Figura 11 - Tabela ARP com endereço Ethernet não localizado Veja na Figura 11 que inicialmente enviamos um ping para o endereço IP 10.1.1.5, para o qual sabemos não existir nenhuma máquina com este endereço na rede. A resposta do ping, “Destination Host Unreachable”, significa “Máquina de Destino Inalcançável”, ou seja, ninguém respondeu ao ping enviado. A verdade é que o ping nem mesmo chegou a ser enviado! Como a máquina de origem e a de destino pertencem à mesma rede (10.1.1.0), a máquina A enviou primeiro uma mensagem ARP perguntando pelo Ethernet de 10.1.1.5, mas ninguém respondeu! O valor “(incomplete)“ na coluna HWaddress mostra que não foi possível descobrir o endereço Ethernet associado ao IP 10.1.1.5. Como esta entrada na tabela está “incompleta”, sempre que alguém tentar se comunicar com o endereço IP 10.1.1.5 uma nova mensagem ARP será enviada. Alterando a tabela ARP Além de verificar o conteúdo da cache também é possível excluir alguma entrada ou cadastrar uma entrada de modo permanente. Saiba, entretanto, que normalmente você não precisa utilizar esses comandos, pois o ARP funciona sem requer nenhuma configuração. O cadastro permanente de endereços na cache é chamado estático e asentradas inseridas desta forma nunca são apagadas depois de certo tempo. Embora forneça um nível maior de segurança que o modo dinâmico, pois dificulta a tentativa de alguém em se passar por um determinado endereço, é menos flexível que o modo dinâmico. – se a máquina mudar seu IP ou Ethernet, você terá que mudar o registro manualmente. O comando para inserir um registro associando o endereço Ethernet xx:xx:xx:xx:xx:xx com o IP A.B.C.D é: arp –s A.B.C.D xx:xx:xx:xx:xx:xx. A exclusão de registros da cache é muito utilizada quando você quer fazer testes, e eventualmente quando você detecta que algum endereço na cache ainda não foi atualizado após você trocar a placa de rede de alguma máquina. A Figura 12 mostra como é possível excluir o endereço Ethernet associado a um IP. Veja que após a exclusão o endereço aparece como “incompleto”. Figura 12 - Excluindo um endereço Ethernet da tabela ARP Máquinas em redes diferentes e o ARP Vamos agora analisar o que acontece com o ARP em um cenário diferente do que usamos nos exemplos anteriores, onde todas as máquinas estavam na mesma rede. Nesse nosso cenário, que é mostrado na Figura 13, as máquinas A e C estão em duas redes diferentes, e a máquina B é agora um roteador que possui duas placas de rede, uma conectada a cada rede. Figura 13 - ARP com máquinas em redes diferentes Observe que a placa de rede eth0 de B possui IP 10.1.1.1 e Ethernet AA:AA:AA:00:01:01, e a placa eth1 possui IP 10.2.2.1 e Ethernet AA:AA:AA:00:02:01. Além disso, o Gateway (roteador padrão) da máquina A é 10.1.1.1 e o Gateway da máquina C é 10.2.2.1. O que será que acontece quando A realiza um ping para C? Qual endereço Ethernet será colocado na Tabela ARP de A? Vamos olhar logo como a tabela ARP fica, e depois explicamos. Veja na Figura 14 a tabela cache de A após o ping para C. Figura 14 - Tabela ARP após ping para máquina em outra rede Entendeu o que aconteceu? Ou seja, se tentamos nos comunicar com a máquina C, por que apareceram os endereços IP e Ethernet do roteador (máquina B)? A resposta é que como A e C estão em redes diferentes, não há como enviar o pacote IP diretamente para C. Esse pacote precisa ser enviado para o roteador da rede onde A se encontra. Se tiver com dúvidas quanto a esse este assunto, volte na parte de roteamento da disciplina de Sistemas de Conectividade e faça uma revisão. Assim sendo, não faria sentido enviar uma ARP tentando descobrir o Ethernet de C. Para enviar o pacote IP para o roteador é necessário colocar o endereço Ethernet do roteador no campo de destino do quadro Ethernet. É aqui que o ARP entra. Como a máquina B sabe o endereço IP do roteador da sua rede, ela envia uma requisição ARP perguntando por esse IP. Na verdade ela não quer enviar nenhum pacote para o endereço IP do roteador, ela quer apenas descobrir seu Ethernet! A Figura 15 mostra os pacotes capturados (apenas os primeiros pacotes) na máquina B, quando a máquina A enviou um ping para C. Veja que B recebe uma requisição ARP vinda de A (10.1.1.2) para seu IP (10.1.1.1), e envia a resposta. Observe também que, embora os pacotes de ping sejam enviados ao roteador (para seu Ethernet), naturalmente o endereço IP de destino do pacote é o IP de C (10.2.2.2). Figura 15 - Mensagens ARP quando as máquinas origem e destino estão em redes diferentes Resumo Nesta aula você aprendeu que o protocolo ARP traduz endereços da camada de rede para endereços da camada de enlace (endereços MAC), e que na maioria das redes esses protocolos são o IP e o Ethernet. Aprendeu que esse protocolo envia uma mensagem ARP Resquest para o endereço de broadcast da rede perguntando quem tem um determinado endereço IP. Viu que, após isso, a máquina que possui esse IP envia uma resposta diretamente para quem enviou a solicitação, contendo o Ethernet associado ao IP informado. Você aprendeu também que para reduzir o tráfego gerado na rede, e o tempo para obter a informação, o ARP faz uso de uma tabela cache (chamada de tabela ARP) que mantém os endereços (IP e Ethernet) recentemente descobertos. Acesse o Laboratório 1 desta aula (Aula 1) no Virtualbox e realize os seguintes procedimentos. 1. Verifique a tabela ARP da máquina A. 2. Coloque o tcpdump para executar na máquina B. Execute um ping da máquina A para a máquina C. Depois verifique como ficaram as tabelas ARP das máquinas A, B, e C. 3. Verifique os pacotes capturados no passo anterior pelo tcpdump e veja se foram trocados os pacotes que você esperava. 4. Execute o ping de A para C novamente e veja os pacotes capturados em B. 5. Na máquina A, exclua a entrada referente ao IP de C e execute o ping novamente de A para C. Mais uma vez analise os pacotes capturados em B. Referencias FOROUZAN, B. Comunicação de dados e redes de computadores. 3. ed. Porto Alegre: Bookman, 2006. KUROSE, J.; ROSS, K. Redes de computadores e a internet. 5. ed. São Paulo: Addison Wesley, 2010. TANENBAUM, Andrew S. Redes de computadores. 4. ed. Rio de Janeiro: Editora Elsevier, 2003. Voltar Imprimir Topo Na disciplina Sistemas de Conectividade você aprendeu o que é uma rede e como as máquinas são capazes de se comunicar. Aprendeu também que o modelo OSI divide as funções de uma rede em sete camadas, mas naquela disciplina mantivemos o foco no estudo das camadas: física, enlace e rede. Caso não lembre, é importante retomar o estudo destes temas, pois precisaremos deles nesta disciplina. Agora você sabe que atualmente as redes utilizam o modelo TCP/IP, que, diferentemente do modelo OSI, possui apenas a camada de aplicação acima da camada de transporte. O foco desta disciplina é estudar detalhadamente os principais protocolos da camada de aplicação da pilha TCP/IP. Para que as aulas fiquem mais interessantes e lhe ajudem a colocar seu conhecimento em prática, além de estudar os protocolos propriamente ditos, você aprenderá a instalar os serviços que são implementados com eles, como por exemplo web, e-mail, DNS, entre outros. Antes de estudarmos os protocolos de aplicação, iremos complementar nossos estudos sobre a camada de rede, iniciados na disciplina anterior, e estudar os protocolos UDP e TCP da camada de transporte. Portanto, caso você tenha achado a disciplina de Sistemas de Conectividade muito teórica, e se sentido um pouco frustrado por não ter aprendido a configurar muitas coisas de uma rede de verdade, anime-se, porque esta disciplina é bastante prática! Você aprenderá tanto a teoria de cada assunto explicado como a realizar configurações reais nas máquinas. A ideia é que ao término desta disciplina você consiga realmente configurar uma rede real! Para tornar esta disciplina bastante prática, você irá utilizar algumas ferramentas que são utilizadas por muitas pessoas que trabalham com informática atualmente. O interessante é que, além de aprender algo que será bastante útil quando você for trabalhar com informática, essas ferramentas são bastante adequadas para estudarmos redes de computadores. Por isso, nesta aula e na próxima, vamos estudar algumas dessas ferramentas e como elas serão usadas para criar os ambientes que utilizaremos como exemplo durante as aulas desta disciplina. Nesta aula estudaremos um dos assuntos que têm recebido maior destaque atualmente, e que se faz cada vez mais presente nas empresas, que são as máquinas virtuais. Ao término desta disciplina você será capaz de: • Entender o que é uma máquina virtual. • Instalar várias máquinas virtuais em um mesmo computador usando o VirtualBox. • Aprender os modos de rede suportados pelas máquinas virtuais criadas com o VirtualBox. Máquinas virtuais Tradicionalmente, se você precisasse de duas “máquinas” independentes, cada uma executando sua própria cópia do sistema operacional, você utilizaria dois computadores diferentes para esta finalidade. Supondo, por exemplo, que você quisesse ter uma máquina com o sistema operacional Windows e outra com o Linux, você compraria dois computadores e instalaria o Windows em um e o Linux em outro. À medida que o hardware dos computadores evoluiu, cada máquina passou a ter capacidade de processamento muito grande, que muitas vezes não é utilizada na sua totalidade pelos programas executados na máquina. Para aproveitar esses recursos que ficam ociosos, foi criado um mecanismo que permite executar mais de um sistema operacional em um mesmo computador, sendo que cada um tem a impressão de que a máquina (o hardware) é apenas dele. Ou seja, cada sistema operacional se comporta realmente como se fosse uma máquina fisicamente separada. Os sistemas operacionais não sabem que estão compartilhando um mesmo computador! O mecanismo que citamos no parágrafo anterior chama-se virtualização, e é uma das grandes áreas da computação atualmente. Muitas empresas já utilizam virtualização, e ao invés de terem um número elevado de computadores atuando como servidores, elas têm um número bem menor de máquinas reais, sendo que em cada uma delas são criadas e executadas diversas máquinas virtuais diferentes. Como cada máquina virtual executa seu próprio sistema operacional, elas podem usar o mesmo sistema operacional, ou sistemas operacionais diferentes. O importante é que elas se comportam como se fosse máquinas reais diferentes. Uma das grandes vantagens da virtualização é que os recursos de hardware são melhores utilizados. Suponha que você tem uma máquina real com velocidade de processamento de X GHz e Y GB de memória RAM, e nesta máquina real são criadas duas máquinas virtuais. Você pode alocar, por exemplo, X/2 GHz de processamento e Y/2 de Memória para cada máquina. A vantagem é que você pode dizer que se alguém não estiver usando toda a capacidade que lhe foi reservada, esse recurso seja utilizado pela outra máquina virtual! Se as máquinas fossem realmente dois computadores separados essa tarefa normalmente não teria como ser feita. Figura 1 – Máquinas virtuais em uma mesma máquina real A forma mais comum de utilizar o esquema descrito acima é utilizar um software de virtualização sobre o qual os outros sistemas operacionais são executados. Nas máquinas de empresas, onde normalmente vão ser criadas várias máquinas virtuais em um único computador real, esse software de virtualização é, ele próprio, um sistema operacional. A Figura 1 ilustra este processo, onde em: a) é mostrada a arquitetura do ambiente de máquinas virtuais, e b) são mostrados exemplos de programas utilizados, tanto para o software de virtualização como nas máquinas virtuais. Como exemplos do software de virtualização podemos citar o VMware [https://www.vmware.com/br/] ou o Xen [https://www.xen.org/]. Esses softwares são sistemas operacionais que suportam virtualização, portanto eles são instalados no computador real (naturalmente, um deles em cada máquina real). A máquina virtual 1 poderia estar executando o sistema operacional Windows e a máquina virtual 2 um Linux, ou as duas poderiam estar executando Linux, ou as duas Windows. Na verdade, qualquer sistema operacional pode ser instalado nas máquinas virtuais, pois, como dissemos, ele não toma conhecimento que está sendo executado sobre uma máquina virtual. Isso acontece porque o software de virtualização cria um hardware virtual de modo que o sistema operacional da máquina virtual enxerga realmente como se existisse um computador. No começo desta aula dissemos que você iria utilizar máquinas virtuais. Por isso, talvez você esteja preocupado pensando que vai ter que apagar o sistema operacional da máquina que utiliza para instalar um sistema operacional com suporte à virtualização. Calma! Isso seria complicar as coisas para você, e não é isso que queremos. A boa notícia e que existem softwares de virtualização para serem executados sobre o sistema operacional que você já utiliza. Ou seja, esses softwares não são sistemas operacionais! Assim, seja qual for o seu sistema operacional, é possível instalar um software de virtualização nele e criar as máquinas virtuais. Para efeitos de instalação, esse software de virtualização é um programa como outro qualquer. Ou seja, você o instala como faz com qualquer outro programa. A Figura 2 mostra este modelo. Figura 2 – Software de virtualização sobre um sistema operacional Veja que o programa de virtualização é apenas um software a mais que está instalado no seu sistema operacional. A caixa “Prog”, na Figura 2(a), mostra que existem vários outros programas na máquina – como o Office, mostrado em 2(b) – que executam diretamente sobre o sistema operacional principal. Além disso, mostramos Windows como o sistema operacional principal da máquina, mas também poderia ter sido utilizado outro, como o Linux, por exemplo. Basta que exista um software de virtualização para ele. O software mostrado na Figura 2(b), chamado VirtualBox [https://www.virtualbox.org/], existe tanto para Linux como para Windows, e é o software que utilizaremos nesta disciplina. Vamos agora realizar uma atividade para treinar um pouco o seu conhecimento. 1. Acesse o site do VirtualBox e do VMware e verifique se eles possuem versões que executam como sistema operacional (modelo apresentado na Figura 1) e versões para serem executadas sobre um sistema operacional já utilizado (modelo apresentado na Figura 2). 2. Caso eles (ou algum deles) suportem a versão para executar sobre um sistema operacional já existente, veja quais são os sistemas operacionais suportados. “Se existe essa versão que eu posso instalar sobre o meu sistema operacional, por que alguém iria usar a outra, que requer a instalação de um sistema operacional específico?” A resposta é desempenho e gerenciamento dos recursos de hardware, que são mais completos na versão do software que é um sistema operacional. Assim, quando você pretende criar diversas máquinas virtuais sobre o mesmo computador, como é o caso de muitas empresas, utilize a versão que é um sistema operacional. Quando for criar uma, ou mesmo poucas máquinas virtuais, mas que não venham a ter um uso muito intenso, você pode utilizar a versão que executa sobre o seu sistema operacional. Portanto, fique tranquilo. Como para os exemplos que utilizaremos nesta disciplina vamos criar poucas máquinas virtuais, tipicamente duas ou três, e essas máquinas não vão ter uma carga de processamento elevada, podemos usar tranquilamente um software de virtualização sobre o nosso próprio sistema operacional. Como dissemos anteriormente, iremos utilizar o VirtualBox. DICA Sites interessantes: • (Português) • (Português) • (Português) • (Inglês) VirtualBox Nesta seção abordaremos a instalação do VirtualBox, que executa sobre o seu sistema operacional já instalado. Uma coisa interessante é que a instalação do VirtualBox pode ser um pouco diferente dependendo do seu sistema operacional, porém a criação de uma máquina virtual segue o mesmo procedimento – quer você esteja usando a versão para Linux, quer para Windows. Além da instalação propriamente dita do VirtualBox (utilizaremos a versão 3.x), você também aprenderá a criar e configurar uma máquina virtual. Para melhor entendermos o que é e como funciona uma máquina virtual, primeiro temos que compreender o que é uma máquina host e uma máquina guest. Uma máquina host é uma máquina hospedeira, ou seja, aquela que receberá as máquinas convidadas (guest). Dizendo de outra forma, a máquina host é a máquina real onde o sistema operacional principal está instalado. Olhe novamente a Figura 2(b). A máquina host é a máquinapropriamente dita, compreendida pelo hardware e pelo sistema operacional mostrado no retângulo chamado “Windows”. As máquinas guest são as máquinas instaladas dentro do software de virtualização. Na Figura 2(b) esse software de virtualização é representado pelo retângulo “Virtualbox”, e as máquinas guest sãorepresentadas pelos retângulos “Linux”e “Win”. Desse modo, a máquina host será responsável por reservar parte dos seus recursos para cada máquina convidada, e essa reserva é feita no momento da criação dessas máquinas. Apesar de serem especificados no momento da criação da máquina guest, esses recursos podem ser alterados posteriormente. Suponho que você esteja ansioso para ver como criar e usar essas máquinas virtuais. Então vamos começar vendo como fazer a instalação do Virtualbox, que é um processo bastante simples. Depois veremos um roteiro passo a passo como criar as máquinas virtuais dentro dele. Como dissemos, existem versões do Virtualbox para diversos sistemas operacionais. Quando for utilizá-lo escolha a versão para o sistema operacional que você utiliza. A seguir mostramos exemplos de executáveis do virtualBox para alguns sistemas operacionais. Figura 3 – Executáveis do Virtualbox para diferentes sistemas operacionais Vamos agora ver como instalar o VirtualBox no Ubuntu. Neste caso você nem precisa saber o nome do executável, basta utilizar o mecanismo de instalação de programas do Ubuntu (o que é feito, por exemplo, usando-se o comando apt-get install). Portanto, para instalar, faça o seguinte: 1° abra o terminal(gome-terminal) ou xterm; 2º entre em modo super usuário(root) com: sudo su e digite a sua senha; 3º digite o comando a seguir para iniciar a instalação: apt-get install virtualbox-ose 4º aceite a licença e pronto. Máquina hospedeira pronta. A Figura 4 mostra este processo após a abertura do terminal (que foi descrita no 1º passo). Naturalmente, o nome da máquina e do usuário que irão aparecer para você serão diferentes. ATENÇÃO Lembre-se que a instalação é feita na máquina host, que poderia ser uma máquina na sua casa, ou uma máquina de um laboratório de informática da sua escola. Portanto, o nome da máquina e o nome do usuário dependem da máquina onde você está instalando o Virtualbox. Figura 4 – Instalação do Virtualbox no Ubuntu usando o comando apt-get Agora que o Virtualbox está instalado, veremos como criar uma máquina virtual convidada (guest). As operações mostradas a seguir são iguais para todos os sistemas operacionais. Primeiro inicie o Virtualbox, clicando no menu “Aplicativos” do Linux (canto superior esquerdo da tela), depois em “Acessórios” e finalmente em “Virtualbox OSE”. Após isso, aparecerá a tela do Virtualbox com um ambiente semelhante ao mostrado na Figura 5. O ambiente abaixo mostra que nosso host tem duas máquinas virtuais guest instaladas (“Ubuntu9.10” e “Windows XP”). Após uma nova instalação, evidentemente não haverá nenhuma máquina criada e essa parte estará em branco. Figura 5 – Tela inicial do VirtualBox Como exemplo, criaremos mais uma máquina virtual com o nome Máquina_A. Para isso, clicamos no botão Novo ou no menu Aquivo(F) e depois clicamos em Novo. A próxima tela que surgirá é mostrada na Figura 6, onde se deve clicar em Próximo(N). Figura 6 – Primeira tela no processo de criação de uma máquina virtual O próximo passo consiste em definirmos o nome da máquina guest e o sistema operacional que será utilizado, conforme é mostrado na Figura 7. Chamaremos a nossa máquina de “Maquina_A” e escolheremos o sistema operacional “Linux/Ubuntu”. Observe que existem várias versões do Windows e várias versões do Linux, além de outros sistemas operacionais. Escolha o que você pretende instalar na máquina virtual que está criando. Portanto, atenção! Não pense que ele já ira instalar o sistema operacional. Quando terminar de criar a máquina virtual, você é que terá de instalar o sistema operacional escolhido na máquina virtual. Figura 7 – Definição do nome da máquina e do sistema operacional O próximo passo é bastante importante e consiste em dimensionamos a memória da máquina real que será reservada para a máquina virtual, conforme mostrado na Figura 8. Para escolher um valor apropriado analise três coisas: i. quanto de memória disponível existe em seu computador (máquina real); ii. qual o sistema operacional que a máquina virtual irá executar (escolhido na etapa anterior); iii. quais aplicações serão executadas na máquina virtual, ou seja, qual a finalidade desta máquina. Caso esteja criando uma máquina apenas para estudos, utilize o valor sugerido pelo VirtualBox. Apesar de ser um parâmetro muito importante, não se preocupe demais com ele neste passo, porque você poderá alterá-lo a qualquer momento depois que a máquina for criada. Figura 8 – Definindo a memória que será alocada para a máquina virtual Agora, alocamos um espaço em disco para nossa máquina virtual. Como normalmente você não terá um disco rígido exclusivo para ser utilizado pela máquina virtual, o VirtualBox cria um arquivo na máquina host que serve como o disco rígido da máquina virtual. Pois é, o sistema operacional que você instalar na máquina virtual, e todos os arquivos que criar, serão armazenados dentro de um único arquivo da máquina real (host). Conforme mostrado na Figura 9, selecione “Disco Rígido de Boot (Primário Master)” e “Criar novo disco rígido” para que o arquivo que representará seu disco rígido seja criado. Figura 9 – Definição do disco rígido da máquina virtual Não se preocupe se parecer confuso o fato de seu disco rígido ser um único arquivo. Você não precisará lidar com isso para utilizar sua máquina virtual, pois ela verá um disco rígido normal. Clique em próximo e será mostrada a tela apresentada na Figura 10. Figura 10 – Início do assistente para configuração do novo disco rígido Como pode ver, essa tela apenas avisa que iremos entrar no assistente para configuração do disco rígido da máquina virtual. Clique em próximo para que seja apresentada a tela mostrada na Figura 11, onde deve ser definido como será a alocação de espaço no disco real da máquina host para o disco virtual. Isso é chamado de tipo de armazenamento, que pode ser fixo ou dinâmico. No fixo, você define exatamente quanto de espaço do disco da máquina host será reservado para a máquina convidada. O problema deste método é que se sua máquina convidada precisar de mais espaço não há como aumentar o tamanho deste disco, embora seja possível criar outros discos virtuais. O método dinâmico funciona da seguinte forma: você define um tamanho padrão, e se houver necessidade de mais espaço o virtual Box solicitará ao sistema operacional host mais espaço para sua máquina convidada, fazendo o disco dela aumentar de tamanho. Esse método é muito mais flexível e é o método mais indicado. Portanto, selecione “Armazenamento dinamicamente expansível” na tela mostrada na Figura 11 e clique em Próximo. Figura 11 – Configuração do espaço utilizado pelo disco virtual Nesta etapa deve-se definir o tamanho inicial do disco para o guest. Sugerimos utilizar o padrão 8GB, mas do mesmo modo que a definição do tamanho da memória, o valor mais adequado dependerá da finalidade que a máquina terá. Figura 12 – Definição do tamanho do disco da máquina convidada Agora é só clicar em “Finalizar” na tela mostrada na Figura 13 e pronto. Terminamos! A máquina virtual convidada (guest) está criada. Figura 13 – Concluindo a criação da máquina convidada Como dissemos, a criação da máquina convidada não instala o sistema operacional que escolhemos automaticamente. Portanto, a próxima etapa é instalar o sistema operacional na máquina virtual. Para isso, vá à tela principal do VirtualBox, conforme mostrado na Figura 14, e clique na máquina recém-criada. Depois clique no botão “Configurações” que está ao lado do botão “Novo”. Figura 14 – Máquina convidada recém-criada Será apresentada a tela mostrada na Figura 15, onde podemos alterar as configurações da máquina virtual criada. Podemos entrar nesta opção a qualquer momento, desde que a máquina convidada não esteja sendo executada, como é o nosso caso, uma vez que ela acabou de ser criada. Vamos clicar em “Sistema” nas opções mostradas na parte esquerda da tela. Isso nos permitirá definir a ordem de boot da máquina, ou seja, por quais dispositivos a máquina tentará iniciar quando a iniciarmos. Lembre-se que em uma máquina real fazemos isso entrando na BIOS da máquina (normalmente apertando a tecla enquanto a máquina está iniciando). Para que possamos fazer a instalação de um novo sistema operacional a partir do CD/DVD é importante que na lista “Ordem de Boot” a opção “CD/DVD” apareça mais acima que “Disco Rígido”. A ordem mostrada pode ser alterada clicando-se no item desejado, por exemplo, “CD/DVD”, e depois clicando-se nas setas que aparecem ao lado. Figura 15 – Configuração da ordem de boot Após termos verificado que “CD/DVD” está acima de “Disco Rígido”, podemos fechar a tela de configuração. Agora podemos colocar o CD (ou DVD) contendo o sistema operacional que queremos instalar na unidade e clicar no botão “Iniciar” na tela principal do VirtualBox. Veja que do mesmo modo que aconteceria em uma máquina real, nossa máquina convidada irá tentar (e conseguir) inicializar pelo CD. A partir deste ponto você continua a instalação como faria em qualquer máquina real, pois, como dissemos, o sistema operacional sendo instalado não sabe da existência da máquina virtual. Depois que terminar a instalação e retirar o CD da unidade, sempre que precisar iniciar a máquina convidada criada basta clicar no nome dela e depois no botão “Iniciar”, na tela principal do VirtualBox. Como não vai mais haver um CD na unidade, e vai haver um sistema operacional instalado no disco rígido da máquina convidada, a inicialização ocorrerá pelo disco rígido. Mudamos o boot para o CD, mas o sistema depois de instalado não necessitará mais de iniciar pelo CD, certo? Como faríamos para alterar para o boot pelo disco? Pense no caso de você não ter o sistema operacional queimado no CD, mas sim uma iso, ou seja, uma imagem do sistema em sua máquina. Como faríamos para instalar a partir da *.iso? Caso haja alguma dúvida, utilize este vídeo para te guiar: • Configuração de rede no VirtualBox Resolvemos criar uma seção separada para explicar a configuração de rede das máquinas virtuais criadas no VirtualBox porque isso é algo de fundamental importância para nossa disciplina. Pretendemos que todos os exemplos que utilizarmos nesta disciplina possam ser reproduzidos com uma rede formada apenas por máquinas virtuais criadas no VirtualBox. Por isso, é muito importante que você entenda como é a configuração de rede das máquinas virtuais para que possa realizar as práticas sugeridas nas aulas. Na tela principal do VirtualBox, clique no botão “Configurações” e depois em Rede, para que apareça a tela mostrada na Figura 16. Nesta tela podemos dizer quantas placas de rede vão existir na máquina convidada. São suportadas até quatro placas. Para ativar uma placa basta clicar em uma das abas identificadas por Adaptador X (onde X pode ser 1, 2, 3 ou 4) e clicar em “Habilitar placa”. Figura 16 – Configuração das placas de rede de uma máquina virtual Quando habilitamos uma placa, devemos escolher o modo como a placa irá funcionar, no campo identificado como “Conectado a:”. As opções e a forma como cada uma trabalha são as seguintes: • Não conectado. Neste modo sua placa de rede estará desconectada de qualquer rede. Fazendo uma analogia, imagine uma máquina real que possui uma placa de rede Ethernet, mas que não possui nenhum cabo de rede conectado nela. Você pode até colocar um endereço IP nesta placa, mas não terá como se comunicar com nenhuma outra máquina. • NAT. Neste modo é como se existisse uma rede entre a placa de rede da máquina convidada e a placa de rede da máquina host, e a máquina host realizasse NAT. Você estudará NAT (Network Address Translation– Tradução de endereços de Rede) na Aula 4. A máquina host fornece a configuração IP da máquina convidada através de um servidor DHCP que a máquina host executa (você estudará DHCP em uma aula posterior nesta disciplina). Este servidor DHCP é iniciado automaticamente pelo VirtualBox. A coisa mais importante para você saber sobre este modo é que ele permite que a máquina convidada acesse as máquinas da rede onde a máquina host está. Além disso, se a máquina host tiver acesso à Internet, a máquina convidada também terá. A Figura 17 ilustra essa configuração. Figura 17 – Máquina virtual usando placa de rede em modo NAT • Placa em Modo Bridge. Este modo é semelhante ao NAT, pois é como se existisse uma rede entre a máquina host e a máquina convidada. Porém, ao invés da máquina host realizar NAT, ela atua como se fosse uma bridge (ponte) entre a rede da máquina host e a rede da máquina convidada. Com isso é como se a máquina convidada estivesse na mesma rede que a máquina host está. A diferença é que a configuração IP da máquina convidada não será fornecida pela máquina host. Deve ser configurada manualmente ou fornecida pelo servidor DHCP da rede onde a máquina host se encontra. Da mesma forma que no modo NAT, neste modo a máquina convidada pode acessar as máquinas da rede da máquina host, e a Internet, caso esse acesso esteja disponível para aquelas máquinas. • Rede Interna. Este é o modo que você mais utilizará para praticar os exemplos que utilizaremos nesta disciplina. Felizmente ele é o mais simples de entender. Neste modo você cria redes completamente isoladas da rede real e da máquina host. Quando você seleciona este modo aparece uma caixa de texto chamada “Nome”. Isso é o nome da rede à qual a máquina pertencerá. Coloque qualquer nome que desejar. Depois que colocar um nome uma vez, ele aparecerá no menu quando for configurar outras máquinas neste mesmo modo (Rede Interna). Todas as máquinas que possuírem o mesmo nome neste campo conseguirão se comunicar. É como se estivessem ligadas em um mesmo hub. A Figura 18 mostra quatro máquinas virtuais dentro de uma única máquina real. As máquinas virtuais (convidadas) 1 e 2 foram configuradas para pertencerem a uma rede chamada “Rede 1”, enquanto as máquinas virtuais 3 e 4 pertencem à rede “Rede 2”. Desse modo, as máquinas 1 e 2 podem se comunicar entre si, e o mesmo ocorre entre as máquinas 3 e 4. Mas uma máquina de uma rede não pode se comunicar com nenhuma máquina da outra rede. Figura 18 – Máquinas virtuais utilizando o modo “Rede Interna” • Placa de rede exclusiva de hospedeiro (Host-Only). Neste modo a máquina convidada pode se comunicar apenas com a máquina host. Observe que podemos ter mais de uma placa de rede em uma mesma máquina convidada. Desse modo, cada placa de rede pode usar um modo diferente. Um cenário interessante seria, por exemplo, criarmos uma segunda placa de rede na Máquina Virtual 1 da Figura 18 e a colocarmos em modo NAT. Isso permitira que as máquinas da rede 1 acessassem as máquinas reais da rede onde a máquina host pertence e a Internet. Práticas usando o VirtualBox Durante a disciplina utilizaremos vários exemplos de redes para explicarmos o funcionamento de protocolos e para explicar como configurar e testar as diversas aplicações. O material contido no texto é suficiente para você entender o que pretenderemos explicar. Entretanto, seria muito interessante que você conseguisse reproduzir os procedimentos que realizarmos, e uma forma muito conveniente de fazer isso é utilizando máquinas virtuais. Recomendamos fortemente que você acesse este link para ver instruções de como fazer isso. Aviso: Acesse a máquina virtual do servidor apenas para se acostumarem com ela, testem como iniciar, desligar, e como trabalhar com a interface gráfica. Uma vez que você se desconectar do servidor, será perdido tudo que foi feito, porque sempre a cada nova conexão é criada uma máquina nova (com a configuração padrão) para você. Portanto, você precisa realizar toda a atividade de uma vez. Sabemos que isso não é o ideal, mas não dispomos de hardware suficiente para todos, esta foi a forma que encontramos para viabilizar as máquinas virtuais. Resumo Nesta aula você aprendeu que a tecnologia chamada de virtualização permite que criemos várias máquinas virtuais em uma única máquina real, e que cada máquina virtual pode executar qualquer sistema operacional. Ou seja, podemos ter uma máquina virtual Windows e duas máquinas Linux, por exemplo, sendo executadas em um único computador. Você aprendeu, também, que para isso funcionar é necessário um software de virtualização, e que esse software pode ser apenas um programa que é instalado sobre um sistema operacional, ou pode ele próprio ser um sistema operacional. Finalmente, você aprendeu que existem diversas formas de uma máquina virtual utilizar a placa de rede da máquina host, e em um desses modos é possível conectar as diversas máquinas virtuais criadas em um mesmo de modo a formar uma rede de computadores. Nesta aula vimos como software de virtualização o VirtualBox. 1. Utilize alguma máquina com acesso à Internet para conectar na máquina Windows lab.metropoledigital.ufrn.br (chamaremos esta máquina de servidor) usando o programa Vmware Viewer. Veja instruções de como fazer isso no link (https://www.metropoledigital.ufrn.br/aulas_avancado/ web/disciplinas/rede_comp/praticas.html). 2. Utilize a aplicação de configuração dos laboratórios, que já é iniciada automaticamente após se conectar no servidor, e escolha a atividade “Autoavaliação” da Aula 1. 3. Nessa aula não existe uma tarefa a ser realizada na Maquina-A. Ela serve para você se familiarizar com o ambiente que irá utilizar nas aulas, ou seja, com a forma de conectar no servidor, como selecionar a aula desejada, e como utilizar as máquinas virtuais. Portanto, você deve apenas analisar os menus, e verificar como liga e desliga a máquina virtual. Para explicação sobre o ambiente gráfico das máquinas virtuais (https://www.metropoledigital.ufrn.br/aulas_avancado/ web/disciplinas/rede_comp/blackbox.html). Observe que nesta aula existe apenas uma máquina virtual disponível. Nas demais aulas normalmente você utilizará duas ou mais máquinas virtuais. 4. Instale o VirtualBox em um computador. 5. Crie uma máquina virtual no VirtualBox e instale o sistema operacional Ubuntu. 6. Clone a máquina criada na Questão 2. Este vídeo irá te guiar caso seja necessário:https://www.youtube.com/watch?v=3vxeOgicTDw. 7. Configure as placas de rede de cada máquina para o modo “Rede Interna”, utilizando como nome da rede “Rede1”. Utilize como endereços IP 192.168.1.1 e 192.168.1.2, e como máscara, nas duas máquinas, 255.255.255.0. Realize um ping da máquina 192.168.1.2 para 192.168.1.1. Deve funcionar, ou seja, o ping deve obter resposta! Referencias INTEL. Virtualização. Disponível em: . Acesso em: 8 nov. 2010. OLIVEIRA, R.; CARISSIMI, A.; TOSCANI, Simão. Sistemas operacionais. Porto Alegre: Editora Bookman, 2009. TUTORIAL VirtualBox: como instalar e configurar uma máquina virtual. Disponível em: . Acesso em: 8 nov. 2010. [TUTORIAL] Máquina Virtual VMware. Disponível em: . Acesso em: 8 nov. 2010. VIRTUALBOX. Disponível em: . Acesso em: 2 set. 2010. VMWare. Disponível em: . Acesso em: 2 set. 2010. XEN. Disponível em: . Acesso em: 2 set. 2010. Esta aula é a última das três aulas desta disciplina que são dedicadas a lhe ensinar a trabalhar com ferramentas que além de serem muito utilizadas pelas pessoas que administram redes de computadores, são utilizadas nesta disciplina como forma de lhe proporcionar a prática dos conteúdos ministrados. Nesta aula estudaremos um tipo de ferramenta que consegue capturar pacotes transmitidos em uma rede. Esse tipo de programa é muito útil para análise da rede e detecção de problemas. Veremos um programa que trabalha em modo texto, o Tcpdump,e outro que utiliza uma interface gráfica, o Wireshark. Ao final desta aula você será capaz de: • Utilizar a ferramenta Wireshark para capturar pacotes na rede. • Utilizar a ferramenta Tcpdump para capturar pacotes na rede. • Entender a forma como os pacotes são exibidos, de modo que possa interpretar as informações mostradas nos pacotes capturados. • Identificar em que máquina da rede executar a ferramenta de captura de pacotes. Captura de pacotes Como você estudou na disciplina de Sistemas de Conectividade, quando se deseja transmitir uma mensagem pela rede, cada camada TCP/IP na máquina de origem acrescenta um cabeçalho a essa informação. Na máquina de destino as camadas equivalentes retiram esses cabeçalhos. Você viu também que o conjunto cabeçalho + mensagem de cada camada recebe um nome. Na camada de enlace o chamamos de quadro, na camada de rede de pacote, na camada de transporte de segmento e na camada de aplicação de mensagem. Nesta aula você estudará uma ferramenta que permite a captura dos quadros transmitidos e recebidos por uma placa de rede. Como um quadro contém um pacote, que por sua vez pode conter um segmento, que por sua vez pode conter uma mensagem, esse tipo de ferramenta é genericamente chamada de ferramenta de captura de pacotes,e não captura de quadros. Desse modo, as ferramentas de captura de pacotes nos mostram informações de todas as camadas do modelo TCP/IP que estão presentes no pacote capturado. Suponha, por exemplo, que você capturou um pacote gerado pela aplicação ping em uma máquina com uma placa de rede Ethernet. Como a informação enviada pelo ping é uma mensagem do protocolo ICMP, que é transmitida dentro de um pacote IP, que por sua vez é transmitido dentro de um quadro Ethernet, a ferramenta de captura irá lhe mostrar informações sobre os cabeçalhos Ethernet, IP e ICMP. Como outro exemplo, suponha que você capturou informações transmitidas entre um browser e um servidor Web. Como a Web utiliza o protocolo HTTP (um protocolo de aplicação), que é transmitido dentro de segmentos TCP (protocolo de transporte), e cada segmento TCP é transmitido dentro de um pacote IP, que é transmitido dentro de um quadro Ethernet, a ferramenta de captura irá lhe mostrar informações sobre os cabeçalhos Ethernet, IP, TCP e HTTP. Ressaltamos que a ferramenta de captura de pacotes mostra as informações dos cabeçalhos, mas naturalmente ela mostra todos os dados contidos no pacote. Lembre-se também que embora o comportamento padrão de uma placa de rede seja descartar todos os quadros que não são destinados a ela, nem ao endereço de broadcast, pode-se configurar a placa para receber todos os quadros, o que caracteriza o modo promíscuo. Como já visto em Sistemas de Conectividade, isso se chama modo promíscuo, ou modo espião. Quando executamos uma ferramenta de captura de pacotes normalmente ela trabalha utilizando o modo espião. Evidentemente, para poder usar esse modo, é necessário que o usuário seja administrador da máquina. Você deve estar perguntando: “em que máquina eu devo executar a ferramenta de captura de pacotes?”. Pois bem, isso vai depender da finalidade para a qual você está executando a ferramenta. Mas lembre-se de que os dados devem passar por essa máquina para poderem ser capturados. Supondo que você pretende analisar uma comunicação entre as máquinas A e B, as alternativas são: i. Em um roteador no caminho entre A e B; ii. na máquina A; iii. na máquina B; iv. caso a rede onde A (ou B) se encontra use um hub, em uma outra máquina ligada no mesmo hub; v. caso A (ou B) esteja em uma rede sem fio, nessa mesma rede sem fio (desde que a rede não esteja usando criptografia). Wireshark O Wireshark é um dos programas de captura de pacotes mais utilizados. Ele mostra os dados e as informações dos cabeçalhos dos protocolos contidos nos pacotes capturados. Por ser um software Open source, o usuário pode modificar o código para melhor se adaptar às suas necessidades, assim como a documentação que está sob os termos da GNU (General Public License), ou seja, permite sua cópia, modificação e distribuição. Mas não se assuste! Dificilmente alguém precisa fazer isso, e você vai usar o programa sem precisar fazer modificação alguma nele! Existem versões do Wireshark para as plataformas Windows, Linux, MacOs, Solaris, FreeBSD, entre outros. Isso é muito bom, porque evita que você tenha que aprender a utilizar vários programas diferentes para realizar a mesma tarefa. O programa wireshark é muito usado por profissionais de redes, como: • Administradores, com o objetivo de solucionar problemas na rede; • Engenheiros de segurança, com o objetivo de verificar possíveis problemas na segurança; • Desenvolvedores, para encontrar possíveis bugs na implementação do protocolo; • Estudantes, como o objetivo de melhorar o entendimento acerca dos protocolos utilizados em redes de computadores. Vale salientar que o Wireshark também é muito usado pelos hackers para fins ilícitos, ou seja, para fins que contrariam a lei, pois com ele é possível encontrar falhas nas implementações dos protocolos, capturar senhas e obter outras informações. Instalação do Wireshark Nesta seção mostraremos como realizar a instalação do Wireshark, como usar o menu e alguns elementos básicos da sua interface gráfica. Utilizamos para esta aula a distribuição do GNU/Linux Ubuntu 9.10 e 10.04LTS, mas os passos são os mesmos para qualquer distribuição Linux baseada no Debian. Ilustraremos parte das imagens com letras do alfabeto latino (A-Z) para que você se situe melhor em relação à explicação. Várias distribuições Linux, como é o caso do Ubuntu, mantêm todos os seus programas disponíveis em diversos servidores na Internet, e possuem um sistema de instalação de programas (chamado Gerenciador de Pacotes) que faz o download dos arquivos a serem instalados diretamente destes servidores. Ou seja, não é necessário que você faça o download dos arquivos e depois execute o programa de instalação. Você pode utilizar o gerenciador de pacotes, que se chama “apt”, para procurar os programas disponíveis e mandar instalar o programa desejado. Ele será então baixado da Internet e instalado. O gerenciador pode ser executado através do modo texto com o comando apt-get install, ou através do modo gráfico usando o menu Aplicativos->Central de Programas Ubuntu.Veremos os dois modos a seguir. Temos duas observações importantes sobre o Gerenciador de Pacotes do Linux. A primeira é que você já o utilizou nas duas aulas anteriores para instalar os programas Virtualbox e Opensh-server. A segunda observação é que nesse contexto que estamos falando no momento (Gerenciador de Pacotes), o termo Pacotese refere a Programas (e não a um quadro de rede). Para a instalação em modo texto inicialmente você deve abrir um terminal. Isso pode ser feito usando o menu mostrado na Figura 1. Para a melhor visualização do processo, você pode assistir este vídeo: , que mostra os passos da instalação do Wireshark no Ubuntu. Figura 1 – Imagem de abertura do Terminal (gnome-terminal). Para a instalação e execução do Wireshark o usuário deverá ter permissão máxima, ou seja, ser super usuário (root). O comando Linux que transforma o usuário em superusuário é o sudo su, conforme mostrado na Figura 2. Lembramos que as operações usadas nesta aula são aceitas em qualquer distribuição baseada no debian. Figura 2 – Acesso como superusuário. Na Figura 3 é apresentado o comando usado (apt-get install) para instalar um aplicativo existente nos repositórios Linux através do gerenciador de pacotes (programas) apt. Caso deseje mais informações sobre o apt, você pode consultar a página . Figura 3 – Instalação a partir do apt-get Como dissemos, também é possível instalar o Wireshark a partir do modo gráfico, conforme mostrado nas Figuras 4, 5 e 6. Inicialmente abra a Central de Programas do Ubuntu, clicando em Aplicativos->Central de Programas Ubuntu,conforme mostrado em a na Figura 4. Figura 4 – Instalação por meio da Central de Programas do Ubuntu Depois, digite a palavra wireshark no campo indicado com a letra b na Figura 5, para mostrar os programas com esse nome que estão disponíveis no repositório Linux. Figura 5 – Localização do programa Wireshark para ser instalado Clique então sobre o programa Wireshark que será mostrado, e depois clique em instalar, como mostrado em c na Figura 6. Figura 6 – Inicializando a instalação do Wireshark Utilizando o Wireshark Após a instalação do wireshark, vamos agora executá-lo e aprender a utilizá-lo. Lembramos que a execução do Wireshark deverá ser como superusuário (root), de modo que você deve digitar o comando sudo su antes de executar o wireshark. Para iniciar o programa usamos o comando wireshark. Porém, desta forma o programa ficará atrelado ao Terminal, ou seja, se o terminal for encerrado o programa também o será. Outra forma de executar o programa é acrescentar o caractere & após seu nome, fazendo com que o programa execute em background (segundo plano), liberando, assim, o terminal para que você possa utilizá-lo. O comando ficaria assim: wireshark & , como mostrado na Figura 7. Figura 7 – Execução do Wireshark Ao executar o comando mostrado na Figura 7, o wireshark irá iniciar e teremos a tela mostrada na Figura 8. A chave apontada pela letra a nessa Figura mostra as interfaces presentes no computador que podem ser usadas para capturar os pacotes. Basta clicar sobre o nome da interface para que a captura de pacotes seja iniciada. Saiba que os nomes das interfaces podem mudar de computador para computador, pois elas dependem do hardware presente em sua máquina e como o sistema operacional o chama. A interface deve ser a usada para ter acesso à rede. No Linux o nome das placas de rede Ethernet são eth0, eth1, e assim sucessivamente, e as placas de rede sem fio (wireless) são chamadas de wlan0, wlan1, e assim sucessivamente. Vale lembrar que você pode escolher capturar os pacotes em modo promíscuo (promiscuous mode) ou não promíscuo. No modo promíscuo, a interface aceitará todos os pacotes da rede que chegarem nela, mesmo os que não são destinados à mesma. O modo desejado é informado no filtro, que será explicado posteriormente. Figura 8 – Tela inicial do Wireshark 1. Para que serve colocar o caractere & após o nome do programa Wireshark quando vamos executar esse programa através do terminal? 2. O que significa modo promíscuo? Menus Agora que você já sabe instalar, executar e escolher a interface de rede de onde capturar os pacotes, vamos mostrar em detalhes as opções disponíveis no menu principal do Wireshark, que é mostrado na Figura 9. Clicar em qualquer uma das palavras desse menu irá levar a um outro menu mais específico, que é descrito a seguir. Figura 9 – Menu do Wireshark File (Arquivo): É possível salvar as informações capturadas em um arquivo para que possam ser analisadas posteriormente. Este menu contém itens que possibilitam salvar as informações em um arquivo, abrir um arquivo salvo anteriormente, imprimir, exportar (salvar em um formato padrão, como txt). Além disso, existe a opção sair, que fecha o programa. Edit (Editar): Este menu contém itens que possibilitam encontrar um pacote específico dentre todos os pacotes capturados. Para isso aplicamos um filtro, que consiste em especificar alguma informação sobre o pacote desejado. Após aplicar o filtro apenas os pacotes que “casam” com ele são exibidos. Outra opção muito importante deste menu é Preferências, que permite configurar várias coisas sobre a aparência e o modo de funcionamento do programa. View (Visualizar): Este menu contém itens que controlam a exibição dos dados capturados, como por exemplo a coloração de pacotes, ampliação da fonte, exibição do pacote em uma janela separada, entre outros. Go (Ir): Este menu contém itens que permitem ir para um pacote específico. Capture (Captura): Este menu contém itens que lhe permitem iniciar e parar a captura de pacotes, além de definir filtros a serem aplicados na captura. Enquanto os filtros utilizados no menu Edit (Submenu Find Packet – Localizar pacotes)controlam os pacotes que são exibidos na tela, os filtros especificados no submenu Options deste menu controlam os pacotes que serão capturados. O submenu “Capture Filters” (Filtros de captura) deste menu permite que você crie novos filtros. Na sessão “Filtros” desta aula daremos mais detalhes sobre eles. Analyze (Analisar): Este menu contém itens que permitem manipular filtros de tela, ativar ou desativar a dissecção de protocolos. Você não precisa se preocupar muito com esse menu porque normalmente ele não é muito utilizado. Statistic (Estatística): Este menu contém itens que exibem janelas com estatísticas diversas, incluindo um resumo dos pacotes que foram capturados, hierarquia de protocolos e muito mais. Telephony (Telefonia): Este menu contém itens que exibem várias estatísticas relacionadas com janelas de telefonia, incluindo uma análise de mídia, diagramas de fluxo e muito mais. Você não precisa se preocupar muito com esse menu porque normalmente ele não é muito utilizado. Tools (Ferramentas): Este menu contém várias ferramentas disponíveis no Wireshark, como a criação de Regras de ACL do Firewall. Help (Ajuda): Este menu contém itens para ajudar o usuário, por exemplo, no acesso a uma lista dos protocolos suportados, páginas de manual ou o acesso online às páginas relacionadas. Talvez depois de olhar tantas opções de menu você esteja achando que utilizar o Wireshark é muito complicado. Não se preocupe, com o tempo você verá que na prática a maioria dessas opções não é usada frequentemente. Muitas vezes você apenas clica no nome da interface para iniciar a captura e clica nos pacotes que deseje ver mais detalhes. Realmente a utilização do Wireshark é bem simples! Olhe a descrição dos botões apresentada a seguir que você já verá um número bem menor de opções, mas que fornecem a maioria das operações que você vai precisar. 1. Abra o Wireshark como administrador ou superusuário. 2. Escolha a interface de rede que está utilizando para comunicar. Ex.: Eth0. 3. Tente identificar os pacotes que são capturados. Ex: ICMP causado pelo ping. Botões de atalho Algumas das operações discutidas acima, na Figura 9, podem ser executadas a partir de um atalho, como mostraremos agora. Figura 10 – Aba de botões de atalho : Captura de interfaces. : Mostra a opção de captura. : Inicia a captura. : Para a captura. : Reinicia a captura. Listagem dos pacotes capturados A parte mais importante que você precisa saber é analisar os pacotes. Portanto, vamos agora começar a ver como o Wireshark mostra os pacotes para você. Nessa seção você verá como o Wireshark lhe apresenta a lista dos pacotes capturados, e na próxima seção aprenderá a ver os detalhes de cada pacote. A listagem dos pacotes capturados é mostrada na Figura 11. Essa tela irá aparecer após você iniciar a captura dos pacotes, que pode ser feita, como já dissemos, clicando no nome da interface, selecionando uma opção do menu ou clicando em um botão. Figura 11 – Painel Lista de Pacotes A seguir discutiremos o que significam os campos identificados com as letras de “a” até “f” na Figura 11. a No. : Número do pacote na lista dos pacotes capturados. Como você pode perceber, esse número é sequencial, e é incrementado cada vez que um pacote é capturado. b Time: Tempo de timestamp do pacote. Informa o instante decorrido desde o início da captura até o momento em que o referido pacote foi capturado. É útil, por exemplo, para você analisar o tempo decorrido entre dois pacotes quaisquer, como o tempo decorrido entre o envio de um pacote que solicitou uma página web a um servidor e o pacote de resposta, contendo a página, enviado pelo servidor. c Source: Endereço IP de origem, ou seja, qual máquina transmitiu o pacote. d Destination: Endereço de destino, ou seja, para qual máquina o pacote deve ser entregue. e Protocol: Protocolo usado na transferência do pacote. f Info: Informações adicionais sobre o conteúdo do pacote. As informações que são mostradas dependem do tipo de protocolo sendo utilizado (informado no item anterior). 1. O que significa o campo “Source” na Figura 11? 2. O que significa o campo “Destination” na Figura 11? Analisando um pacote detalhadamente A listagem mostrada na Figura 11 é importante para lhe dar uma visão mais geral da comunicação, pois ela permite identificar quais pacotes foram transmitidos e em que ordem. Mas muitas vezes é necessário analisar detalhes de alguns pacotes. Na Figura 11, mostramos apenas uma parte da tela do Wireshark que aparece após a captura dos pacotes. Para mostrarmos a tela completa, onde podemos ver detalhes de cada pacote, vamos realizar um ping entre duas máquinas e capturar os pacotes. Assuma uma rede composta pelas três máquinas mostradas na Figura 12. Figura 12 – Três máquinas em uma rede Imagine que a máquina B realizou um ping para a máquina A, conforme mostrado na Figura 13. O ping usa as mensagens “Echo Request” e “Echo Response” do protocolo ICMP. Desse modo, a máquina que realiza ping envia para a outra a mensagem “Echo Request”, e a máquina que recebe esta mensagem envia de volta a mensagem “Echo Response”. Figura 13 – Máquina B realizou um ping para a máquina A O ping é útil para verificarmos se conseguimos nos comunicar com uma determinada máquina e para termos uma ideia do tempo em que a comunicação demora. Por isso, em uma saída típica do comando ping, como a que pode ser vista na Figura 13, ele informa se recebeu o “Echo Reply” para cada pacote transmitido (icmp_seq=), e quanto tempo depois do envio do “Echo Request” essa resposta foi recebida (time=). Desse modo, vemos que para os quatro primeiros pacotes transmitidos, as respostas foram recebidas. Veja que o tempo em que cada resposta (Echo Reply) chegou é diferente, pois ele depende do tráfego presente na rede durante aquela comunicação. Por padrão, o ping envia pacotes contendo 64 bytes, mas esse valor pode ser alterado. Quando estiver apenas interessado em verificar se consegue se comunicar com outra máquina, 64 bytes está bom. Mas quando estiver interessado em analisar o tempo em que os pacotes demoram para serem transmitidos, é bom aumentar o tamanho dos pacotes para que eles estejam mais próximos do tamanho real utilizado pela maioria das aplicações. Um bom valor para utilizar seria, por exemplo, 1400 bytes. Basta acrescentar “-s tam” ao ping, onde “Tam” é o tamanho do pacote em bytes. Ex. ping –s 1400 10.1.1.1 Após essa pequena “pausa” para falarmos do ping, vamos agora voltar ao Wireshark e a questão de como podemos ver os detalhes de cada pacote. Lembrando que cada pacote ICMP é enviado dentro de um pacote IP, veja na Figura 14 a tela completa do Wireshark após a captura dos pacotes de ping realizada de B para A. Figura 14 – Pacotes capturados de um ping de B para A com o Wireshark Na Figura 14 você pode ver que a tela onde os pacotes capturados são exibidos é formada por três partes, a, b e c. Em a vemos a mesma parte da tela do Wireshark que foi mostrada na Figura 11, sendo que agora reduzimos seu tamanho para vermos também as informações mostradas em b e c. Em a podemos ver que o primeiro pacote capturado foi um pacote ICMP (campo Protocol) do tipo Echo Request (campoInfo) enviado pela máquina com IP 10.1.1.2 (campo source) para a máquina 10.1.1.1 (campo destination). A segunda linha em a mostra que 10.1.1.1 enviou a resposta quando haviam passado 0.000067 segundos (campo Time) desde que a captura de pacotes tinha sido iniciada. A parte identificada por b mostra informações individuais do pacote, separando essas informações de acordo com os cabeçalhos de cada protocolo existente no pacote. Cada linha mostra informações sobre um protocolo. Desse modo, a segunda linha de b mostra informações sobre o quadro Ethernet, a terceira linha sobre a o protocolo IP e a quarta linha sobre o protocolo ICMP. A primeira linha mostra informações relacionadas principalmente ao tempo em que o pacote foi capturado. Em c o conteúdo do pacote é mostrado exatamente como ele é recebido, ou seja, como uma sequência de bytes. Essas informações são utilizadas para realizar análises mais profundas nos protocolos. Normalmente você vai analisar apenas as informações mostradas em a e b. As informações mostradas em b e c são referentes ao pacote que estiver selecionado em a. Para selecionar um pacote em a basta clicar nele uma vez. Na Figura 14 o pacote selecionado em a é o de número 2. Se você clicar duas vezes sobre um pacote em a as informações contidas em b e c também serão mostradas, só que em uma outra janela. Podemos expandir as informações mostradas em b para cada protocolo, ou seja, ver as informações de cada protocolo em detalhes. Nas próximas quatro figuras expandimos um item de b por vez. Todas as informações mostradas são referentes ao pacote de número 2, que é um pacote ICMP de Echo Reply enviado de 10.1.1.1 para 10.1.1.2. Na Figura 15 expandimos a primeira linha em b, de modo que você pode ver que os detalhes mostrados são referentes principalmente à hora em que o pacote foi capturado, seu tamanho e quais protocolos estão contidos no pacote. Figura 15 – Informações básicas sobre o pacote capturado Na Figura 16 expandimos a segunda linha em b, que mostra informações sobre a camada de enlace utilizada para transmitir o quadro. Você pode ver que se tratava de um quadro Ethernet. Desse modo, são mostrados os valores contidos nos campos endereço de destino (Destination) e endereço de origem (Source) do quadro, além do valor do campo de tipo (Type). Pelos valores mostrados você vê que o quadro foi enviado da máquina cujo endereço Ethernet da placa de rede é AA:AA:AA:00:00:01 para a máquina com endereço Ethernet AA:AA:AA:00:00:02. Se você olhar a Figura 12, verá que esses são os endereços das máquinas A e B, respectivamente. Ou seja, da máquina que recebeu o ping para a que o enviou. O valor 0x0800 contido no campo de tipo (Type) mostra que o conteúdo do quadro Ethernet é um pacote IP. 0x0800 é o valor em hexadecimal do código do protocolo IP. Figura 16 – Informações da camada de enlace Ao expandimos a terceira linha em b, vemos as informações referentes ao cabeçalho IP, conforme mostrado na Figura 17. Não vamos descrever todos os campos, mas você pode observar que o pacote foi enviado a partir do endereço IP (Source) 10.1.1.1 para a máquina com o endereço 10.1.1.2. Além disso, você pode observar que o campo Protocol contém o código do protocolo ICMP, que é o número 0x01 (em hexadecimal). Lembre-se que este campo indica qual é o protocolo contido na parte de dados do pacote IP! Figura 17 – Informações do protocolo IP Finalmente, ao expandirmos a quarta linha de b vemos as informações referentes ao cabeçalho do protocolo ICMP, conforme mostrado na Figura 18. Os campos Type e Code, ambos com o valor zero, significam que se trata de uma pacote “Echo Reply”. O valor 1 no campo “Sequence number” (Número de sequência) mostra que se trata de uma resposta relativa ao primeiro pacote enviado pela máquina 10.1.1.2. Figura 18 – Informações do protocolo ICMP Utilizando filtros Como explicamos anteriormente, o Wireshark permite que você utilize filtros para selecionar quais pacotes serão capturados, ou para selecionar entre os pacotes já capturados quais serão exibidos na tela. Para o primeiro caso utilize o menu “Edit->Find Packet”, e depois clique no botão “Filter”. Para o segundo caso clique no menu “Capture->Options” e depois clique no botão “Filter”. O Wireshark já vem com uma série de filtros, e você ainda pode criar seus próprios filtros através do menu “Capture-> Capture Filters”. Um filtro nada mais é do que uma regra contendo nomes de campos dos protocolos contidos nos pacotes e os valores que cada campo deve conter. Se um pacote contém o valor especificado no filtro para o referido campo ele “casa” com o filtro. Um exemplo de filtro simples pode ser que o endereço IP do pacote seja X.X.X.X (substituindo X.X.X.X pelo endereço desejado). Outro filtro simples pode ser que o protocolo seja TCP e a porta seja 80. A tela onde se escolhe um filtro para aplicar ou se permite a criação de um novo filtro é a mesma, e está mostrada na Figura 19. Figura 19 – Filtros no Wireshark Veja ainda na Figura 19 que, ao clicarmos sobre um filtro na parte inferior da tela, aparece o texto (string) que equivale a este filtro. Com o tempo você pode começar a aprender esta “linguagem” e preferir escrever as regras do filtro ao invés de selecioná-las na tela mostrada na Figura 19. Conforme mostrado na Figura 20, existe um campo na interface do Wireshark que lhe permite escrever os filtros usados para selecionar os pacotes exibidos. No exemplo, o filtro faz com que sejam exibidos apenas os pacotes do protocolo ARP. Você estudará esse protocolo na próxima aula. Figura 20 – Especificando um filtro através de texto Recursos disponíveis por plataforma Saiba que nem todas as interfaces (Bluetooth, Ethernet, USB, WLAN, Loopback etc.) são suportadas nas versões do Wireshark para todos os sistemas operacionais. Verifique se a interface que pretende utilizar é suportada no seu sistema operacional no link . Adiantamos que Ethernet e WLAN são suportadas no Windows, Linux e Mac. Tcpdump Uma das ferramentas de captura de pacotes mais tradicionais no Linux é o tcpdump [4]. Apesar de não possuir uma interface gráfica, ele ainda é até hoje muito utilizado. Lembra-se da linguagem de filtros do Wireshark? Pois é, digamos que no tcpdump essa é a única forma de definir os filtros. Caso esteja pensando por que você iria deixar de usar uma ferramenta que possui uma ótima interface gráfica para usar uma em modo texto, temos duas respostas. A primeira é agilidade. É bem mais rápido digitar um comando do que abrir um programa gráfico e utilizar seus menus. A segunda é que muitas vezes você precisa capturar pacotes em servidores que não têm uma interface gráfica instalada. Mas não se preocupe. Normalmente nesses casos os filtros que você vai precisar usar são bem simples. Você normalmente vai estar mais interessado em verificar se determinados pacotes chegaram até a máquina onde você está executando a ferramenta do que propriamente em analisar detalhadamente os campos do pacote. Nesses casos, a interface gráfica realmente não faz tanta diferença. Vamos capturar os mesmos pacotes do exemplo mostrado na Figura 13, onde a máquina B, que possui o endereço IP 10.1.1.2, enviou um ping para a máquina A, que possui o endereço IP 10.1.1.1. A Figura 21 mostra o comando e a saída do tcpdump. Figura 21 – Pacotes capturados com tcpdump Veja que a sintaxe é bem simples. O “-i eth0” é para indicar de qual placa de rede os pacotes devem ser capturados. Caso não fosse informado, o padrão é capturar da eth0. Portanto, nesse caso bastava termos digitado “tcpdump”. Além disso, você pode observar que as informações exibidas são praticamente as mesmas do Wireshark (só que mais resumidas), com a diferença que não aparecem os nomes dos campos. A Tabela 1 mostra outros exemplos do uso do tcpdump. Comando Significado tcpdump src 10.1.1.1 and icmp Pacotes ICMP cujo IP de origem seja 10.1.1.1. Tcpdump dst 10.1.1.2 and tcp and dst port 80 Pacotes cujo endereço IP de destino seja 10.1.1.2, o protocolo de transporte seja TCP e a porta de destino seja 80. Tcpdump host 10.1.1.1 Pacotes cujo endereço IP de origem ou o de destino seja 10.1.1.1. Tabela 1 – Exemplos do uso do tcpdump Resumo Nesta aula você aprendeu um novo tipo de ferramenta, que são as ferramentas de captura de pacotes. Estudou qual a finalidade delas e aprendeu a utilizar dois programas diferentes: o Wireshark e o tcpdump. Aprendeu que essas ferramentas mostram as informações dos cabeçalhos de cada protocolo existente no pacote e possuem uma linguagem de filtro para se especificar quais pacotes devem ser capturados. Também analisou em detalhes como o programa ping, que utiliza o protocolo ICMP, funciona. 1. Através de qual menu é possível especificar um filtro para selecionar entre todos os pacotes capturados quais devem ser mostrados na tela? 2. Qual a expressão de texto para um filtro que deve capturar apenas pacotes IP transmitidos ou recebidos do IP 192.168.1.2 (Dica: olhe os filtros que já existem)? 3. O que significa o campo “Time” na lista de pacotes capturados? 4. É possível utilizar o Wireshark para capturar pacotes transmitidos entre quaisquer duas máquinas da sua rede sem executá-lo como root (administrador)? 5. Acesse o Laboratório1 desta aula (Aula 3) no Virtualbox. Sabendo que essa rede utiliza um hub, realize um ping da máquina A para a máquina B, e tente capturar os pacotes ICMP transmitidos na máquina C. Isso será possível? Referencias KUROSE, James. Redes de computadores e a internet: uma abordagem Top-down. 4. ed. São Paulo: Perason Education, 2010. p.572-573. TCPDUMP. Disponível em: . Acesso em: 23 jul. 2010. WIKIPÉDIA. Promiscuous mode. Disponível em: . Acesso em: 22 jul. 2010. WIRESHARK. Disponível em: . Acesso em: 21 jul. 2010. Esta aula é a última das três aulas desta disciplina que são dedicadas a lhe ensinar a trabalhar com ferramentas que além de serem muito utilizadas pelas pessoas que administram redes de computadores, são utilizadas nesta disciplina como forma de lhe proporcionar a prática dos conteúdos ministrados. Nesta aula estudaremos um tipo de ferramenta que consegue capturar pacotes transmitidos em uma rede. Esse tipo de programa é muito útil para análise da rede e detecção de problemas. Veremos um programa que trabalha em modo texto, o Tcpdump,e outro que utiliza uma interface gráfica, o Wireshark. Ao final desta aula você será capaz de: • Utilizar a ferramenta Wireshark para capturar pacotes na rede. • Utilizar a ferramenta Tcpdump para capturar pacotes na rede. • Entender a forma como os pacotes são exibidos, de modo que possa interpretar as informações mostradas nos pacotes capturados. • Identificar em que máquina da rede executar a ferramenta de captura de pacotes. Captura de pacotes Como você estudou na disciplina de Sistemas de Conectividade, quando se deseja transmitir uma mensagem pela rede, cada camada TCP/IP na máquina de origem acrescenta um cabeçalho a essa informação. Na máquina de destino as camadas equivalentes retiram esses cabeçalhos. Você viu também que o conjunto cabeçalho + mensagem de cada camada recebe um nome. Na camada de enlace o chamamos de quadro, na camada de rede de pacote, na camada de transporte de segmento e na camada de aplicação de mensagem. Nesta aula você estudará uma ferramenta que permite a captura dos quadros transmitidos e recebidos por uma placa de rede. Como um quadro contém um pacote, que por sua vez pode conter um segmento, que por sua vez pode conter uma mensagem, esse tipo de ferramenta é genericamente chamada de ferramenta de captura de pacotes,e não captura de quadros. Desse modo, as ferramentas de captura de pacotes nos mostram informações de todas as camadas do modelo TCP/IP que estão presentes no pacote capturado. Suponha, por exemplo, que você capturou um pacote gerado pela aplicação ping em uma máquina com uma placa de rede Ethernet. Como a informação enviada pelo ping é uma mensagem do protocolo ICMP, que é transmitida dentro de um pacote IP, que por sua vez é transmitido dentro de um quadro Ethernet, a ferramenta de captura irá lhe mostrar informações sobre os cabeçalhos Ethernet, IP e ICMP. Como outro exemplo, suponha que você capturou informações transmitidas entre um browser e um servidor Web. Como a Web utiliza o protocolo HTTP (um protocolo de aplicação), que é transmitido dentro de segmentos TCP (protocolo de transporte), e cada segmento TCP é transmitido dentro de um pacote IP, que é transmitido dentro de um quadro Ethernet, a ferramenta de captura irá lhe mostrar informações sobre os cabeçalhos Ethernet, IP, TCP e HTTP. Ressaltamos que a ferramenta de captura de pacotes mostra as informações dos cabeçalhos, mas naturalmente ela mostra todos os dados contidos no pacote. Lembre-se também que embora o comportamento padrão de uma placa de rede seja descartar todos os quadros que não são destinados a ela, nem ao endereço de broadcast, pode-se configurar a placa para receber todos os quadros, o que caracteriza o modo promíscuo. Como já visto em Sistemas de Conectividade, isso se chama modo promíscuo, ou modo espião. Quando executamos uma ferramenta de captura de pacotes normalmente ela trabalha utilizando o modo espião. Evidentemente, para poder usar esse modo, é necessário que o usuário seja administrador da máquina. Você deve estar perguntando: “em que máquina eu devo executar a ferramenta de captura de pacotes?”. Pois bem, isso vai depender da finalidade para a qual você está executando a ferramenta. Mas lembre-se de que os dados devem passar por essa máquina para poderem ser capturados. Supondo que você pretende analisar uma comunicação entre as máquinas A e B, as alternativas são: i. Em um roteador no caminho entre A e B; ii. na máquina A; iii. na máquina B; iv. caso a rede onde A (ou B) se encontra use um hub, em uma outra máquina ligada no mesmo hub; v. caso A (ou B) esteja em uma rede sem fio, nessa mesma rede sem fio (desde que a rede não esteja usando criptografia). Wireshark O Wireshark é um dos programas de captura de pacotes mais utilizados. Ele mostra os dados e as informações dos cabeçalhos dos protocolos contidos nos pacotes capturados. Por ser um software Open source, o usuário pode modificar o código para melhor se adaptar às suas necessidades, assim como a documentação que está sob os termos da GNU (General Public License), ou seja, permite sua cópia, modificação e distribuição. Mas não se assuste! Dificilmente alguém precisa fazer isso, e você vai usar o programa sem precisar fazer modificação alguma nele! Existem versões do Wireshark para as plataformas Windows, Linux, MacOs, Solaris, FreeBSD, entre outros. Isso é muito bom, porque evita que você tenha que aprender a utilizar vários programas diferentes para realizar a mesma tarefa. O programa wireshark é muito usado por profissionais de redes, como: • Administradores, com o objetivo de solucionar problemas na rede; • Engenheiros de segurança, com o objetivo de verificar possíveis problemas na segurança; • Desenvolvedores, para encontrar possíveis bugs na implementação do protocolo; • Estudantes, como o objetivo de melhorar o entendimento acerca dos protocolos utilizados em redes de computadores. Vale salientar que o Wireshark também é muito usado pelos hackers para fins ilícitos, ou seja, para fins que contrariam a lei, pois com ele é possível encontrar falhas nas implementações dos protocolos, capturar senhas e obter outras informações. Instalação do Wireshark Nesta seção mostraremos como realizar a instalação do Wireshark, como usar o menu e alguns elementos básicos da sua interface gráfica. Utilizamos para esta aula a distribuição do GNU/Linux Ubuntu 9.10 e 10.04LTS, mas os passos são os mesmos para qualquer distribuição Linux baseada no Debian. Ilustraremos parte das imagens com letras do alfabeto latino (A-Z) para que você se situe melhor em relação à explicação. Várias distribuições Linux, como é o caso do Ubuntu, mantêm todos os seus programas disponíveis em diversos servidores na Internet, e possuem um sistema de instalação de programas (chamado Gerenciador de Pacotes) que faz o download dos arquivos a serem instalados diretamente destes servidores. Ou seja, não é necessário que você faça o download dos arquivos e depois execute o programa de instalação. Você pode utilizar o gerenciador de pacotes, que se chama “apt”, para procurar os programas disponíveis e mandar instalar o programa desejado. Ele será então baixado da Internet e instalado. O gerenciador pode ser executado através do modo texto com o comando apt-get install, ou através do modo gráfico usando o menu Aplicativos->Central de Programas Ubuntu.Veremos os dois modos a seguir. Temos duas observações importantes sobre o Gerenciador de Pacotes do Linux. A primeira é que você já o utilizou nas duas aulas anteriores para instalar os programas Virtualbox e Opensh-server. A segunda observação é que nesse contexto que estamos falando no momento (Gerenciador de Pacotes), o termo Pacotese refere a Programas (e não a um quadro de rede). Para a instalação em modo texto inicialmente você deve abrir um terminal. Isso pode ser feito usando o menu mostrado na Figura 1. Para a melhor visualização do processo, você pode assistir este vídeo: , que mostra os passos da instalação do Wireshark no Ubuntu. Figura 1 – Imagem de abertura do Terminal (gnome-terminal). Para a instalação e execução do Wireshark o usuário deverá ter permissão máxima, ou seja, ser super usuário (root). O comando Linux que transforma o usuário em superusuário é o sudo su, conforme mostrado na Figura 2. Lembramos que as operações usadas nesta aula são aceitas em qualquer distribuição baseada no debian. Figura 2 – Acesso como superusuário. Na Figura 3 é apresentado o comando usado (apt-get install) para instalar um aplicativo existente nos repositórios Linux através do gerenciador de pacotes (programas) apt. Caso deseje mais informações sobre o apt, você pode consultar a página . Figura 3 – Instalação a partir do apt-get Como dissemos, também é possível instalar o Wireshark a partir do modo gráfico, conforme mostrado nas Figuras 4, 5 e 6. Inicialmente abra a Central de Programas do Ubuntu, clicando em Aplicativos->Central de Programas Ubuntu,conforme mostrado em a na Figura 4. Figura 4 – Instalação por meio da Central de Programas do Ubuntu Depois, digite a palavra wireshark no campo indicado com a letra b na Figura 5, para mostrar os programas com esse nome que estão disponíveis no repositório Linux. Figura 5 – Localização do programa Wireshark para ser instalado Clique então sobre o programa Wireshark que será mostrado, e depois clique em instalar, como mostrado em c na Figura 6. Figura 6 – Inicializando a instalação do Wireshark Utilizando o Wireshark Após a instalação do wireshark, vamos agora executá-lo e aprender a utilizá-lo. Lembramos que a execução do Wireshark deverá ser como superusuário (root), de modo que você deve digitar o comando sudo su antes de executar o wireshark. Para iniciar o programa usamos o comando wireshark. Porém, desta forma o programa ficará atrelado ao Terminal, ou seja, se o terminal for encerrado o programa também o será. Outra forma de executar o programa é acrescentar o caractere & após seu nome, fazendo com que o programa execute em background (segundo plano), liberando, assim, o terminal para que você possa utilizá-lo. O comando ficaria assim: wireshark & , como mostrado na Figura 7. Figura 7 – Execução do Wireshark Ao executar o comando mostrado na Figura 7, o wireshark irá iniciar e teremos a tela mostrada na Figura 8. A chave apontada pela letra a nessa Figura mostra as interfaces presentes no computador que podem ser usadas para capturar os pacotes. Basta clicar sobre o nome da interface para que a captura de pacotes seja iniciada. Saiba que os nomes das interfaces podem mudar de computador para computador, pois elas dependem do hardware presente em sua máquina e como o sistema operacional o chama. A interface deve ser a usada para ter acesso à rede. No Linux o nome das placas de rede Ethernet são eth0, eth1, e assim sucessivamente, e as placas de rede sem fio (wireless) são chamadas de wlan0, wlan1, e assim sucessivamente. Vale lembrar que você pode escolher capturar os pacotes em modo promíscuo (promiscuous mode) ou não promíscuo. No modo promíscuo, a interface aceitará todos os pacotes da rede que chegarem nela, mesmo os que não são destinados à mesma. O modo desejado é informado no filtro, que será explicado posteriormente. Figura 8 – Tela inicial do Wireshark 1. Para que serve colocar o caractere & após o nome do programa Wireshark quando vamos executar esse programa através do terminal? 2. O que significa modo promíscuo? Menus Agora que você já sabe instalar, executar e escolher a interface de rede de onde capturar os pacotes, vamos mostrar em detalhes as opções disponíveis no menu principal do Wireshark, que é mostrado na Figura 9. Clicar em qualquer uma das palavras desse menu irá levar a um outro menu mais específico, que é descrito a seguir. Figura 9 – Menu do Wireshark File (Arquivo): É possível salvar as informações capturadas em um arquivo para que possam ser analisadas posteriormente. Este menu contém itens que possibilitam salvar as informações em um arquivo, abrir um arquivo salvo anteriormente, imprimir, exportar (salvar em um formato padrão, como txt). Além disso, existe a opção sair, que fecha o programa. Edit (Editar): Este menu contém itens que possibilitam encontrar um pacote específico dentre todos os pacotes capturados. Para isso aplicamos um filtro, que consiste em especificar alguma informação sobre o pacote desejado. Após aplicar o filtro apenas os pacotes que “casam” com ele são exibidos. Outra opção muito importante deste menu é Preferências, que permite configurar várias coisas sobre a aparência e o modo de funcionamento do programa. View (Visualizar): Este menu contém itens que controlam a exibição dos dados capturados, como por exemplo a coloração de pacotes, ampliação da fonte, exibição do pacote em uma janela separada, entre outros. Go (Ir): Este menu contém itens que permitem ir para um pacote específico. Capture (Captura): Este menu contém itens que lhe permitem iniciar e parar a captura de pacotes, além de definir filtros a serem aplicados na captura. Enquanto os filtros utilizados no menu Edit (Submenu Find Packet – Localizar pacotes)controlam os pacotes que são exibidos na tela, os filtros especificados no submenu Options deste menu controlam os pacotes que serão capturados. O submenu “Capture Filters” (Filtros de captura) deste menu permite que você crie novos filtros. Na sessão “Filtros” desta aula daremos mais detalhes sobre eles. Analyze (Analisar): Este menu contém itens que permitem manipular filtros de tela, ativar ou desativar a dissecção de protocolos. Você não precisa se preocupar muito com esse menu porque normalmente ele não é muito utilizado. Statistic (Estatística): Este menu contém itens que exibem janelas com estatísticas diversas, incluindo um resumo dos pacotes que foram capturados, hierarquia de protocolos e muito mais. Telephony (Telefonia): Este menu contém itens que exibem várias estatísticas relacionadas com janelas de telefonia, incluindo uma análise de mídia, diagramas de fluxo e muito mais. Você não precisa se preocupar muito com esse menu porque normalmente ele não é muito utilizado. Tools (Ferramentas): Este menu contém várias ferramentas disponíveis no Wireshark, como a criação de Regras de ACL do Firewall. Help (Ajuda): Este menu contém itens para ajudar o usuário, por exemplo, no acesso a uma lista dos protocolos suportados, páginas de manual ou o acesso online às páginas relacionadas. Talvez depois de olhar tantas opções de menu você esteja achando que utilizar o Wireshark é muito complicado. Não se preocupe, com o tempo você verá que na prática a maioria dessas opções não é usada frequentemente. Muitas vezes você apenas clica no nome da interface para iniciar a captura e clica nos pacotes que deseje ver mais detalhes. Realmente a utilização do Wireshark é bem simples! Olhe a descrição dos botões apresentada a seguir que você já verá um número bem menor de opções, mas que fornecem a maioria das operações que você vai precisar. 1. Abra o Wireshark como administrador ou superusuário. 2. Escolha a interface de rede que está utilizando para comunicar. Ex.: Eth0. 3. Tente identificar os pacotes que são capturados. Ex: ICMP causado pelo ping. Botões de atalho Algumas das operações discutidas acima, na Figura 9, podem ser executadas a partir de um atalho, como mostraremos agora. Figura 10 – Aba de botões de atalho : Captura de interfaces. : Mostra a opção de captura. : Inicia a captura. : Para a captura. : Reinicia a captura. Listagem dos pacotes capturados A parte mais importante que você precisa saber é analisar os pacotes. Portanto, vamos agora começar a ver como o Wireshark mostra os pacotes para você. Nessa seção você verá como o Wireshark lhe apresenta a lista dos pacotes capturados, e na próxima seção aprenderá a ver os detalhes de cada pacote. A listagem dos pacotes capturados é mostrada na Figura 11. Essa tela irá aparecer após você iniciar a captura dos pacotes, que pode ser feita, como já dissemos, clicando no nome da interface, selecionando uma opção do menu ou clicando em um botão. Figura 11 – Painel Lista de Pacotes A seguir discutiremos o que significam os campos identificados com as letras de “a” até “f” na Figura 11. a No. : Número do pacote na lista dos pacotes capturados. Como você pode perceber, esse número é sequencial, e é incrementado cada vez que um pacote é capturado. b Time: Tempo de timestamp do pacote. Informa o instante decorrido desde o início da captura até o momento em que o referido pacote foi capturado. É útil, por exemplo, para você analisar o tempo decorrido entre dois pacotes quaisquer, como o tempo decorrido entre o envio de um pacote que solicitou uma página web a um servidor e o pacote de resposta, contendo a página, enviado pelo servidor. c Source: Endereço IP de origem, ou seja, qual máquina transmitiu o pacote. d Destination: Endereço de destino, ou seja, para qual máquina o pacote deve ser entregue. e Protocol: Protocolo usado na transferência do pacote. f Info: Informações adicionais sobre o conteúdo do pacote. As informações que são mostradas dependem do tipo de protocolo sendo utilizado (informado no item anterior). 1. O que significa o campo “Source” na Figura 11? 2. O que significa o campo “Destination” na Figura 11? Analisando um pacote detalhadamente A listagem mostrada na Figura 11 é importante para lhe dar uma visão mais geral da comunicação, pois ela permite identificar quais pacotes foram transmitidos e em que ordem. Mas muitas vezes é necessário analisar detalhes de alguns pacotes. Na Figura 11, mostramos apenas uma parte da tela do Wireshark que aparece após a captura dos pacotes. Para mostrarmos a tela completa, onde podemos ver detalhes de cada pacote, vamos realizar um ping entre duas máquinas e capturar os pacotes. Assuma uma rede composta pelas três máquinas mostradas na Figura 12. Figura 12 – Três máquinas em uma rede Imagine que a máquina B realizou um ping para a máquina A, conforme mostrado na Figura 13. O ping usa as mensagens “Echo Request” e “Echo Response” do protocolo ICMP. Desse modo, a máquina que realiza ping envia para a outra a mensagem “Echo Request”, e a máquina que recebe esta mensagem envia de volta a mensagem “Echo Response”. Figura 13 – Máquina B realizou um ping para a máquina A O ping é útil para verificarmos se conseguimos nos comunicar com uma determinada máquina e para termos uma ideia do tempo em que a comunicação demora. Por isso, em uma saída típica do comando ping, como a que pode ser vista na Figura 13, ele informa se recebeu o “Echo Reply” para cada pacote transmitido (icmp_seq=), e quanto tempo depois do envio do “Echo Request” essa resposta foi recebida (time=). Desse modo, vemos que para os quatro primeiros pacotes transmitidos, as respostas foram recebidas. Veja que o tempo em que cada resposta (Echo Reply) chegou é diferente, pois ele depende do tráfego presente na rede durante aquela comunicação. Por padrão, o ping envia pacotes contendo 64 bytes, mas esse valor pode ser alterado. Quando estiver apenas interessado em verificar se consegue se comunicar com outra máquina, 64 bytes está bom. Mas quando estiver interessado em analisar o tempo em que os pacotes demoram para serem transmitidos, é bom aumentar o tamanho dos pacotes para que eles estejam mais próximos do tamanho real utilizado pela maioria das aplicações. Um bom valor para utilizar seria, por exemplo, 1400 bytes. Basta acrescentar “-s tam” ao ping, onde “Tam” é o tamanho do pacote em bytes. Ex. ping –s 1400 10.1.1.1 Após essa pequena “pausa” para falarmos do ping, vamos agora voltar ao Wireshark e a questão de como podemos ver os detalhes de cada pacote. Lembrando que cada pacote ICMP é enviado dentro de um pacote IP, veja na Figura 14 a tela completa do Wireshark após a captura dos pacotes de ping realizada de B para A. Figura 14 – Pacotes capturados de um ping de B para A com o Wireshark Na Figura 14 você pode ver que a tela onde os pacotes capturados são exibidos é formada por três partes, a, b e c. Em a vemos a mesma parte da tela do Wireshark que foi mostrada na Figura 11, sendo que agora reduzimos seu tamanho para vermos também as informações mostradas em b e c. Em a podemos ver que o primeiro pacote capturado foi um pacote ICMP (campo Protocol) do tipo Echo Request (campoInfo) enviado pela máquina com IP 10.1.1.2 (campo source) para a máquina 10.1.1.1 (campo destination). A segunda linha em a mostra que 10.1.1.1 enviou a resposta quando haviam passado 0.000067 segundos (campo Time) desde que a captura de pacotes tinha sido iniciada. A parte identificada por b mostra informações individuais do pacote, separando essas informações de acordo com os cabeçalhos de cada protocolo existente no pacote. Cada linha mostra informações sobre um protocolo. Desse modo, a segunda linha de b mostra informações sobre o quadro Ethernet, a terceira linha sobre a o protocolo IP e a quarta linha sobre o protocolo ICMP. A primeira linha mostra informações relacionadas principalmente ao tempo em que o pacote foi capturado. Em c o conteúdo do pacote é mostrado exatamente como ele é recebido, ou seja, como uma sequência de bytes. Essas informações são utilizadas para realizar análises mais profundas nos protocolos. Normalmente você vai analisar apenas as informações mostradas em a e b. As informações mostradas em b e c são referentes ao pacote que estiver selecionado em a. Para selecionar um pacote em a basta clicar nele uma vez. Na Figura 14 o pacote selecionado em a é o de número 2. Se você clicar duas vezes sobre um pacote em a as informações contidas em b e c também serão mostradas, só que em uma outra janela. Podemos expandir as informações mostradas em b para cada protocolo, ou seja, ver as informações de cada protocolo em detalhes. Nas próximas quatro figuras expandimos um item de b por vez. Todas as informações mostradas são referentes ao pacote de número 2, que é um pacote ICMP de Echo Reply enviado de 10.1.1.1 para 10.1.1.2. Na Figura 15 expandimos a primeira linha em b, de modo que você pode ver que os detalhes mostrados são referentes principalmente à hora em que o pacote foi capturado, seu tamanho e quais protocolos estão contidos no pacote. Figura 15 – Informações básicas sobre o pacote capturado Na Figura 16 expandimos a segunda linha em b, que mostra informações sobre a camada de enlace utilizada para transmitir o quadro. Você pode ver que se tratava de um quadro Ethernet. Desse modo, são mostrados os valores contidos nos campos endereço de destino (Destination) e endereço de origem (Source) do quadro, além do valor do campo de tipo (Type). Pelos valores mostrados você vê que o quadro foi enviado da máquina cujo endereço Ethernet da placa de rede é AA:AA:AA:00:00:01 para a máquina com endereço Ethernet AA:AA:AA:00:00:02. Se você olhar a Figura 12, verá que esses são os endereços das máquinas A e B, respectivamente. Ou seja, da máquina que recebeu o ping para a que o enviou. O valor 0x0800 contido no campo de tipo (Type) mostra que o conteúdo do quadro Ethernet é um pacote IP. 0x0800 é o valor em hexadecimal do código do protocolo IP. Figura 16 – Informações da camada de enlace Ao expandimos a terceira linha em b, vemos as informações referentes ao cabeçalho IP, conforme mostrado na Figura 17. Não vamos descrever todos os campos, mas você pode observar que o pacote foi enviado a partir do endereço IP (Source) 10.1.1.1 para a máquina com o endereço 10.1.1.2. Além disso, você pode observar que o campo Protocol contém o código do protocolo ICMP, que é o número 0x01 (em hexadecimal). Lembre-se que este campo indica qual é o protocolo contido na parte de dados do pacote IP! Figura 17 – Informações do protocolo IP Finalmente, ao expandirmos a quarta linha de b vemos as informações referentes ao cabeçalho do protocolo ICMP, conforme mostrado na Figura 18. Os campos Type e Code, ambos com o valor zero, significam que se trata de uma pacote “Echo Reply”. O valor 1 no campo “Sequence number” (Número de sequência) mostra que se trata de uma resposta relativa ao primeiro pacote enviado pela máquina 10.1.1.2. Figura 18 – Informações do protocolo ICMP Utilizando filtros Como explicamos anteriormente, o Wireshark permite que você utilize filtros para selecionar quais pacotes serão capturados, ou para selecionar entre os pacotes já capturados quais serão exibidos na tela. Para o primeiro caso utilize o menu “Edit->Find Packet”, e depois clique no botão “Filter”. Para o segundo caso clique no menu “Capture->Options” e depois clique no botão “Filter”. O Wireshark já vem com uma série de filtros, e você ainda pode criar seus próprios filtros através do menu “Capture-> Capture Filters”. Um filtro nada mais é do que uma regra contendo nomes de campos dos protocolos contidos nos pacotes e os valores que cada campo deve conter. Se um pacote contém o valor especificado no filtro para o referido campo ele “casa” com o filtro. Um exemplo de filtro simples pode ser que o endereço IP do pacote seja X.X.X.X (substituindo X.X.X.X pelo endereço desejado). Outro filtro simples pode ser que o protocolo seja TCP e a porta seja 80. A tela onde se escolhe um filtro para aplicar ou se permite a criação de um novo filtro é a mesma, e está mostrada na Figura 19. Figura 19 – Filtros no Wireshark Veja ainda na Figura 19 que, ao clicarmos sobre um filtro na parte inferior da tela, aparece o texto (string) que equivale a este filtro. Com o tempo você pode começar a aprender esta “linguagem” e preferir escrever as regras do filtro ao invés de selecioná-las na tela mostrada na Figura 19. Conforme mostrado na Figura 20, existe um campo na interface do Wireshark que lhe permite escrever os filtros usados para selecionar os pacotes exibidos. No exemplo, o filtro faz com que sejam exibidos apenas os pacotes do protocolo ARP. Você estudará esse protocolo na próxima aula. Figura 20 – Especificando um filtro através de texto Recursos disponíveis por plataforma Saiba que nem todas as interfaces (Bluetooth, Ethernet, USB, WLAN, Loopback etc.) são suportadas nas versões do Wireshark para todos os sistemas operacionais. Verifique se a interface que pretende utilizar é suportada no seu sistema operacional no link . Adiantamos que Ethernet e WLAN são suportadas no Windows, Linux e Mac. Tcpdump Uma das ferramentas de captura de pacotes mais tradicionais no Linux é o tcpdump [4]. Apesar de não possuir uma interface gráfica, ele ainda é até hoje muito utilizado. Lembra-se da linguagem de filtros do Wireshark? Pois é, digamos que no tcpdump essa é a única forma de definir os filtros. Caso esteja pensando por que você iria deixar de usar uma ferramenta que possui uma ótima interface gráfica para usar uma em modo texto, temos duas respostas. A primeira é agilidade. É bem mais rápido digitar um comando do que abrir um programa gráfico e utilizar seus menus. A segunda é que muitas vezes você precisa capturar pacotes em servidores que não têm uma interface gráfica instalada. Mas não se preocupe. Normalmente nesses casos os filtros que você vai precisar usar são bem simples. Você normalmente vai estar mais interessado em verificar se determinados pacotes chegaram até a máquina onde você está executando a ferramenta do que propriamente em analisar detalhadamente os campos do pacote. Nesses casos, a interface gráfica realmente não faz tanta diferença. Vamos capturar os mesmos pacotes do exemplo mostrado na Figura 13, onde a máquina B, que possui o endereço IP 10.1.1.2, enviou um ping para a máquina A, que possui o endereço IP 10.1.1.1. A Figura 21 mostra o comando e a saída do tcpdump. Figura 21 – Pacotes capturados com tcpdump Veja que a sintaxe é bem simples. O “-i eth0” é para indicar de qual placa de rede os pacotes devem ser capturados. Caso não fosse informado, o padrão é capturar da eth0. Portanto, nesse caso bastava termos digitado “tcpdump”. Além disso, você pode observar que as informações exibidas são praticamente as mesmas do Wireshark (só que mais resumidas), com a diferença que não aparecem os nomes dos campos. A Tabela 1 mostra outros exemplos do uso do tcpdump. Comando Significado tcpdump src 10.1.1.1 and icmp Pacotes ICMP cujo IP de origem seja 10.1.1.1. Tcpdump dst 10.1.1.2 and tcp and dst port 80 Pacotes cujo endereço IP de destino seja 10.1.1.2, o protocolo de transporte seja TCP e a porta de destino seja 80. Tcpdump host 10.1.1.1 Pacotes cujo endereço IP de origem ou o de destino seja 10.1.1.1. Tabela 1 – Exemplos do uso do tcpdump Resumo Nesta aula você aprendeu um novo tipo de ferramenta, que são as ferramentas de captura de pacotes. Estudou qual a finalidade delas e aprendeu a utilizar dois programas diferentes: o Wireshark e o tcpdump. Aprendeu que essas ferramentas mostram as informações dos cabeçalhos de cada protocolo existente no pacote e possuem uma linguagem de filtro para se especificar quais pacotes devem ser capturados. Também analisou em detalhes como o programa ping, que utiliza o protocolo ICMP, funciona. 1. Através de qual menu é possível especificar um filtro para selecionar entre todos os pacotes capturados quais devem ser mostrados na tela? 2. Qual a expressão de texto para um filtro que deve capturar apenas pacotes IP transmitidos ou recebidos do IP 192.168.1.2 (Dica: olhe os filtros que já existem)? 3. O que significa o campo “Time” na lista de pacotes capturados? 4. É possível utilizar o Wireshark para capturar pacotes transmitidos entre quaisquer duas máquinas da sua rede sem executá-lo como root (administrador)? 5. Acesse o Laboratório1 desta aula (Aula 3) no Virtualbox. Sabendo que essa rede utiliza um hub, realize um ping da máquina A para a máquina B, e tente capturar os pacotes ICMP transmitidos na máquina C. Isso será possível? esta aula você aprenderá um protocolo que é de fundamental importância para que a pilha de protocolos TCP/IP possa ser utilizada com qualquer tecnologia de rede física, como Ethernet ou as redes sem fio 802.11, por exemplo. Verá que este protocolo se chama ARP (Address Resolution Protocol, ou seja, Protocolo de Resolução de Endereços), e tem como principal função descobrir o endereço MAC da máquina para qual um dado pacote IP deve ser entregue. Você aprenderá porque isso é necessário e como essa tarefa é realizada. • Aprender que o protocolo ARP é utilizado para traduzir endereços de rede para endereços de enlace, e que normalmente traduz um endereço IP para um endereço Ethernet. • Entender por que a tradução de endereços de rede para endereços de enlace é necessária. • Entender como a tradução de endereços de rede para endereços de enlace é realizada. • Utilizar uma ferramenta para monitorar o processo de tradução de endereços. Por que o protocolo ARP é necessário Você sabe que nas redes que utilizam o protocolo IP, como é o caso da Internet e da maioria das redes das empresas atualmente, os dados são transmitidos entre as máquinas em pacotes IP, e que cada pacote contém o endereço IP da máquina que está enviando o pacote e o endereço IP da máquina para a qual ele deve ser entregue. A Figura 1 mostra o pacote IP que é gerado quando uma máquina A quer enviar dados para uma máquina C. Naturalmente os demais campos do cabeçalho IP e os cabeçalhos das camadas de transporte e de aplicação foram omitidos. Figura 1 - Transmissão usando um pacote IP Acontece, como você também já sabe, que não existe nenhuma placa de rede que fale IP, que é um protocolo da camada de rede. As placas de rede implementam a camada de enlace, portanto, comunicam-se enviando quadros no formato definido pela sua tecnologia, como, por exemplo, Ethernet ou 802.11. Naturalmente, do mesmo modo que os pacotes IP contêm campos para os endereços IP, os quadros também contêm os endereços de enlace (chamados endereços MAC) da máquina que gerou o quadro e da máquina para a qual ele deve ser entregue. Lembre-se de que endereço MAC é o mesmo que endereço da placa de rede. O pacote IP é transmitido dentro da parte de dados do quadro utilizado pela placa de rede. Assim sendo, para que o pacote IP seja entregue à máquina desejada, precisamos colocar no campo de endereço MAC de destino do quadro o endereço MAC da máquina que possui o IP com quem queremos falar. O problema é que o usuário não sabe o endereço MAC da máquina com quem quer falar, ele conhece apenas o endereço IP dela! A Figura 2 mostra como o pacote IP da Figura 1 realmente precisaria ser transmitido. Vamos assumir que a rede utilizada é da tecnologia Ethernet. Figura 2 - Enviando um pacote IP dentro do Ethernet Veja que o campo Endereço Ethernet de origem é preenchido com o endereço da placa de rede da máquina local (de onde o pacote será enviado), mas o endereço Ethernet de destino não é conhecido. Portanto, é necessário que exista algum mecanismo automático para se descobrir o endereço Ethernet associado a um endereço IP. Esse mecanismo existe e é o ARP (Protocolo para Resolução de Endereços, do inglês Address Resolution Protocol). Embora o ARP seja um protocolo genérico que é capaz de traduzir endereços de qualquer protocolo de rede para endereços de qualquer protocolo de enlace, normalmente seu uso mais frequente é traduzir endereços IP para endereços Ethernet, ou endereços IP para endereços MAC 802.11 nas redes sem fio. Nos nossos exemplos iremos utilizar a tradução de endereços IP para Ethernet, mas os mesmos procedimentos explicados se aplicam para outros protocolos e tecnologias de enlace. Em nossos exemplos utilizamos endereços Ethernet que tenham algo em comum com o endereço IP da máquina, para que fique mais fácil de você lembrar. O último byte do endereço Ethernet, por exemplo, normalmente será igual ao último byte do endereço IP. Mas lembre-se de que, na realidade, os endereços Ethernet não mantêm nenhuma relação com o IP. Quando você compra uma placa de rede ela já tem seu endereço Ethernet! Para finalizar esta seção, veja na Figura 3 onde o protocolo ARP se situa na pilha de protocolos TCP/IP. Figura 3 - Posição do ARP na pilha de protocolos TCP/IP Funcionamento do ARP Vamos agora entender como o ARP consegue descobrir o endereço Ethernet associado a um endereço IP. Antes dessa explicação é importante que você se lembre que uma máquina pode enviar um quadro Ethernet para uma única outra máquina ou para todas as outras máquinas da rede. No primeiro caso, o endereço da placa de rede da máquina de destino é colocado no campo de endereço de destino do quadro, e este quadro é entregue apenas à máquina de destino. No seguindo caso, o campo endereço de destino do quadro é preenchido com o endereço especialFF:FF:FF:FF:FF:FF, chamado “Endereço de Broadcast”, fazendo com que o quadro seja entregue a todas as máquinas da rede. O ARP se aproveita dessa capacidade de enviar um quadro para todas as máquinas da rede para resolver o seu problema de forma muito simples. Ele apenas envia uma mensagem em broadcast (ou seja, para todas as máquinas da rede) dizendo: “Por favor, quem tem o endereço IP X.X.X.X, informe qual é seu endereço Ethernet”. Evidentemente, X.X.X.X é substituído pelo endereço IP para o qual ela quer descobrir o Ethernet. Como todas as máquinas recebem esta pergunta, a máquina que tiver o endereço IP X.X.X.X responde informando seu Ethernet. É importante observar que essa resposta não é enviada em broadcast! Ela é enviada diretamente para quem fez a pergunta. Isso é possível porque o endereço de quem fez a pergunta está no quadro Ethernet que o destino recebeu, no campo endereço de origem. A mensagem ARP é transmitida dentro da parte de dados do Ethernet, portanto, o campo tipo do quadro Ethernet contém o código do protocolo ARP, que é 0x0806 (em hexadecimal). Além disso, naturalmente, o pacote IP só será transmitido após o envio do quadro ARP e do recebimento da resposta. Ainda sobre o valor do campo tipo, nos nossos exemplos, quando mostrarmos um quadro utilizaremos a palavra “ARP” ao invés do código numérico, pois fica mais fácil de entender. Mas lembre-se que de fato é utilizado o valor numérico! A Figura 4 mostra como ocorre a comunicação mostrada na Figura 2, onde a máquina 10.1.1.1 quer transmitir um pacote IP para a máquina 10.1.1.3, e, para isso, utiliza o ARP para descobrir seu endereço Ethernet. Na Figura 4(a) a máquina “A” envia uma mensagem ARP em broadcast perguntando quem tem o IP 10.1.1.3. Veja que o campo tipo do quadro Ethernet contém a identificação do protocolo ARP, e que todas as outras máquinas da rede, no caso, B e C, recebem o quadro, uma vez que ele foi enviado para o endereço de broadcast (FF:FF:FF:FF:FF:FF). Figura 4(a) - Envio de requisição ARP para descobrir o endereço Ethernet Na Figura 4(b) vemos que apenas a máquina C responde à mensagem ARP, pois ela possui o endereço IP para o qual se deseja descobrir o endereço Ethernet. As outras máquinas, no caso a máquina B, descartam a mensagem ARP. Você pode observar ainda que apenas a máquina A recebe esta resposta, pois o quadro é enviado diretamente para o seu endereço (AA:AA:AA:01:01:01). Figura 4(b) - Envio de resposta ARP informando o endereço Ethernet Finalmente, na Figura 4(c), vemos que após receber a resposta ARP informando qual é o endereço Ethernet (AA:AA:AA:00:00:03) associado ao endereço IP de C (10.1.1.3), o pacote IP que precisa ser enviado para ela é colocado dentro de um quadro Ethernet e enviado. Figura 4(c) - Envio do pacote IP após utilização do ARP 1. Quais os endereços colocados nos camposendereço de origem e endereço de destino de um quadro Ethernet contendo uma mensagem de requisição ARP? 2. Qual o valor do campo de tipo de um quadro Ethernet contendo uma mensagem de resposta ARP? Formato das Mensagens ARP Na seção anterior nós descrevemos as mensagem ARPs contendo apenas dois campos: um para o endereço IP e outro para o endereço Ethernet. Na verdade, como o ARP pode funcionar com vários protocolos de camada de rede e vários protocolos de camada de enlace, as mensagens ARP possuem campos para identificar quais protocolos são utilizados. Antes de vermos esses campos, saiba que existem apenas dois tipos de mensagem, uma para perguntar pelo endereço Ethernet associado a um IP, chamada ARP Request, e outra, chamada ARP Reply, para responder essa requisição, onde a máquina informa o Ethernet associado ao IP. Além disso, as duas possuem exatamente os mesmos campos, sendo diferenciadas pelo valor contido em um dos campos do quadro, que indica o tipo de operação. A Tabela 1 mostra os campos de uma mensagem ARP e o significado de cada um. Nome do Campo Tamanho (em bytes) Descricao Tipo Hardware 2 Código da tecnologia da camada de enlace (Ex: Ethernet. 0x0001 - hexadecimal) Tipo Protocolo 2 Código do Protocolo de Rede (Ex: IP. 0x0800 - hexadecimal) Tam End. Hw 1 Tamanho do endereço de enlace (em bytes). Se for Ethernet, contém o valor 6. Tam End. Proto 1 Tamanho do endereço de Rede (em bytes). Se for IP, contém o valor 4. Operação 2 Código do tipo de mensagem: 0x0001 para o ARP Request, 0x0002 para o ARP Reply. Endereço Hardware Origem - Endereço de Enlace da origem. O tamanho deste campo é o valor do campo “Tam End. Hw” Endereço Protocolo Origem - Endereço de Rede da origem. O tamanho deste campo é o valor do campo “Tam End. Proto” Endereço Hardware destino - Endereço de Enlace da origem. Se for Ethernet, o tamanho é 6 bytes. Endereço Protocolo Destino - Endereço de Enlace da origem. Se for IP, o tamanho é 4 bytes. Tabela 1 - Campos do quadro ARP Para entendermos melhor como os campos de endereço são utilizados, vamos ver os valores que realmente são enviados nos campos de endereço das mensagens ARP trocadas entre as máquinas A e C do exemplo mostrado na sessão anterior – Figuras 3(a) e 3(b). As duas mensagens são enviadas em quadros Ethernet contendo no campo de tipo o código do ARP (0x0806 – Hexadecimal), sendo que o ARP Resquest é enviado para o endereço de broadcast Ethernet, enquanto o ARP reply é enviado diretamente para o endereço Ethernet de A. Arp Request Enviada por A ARP Reply Enviada por C Endereço Hardware Origem AA:AA:AA:00:00:01 AA:AA:AA:00:00:03 Endereço Protocolo Origem 10.1.1.1 10.1.1.3 Endereço Hardware destino 00:00:00:00:00:00:00 AA:AA:AA:00:00:01 Endereço Protocolo Destino 10.1.1.3 10.1.1.1 Tabela 2 - Valores dos campos de endereço para a comunicação entre A e C Observe que no ARP Resquest o endereço IP de origem é preenchido com zeros, pois é exatamente este endereço que desejamos descobrir. 1. Em um quadro que contém uma mensagem ARP, qual campo devemos olhar para saber se é uma mensagem ARP Request ou ARP Reply? Cache ARP Embora o modelo descrito na seção anterior resolva o problema da resolução de endereços, ele apresenta dois efeitos colaterais negativos. O primeiro ocorre pelo fato de enviar mensagens pela rede, pois isso faz com que haja um tempo (atraso) até que a resposta do ARP chegue, causando um atraso no envio do pacote que se deseja transmitir. O segundo efeito negativo se deve ao fato das mensagens serem transmitidas a todas as máquinas na rede. Isso aumenta o volume de tráfego nessa rede, podendo levar a uma redução no seu desempenho. O ideal é que a maior parte da capacidade de transmissão de uma rede seja destinada aos dados das aplicações dos usuários. Quanto mais informações para outras finalidades a rede transmitir, como é o caso do ARP, menos banda de rede sobre para os usuários. Para minimizar os dois problemas citados, o ARP utiliza uma tabela (chamada de cache arp) onde ele guarda as informações que já descobriu. As duas principais colunas desta tabela são endereço IP e endereço MAC. Desse modo, cada linha desta tabela contém um endereço IP e o seu endereço MAC (tipicamente um endereço Ethernet ou 802.11). Usando a tabela, o funcionamento do ARP fica da seguinte maneira: quando uma máquina precisa enviar um pacote para um determinada endereço IP, ela primeiro verifica se este endereço IP já se encontra na tabela ARP. Se ele existir lá, basta obter seu endereço Ethernet a partir da tabela. Neste caso, nenhuma mensagem é enviada. Caso o endereço IP não exista na tabela, aí sim, é enviada a mensagem ARP (ARP request) pela rede. Quando a resposta do ARP chegar (ARP Reply), além de ser utilizada para o envio do pacote IP, é inserida uma entrada na tabela cache associando o IP e o Ethernet. A Figura 5 mostra um fluxograma que descreve o funcionamento do ARP. Figura 5 - Fluxograma do ARP usando a tabela cache Veja que o fluxograma mostra que pode existir uma situação onde enviamos uma requisição ARP e não recebemos resposta. Isso é normal, e acontecerá sempre que tentarmos acessar um endereço IP para o qual não existe nenhuma máquina na nossa rede. Isso acontecerá, por exemplo, quando a máquina que você tenta acessar está desligada. Nesses casos, embora não tenha sido mostrado no fluxograma, também será inserida uma entrada na tabela cache para o endereço IP, mas não haverá um endereço Ethernet associado. Essa entrada servirá apenas para vermos que já tentamos nos comunicar com aquele IP e não conseguimos resposta. Portanto, sempre que alguém precisar se comunicar com esse IP, uma nova requisição ARP será enviada pela rede! É importante observar que as entradas cadastradas na tabela cache não podem ficar lá para sempre. Para entender por que, suponha, por exemplo, que descobrimos que o endereço IP 200.1.1.1 está associado ao endereço Ethernet AA:AA:AA:01:01:01 e inserimos esta entrada na tabela cache. Descobrimos também que o endereço IP 200.2.2.2 está associado ao endereço Ethernet AA:AA:AA:02:02:02 e inserimos esta entrada na tabela cache. Podem acontecer as seguintes duas situações que levariam a um erro: • depois de algum tempo, as máquinas podem ter sido reinicializadas e terem obtido outros endereços IP. Suponha que após a reinicialização a máquina com endereço Ethernet AA:AA:AA01:01:01, obteve-se o endereço IP 200.2.2.2 e a máquina com endereço Ethernet AA:AA:AA:02:02:02 obteve o endereço IP 200.1.1.1. Se você usasse as informações que estão na tabela cache, quando tentasse transmitir um pacote para o endereço IP 200.1.1.1 você o enviaria para o endereço Ethernet AA:AA:AA:01:01:01, o que está errado! Como esse endereço MAC agora está associado ao IP 200.2.2.2, você enviaria o pacote para a máquina errada! • a placa de rede da máquina 200.1.1.1 que possuía endereço Ethernet AA:AA:AA01:01:01 pode ter dado algum problema e ter sido trocada por outra. Como o endereço Ethernet é um endereço gravado na própria placa, a nova placa de rede terá outro endereço Ethernet! Portanto, se continuasse usando as informações armazenadas na tabela cache inicialmente, você não conseguiria mais se comunicar com a máquina 200.1.1.1. Para minimizar os dois problemas citados, cada linha da tabela cache possui uma terceira coluna, que guarda uma informação de tempo, chama timestamp. Essa coluna contém o instante do tempo em que a entrada foi inserida na tabela. Assim, se depois de certo tempo em que foi inserida a entrada não for utilizada, ela é removida. Além disso, mesmo as entradas utilizadas são removidas depois de certo tempo (que naturalmente é maior que o das entradas não utilizadas). Por fim, saiba que a tabela cache de ARP é normalmente chamada apenas de “Tabela ARP”. 1. Para que serve a Tabela ARP? Manipulando a cache Podemos facilmente listar o conteúdo da tabela ARP a partir de comandos do sistema operacional. O comando para listar o conteúdo da tabela é: arp –n. Vamos usar este comando vendo alguns exemplos práticos. Quando tudo dá certo Supondo que temos uma rede como a mostrada na Figura 3. Vamos ver o que acontece quando a máquina A (10.1.1.1) vai enviar um ping para a máquina C (10.1.1.3). Suponha que a máquina A acabou de ser ligada e ainda não se comunicou com ninguém. Desse modo, antes de executar o comando ping na máquina A podemos ver que sua tabela ARP está vazia, digitando o comando arp –n. Veja na Figura 6 que nada é mostrado. Figura 6 - Tabela ARP vazia A seguir, o usuário da máquina A realiza o comando ping para a máquina C. Como podemos ver na Figura 7, a máquina C enviou as respostas, de modo que o comando foi bem sucedido. Figura 7 - Comando Ping executado com sucesso Como a máquina A precisou descobrir o Ethernet associado ao endereço de C (10.1.1.3) para poder lhe enviar os pacotes, ela lhe enviou primeiro uma mensagem ARP e colocou a resposta na Tabela ARP (cache). Podemos verificar isso digitando o comando arp –n na máquina A, conforme mostrado na Figura 8. Figura 8 - Tabela ARP contendo o endereço Ethernet associado ao IP 10.1.13 A coluna Address mostra o endereço IP ao qual o Ethernet mostrado na coluna HWaddress está associado. A colunaHWtype diz que o tipo do endereço da coluna HWaddress é um endereço Ethernet, e a coluna Iface diz qual das placas de rede da máquina A está conectada na rede onde a máquina C se encontra (lembre-se que se a máquina A fosse um roteador ela teria mais de uma placa de rede). Como já explicamos antes, essa entrada fica guardada na Tabela ARP por certo tempo (tipicamente dois minutos) e depois é apagada. Durante o tempo em que ela está na cachê, se a máquina A tentar se comunicar com a máquina C, não será necessário enviar uma nova mensagem ARP. Depois que ela for excluída a próxima tentativa de comunicação com C irá gerar novamente uma mensagem ARP e a resposta será incluída na Cache. O processo continua sempre desse modo, com as informações sendo incluídas e apagadas da cache. Portanto, se esperássemos aproximadamente 2 minutos e executássemos o comando arp –n novamente na máquina A, veríamos que sua tabela ARP estaria vazia novamente. Otimização do ARP Normalmente uma máquina que recebe um pacote IP de outra envia pacotes de volta para esta máquina. Assim sendo, quando uma máquina recebe uma requisição ARP ela pode já inserir o endereço IP e o Ethernet da máquina que lhe enviou o quadro na sua tabela cache. Isso evita que ela precise enviar uma requisição ARP para a máquina de origem quando tentar se comunicar com ela. Desse modo, no exemplo que mostramos – a máquina A enviando um ping para a máquina C –, a máquina C, ao receber a requisição ARP perguntando pelo seu IP (10.1.1.3), já insere o endereço IP (10.1.1.1) e o Ethernet (AA:AA:AA:00:00:01) da máquina A na sua cache. Assim, quando C for responder ao ping, o endereço Ethernet de A já vai ser conhecido e não será necessário enviar uma nova requisição ARP. Na Figura 9, mostramos a tabela ARP da máquina C após receber o ping de A. Veja que foi inserida uma entrada para o IP e Ethernet de A. Figura 9 - Tabela Cache da Máquina C após receber ping de A Talvez você esteja se perguntando: “Espere aí, como tenho certeza se essa entrada foi inserida automaticamente quando C recebeu o ARP ou após ele tentar enviar a resposta do ping?” Se você pensou isso, tem razão. Apenas olhando a tabela não dá para saber, pois quando C tentasse enviar a resposta do ping, ele iria primeiro enviar uma mensagem ARP para A, de modo que a tabela seria preenchida do mesmo jeito! Para termos certeza que C não enviou nenhuma mensagem de requisição ARP, basta capturarmos os quadros transmitidos. Isso pode ser feito em A, em C, ou em qualquer máquina da rede, uma vez que os quadros são enviados em broadcast. Vamos capturar na máquina B – veja Figura 4(c). Na Figura 10, que mostra os quadros capturados durante o envio e a resposta do ping de A para C, você pode ver que em nenhum momento C envia mensagens de requisição ARP para A! Ele envia apenas a resposta ao pedido feito por A. A linha 4 da Figura 10 mostra a mensagem de ARP Request (requisição), onde 10.1.1.1 perguntou quem tem (Who-has) o IP 10.1.1.3. A linha 5, mostra o ARP Reply (resposta), onde 10.1.1.3 responde dizendo que o endereço Ethernet é (is-at) AA:AA:AA:00:00:03. As demais 6 linhas representam os três pacotes ping trocados entre as máquinas. Desse modo, as linhas 6 e 7 são referentes ao primeiro pacote ping (veja a indicação seq 1), e mostram, respectivamente, a requisição ping enviada de 10.1.1.1 para 10.1.1.3 e a resposta enviada de 10.1.1.3 para 10.1.1.1. Figura 10 - Captura de quadros na rede durante a comunicação entre A e C Quando não obtemos resposta Vamos ver agora como fica a tabela ARP quando tentamos nos comunicar com alguém que não responde a mensagem ARP (provavelmente porque a máquina está desligada, ou porque digitamos um endereço IP que não pertence a ninguém da rede). Figura 11 - Tabela ARP com endereço Ethernet não localizado Veja na Figura 11 que inicialmente enviamos um ping para o endereço IP 10.1.1.5, para o qual sabemos não existir nenhuma máquina com este endereço na rede. A resposta do ping, “Destination Host Unreachable”, significa “Máquina de Destino Inalcançável”, ou seja, ninguém respondeu ao ping enviado. A verdade é que o ping nem mesmo chegou a ser enviado! Como a máquina de origem e a de destino pertencem à mesma rede (10.1.1.0), a máquina A enviou primeiro uma mensagem ARP perguntando pelo Ethernet de 10.1.1.5, mas ninguém respondeu! O valor “(incomplete)“ na coluna HWaddress mostra que não foi possível descobrir o endereço Ethernet associado ao IP 10.1.1.5. Como esta entrada na tabela está “incompleta”, sempre que alguém tentar se comunicar com o endereço IP 10.1.1.5 uma nova mensagem ARP será enviada. Alterando a tabela ARP Além de verificar o conteúdo da cache também é possível excluir alguma entrada ou cadastrar uma entrada de modo permanente. Saiba, entretanto, que normalmente você não precisa utilizar esses comandos, pois o ARP funciona sem requer nenhuma configuração. O cadastro permanente de endereços na cache é chamado estático e asentradas inseridas desta forma nunca são apagadas depois de certo tempo. Embora forneça um nível maior de segurança que o modo dinâmico, pois dificulta a tentativa de alguém em se passar por um determinado endereço, é menos flexível que o modo dinâmico. – se a máquina mudar seu IP ou Ethernet, você terá que mudar o registro manualmente. O comando para inserir um registro associando o endereço Ethernet xx:xx:xx:xx:xx:xx com o IP A.B.C.D é: arp –s A.B.C.D xx:xx:xx:xx:xx:xx. A exclusão de registros da cache é muito utilizada quando você quer fazer testes, e eventualmente quando você detecta que algum endereço na cache ainda não foi atualizado após você trocar a placa de rede de alguma máquina. A Figura 12 mostra como é possível excluir o endereço Ethernet associado a um IP. Veja que após a exclusão o endereço aparece como “incompleto”. Figura 12 - Excluindo um endereço Ethernet da tabela ARP Máquinas em redes diferentes e o ARP Vamos agora analisar o que acontece com o ARP em um cenário diferente do que usamos nos exemplos anteriores, onde todas as máquinas estavam na mesma rede. Nesse nosso cenário, que é mostrado na Figura 13, as máquinas A e C estão em duas redes diferentes, e a máquina B é agora um roteador que possui duas placas de rede, uma conectada a cada rede. Figura 13 - ARP com máquinas em redes diferentes Observe que a placa de rede eth0 de B possui IP 10.1.1.1 e Ethernet AA:AA:AA:00:01:01, e a placa eth1 possui IP 10.2.2.1 e Ethernet AA:AA:AA:00:02:01. Além disso, o Gateway (roteador padrão) da máquina A é 10.1.1.1 e o Gateway da máquina C é 10.2.2.1. O que será que acontece quando A realiza um ping para C? Qual endereço Ethernet será colocado na Tabela ARP de A? Vamos olhar logo como a tabela ARP fica, e depois explicamos. Veja na Figura 14 a tabela cache de A após o ping para C. Figura 14 - Tabela ARP após ping para máquina em outra rede Entendeu o que aconteceu? Ou seja, se tentamos nos comunicar com a máquina C, por que apareceram os endereços IP e Ethernet do roteador (máquina B)? A resposta é que como A e C estão em redes diferentes, não há como enviar o pacote IP diretamente para C. Esse pacote precisa ser enviado para o roteador da rede onde A se encontra. Se tiver com dúvidas quanto a esse este assunto, volte na parte de roteamento da disciplina de Sistemas de Conectividade e faça uma revisão. Assim sendo, não faria sentido enviar uma ARP tentando descobrir o Ethernet de C. Para enviar o pacote IP para o roteador é necessário colocar o endereço Ethernet do roteador no campo de destino do quadro Ethernet. É aqui que o ARP entra. Como a máquina B sabe o endereço IP do roteador da sua rede, ela envia uma requisição ARP perguntando por esse IP. Na verdade ela não quer enviar nenhum pacote para o endereço IP do roteador, ela quer apenas descobrir seu Ethernet! A Figura 15 mostra os pacotes capturados (apenas os primeiros pacotes) na máquina B, quando a máquina A enviou um ping para C. Veja que B recebe uma requisição ARP vinda de A (10.1.1.2) para seu IP (10.1.1.1), e envia a resposta. Observe também que, embora os pacotes de ping sejam enviados ao roteador (para seu Ethernet), naturalmente o endereço IP de destino do pacote é o IP de C (10.2.2.2). Figura 15 - Mensagens ARP quando as máquinas origem e destino estão em redes diferentes Resumo Nesta aula você aprendeu que o protocolo ARP traduz endereços da camada de rede para endereços da camada de enlace (endereços MAC), e que na maioria das redes esses protocolos são o IP e o Ethernet. Aprendeu que esse protocolo envia uma mensagem ARP Resquest para o endereço de broadcast da rede perguntando quem tem um determinado endereço IP. Viu que, após isso, a máquina que possui esse IP envia uma resposta diretamente para quem enviou a solicitação, contendo o Ethernet associado ao IP informado. Você aprendeu também que para reduzir o tráfego gerado na rede, e o tempo para obter a informação, o ARP faz uso de uma tabela cache (chamada de tabela ARP) que mantém os endereços (IP e Ethernet) recentemente descobertos. Acesse o Laboratório 1 desta aula (Aula 1) no Virtualbox e realize os seguintes procedimentos. 1. Verifique a tabela ARP da máquina A. 2. Coloque o tcpdump para executar na máquina B. Execute um ping da máquina A para a máquina C. Depois verifique como ficaram as tabelas ARP das máquinas A, B, e C. 3. Verifique os pacotes capturados no passo anterior pelo tcpdump e veja se foram trocados os pacotes que você esperava. 4. Execute o ping de A para C novamente e veja os pacotes capturados em B. 5. Na máquina A, exclua a entrada referente ao IP de C e execute o ping novamente de A para C. Mais uma vez analise os pacotes capturados em B. Na disciplina Sistemas de Conectividade nós estudamos o modelo OSI e o modelo TC/IP. Embora você tenha aprendido a função de todas as camadas, estudamos em detalhes apenas as seguintes camadas: física, enlace e rede. Para isso, estudamos protocolos como o Ethernet e o 802.11, que implementam as camadas física e de enlace, e o protocolo IP, que implementa a camada de rede. Além disso, na aula passada, você estudou o ARP que proporciona a integração do protocolo de rede com o de enlace. Nesta aula estudaremos dois protocolos que implementam a camada de transporte na pilha TCP/IP. Eles são o User Datagram Protocol (UDP – Protocolo de Datagrama do Usuário) e o Transmission Protocol Protocol (TCP – Protocolo de Controle da Transmissão). Saiba que praticamente todos os programas que você utiliza quando está acessando a Internet usam um desses dois protocolos. Após o final desta aula você será capaz de: • Entender como o protocolo TCP funciona, e o que ele oferece aos desenvolvedores de aplicações. • Entender como o protocolo UDP funciona, e o que ele oferece aos desenvolvedores de aplicações. • Saber qual protocolo de transporte utilizar quando for escrever um programa que transmita informações pela rede. • Entender como os programas utilizam a camada de transporte. A camada de transporte Na Aula 5 da disciplina Sistemas de Conectividade, nós explicamos o modelo OSI e as principais funções de cada camada. Nesta aula vamos analisar em mais detalhes as principais funções que podem ser desempenhadas pela camada de transporte. É importante observar que algumas funções devem ser desempenhadas por qualquer protocolo de transporte, enquanto outras são opcionais. Por isso, existem vários protocolos de transporte e você deve escolher o que pretende utilizar nas suas aplicações. Após explicarmos os protocolos existentes vamos lhe dar parâmetros para ajudá-lo a fazer essa escolha. Observe que enquanto a escolha dos protocolos de enlace e de rede é determinada pela rede onde a máquina será utilizada, a escolha do protocolo de transporte é de inteira responsabilidade do desenvolvedor da aplicação. O protocolo de transporte é apenas software, e esse software precisa estar instalado apenas nas máquinas que vão se comunicar pela rede. Ou seja, ele não precisa estar instalado nos roteadores ao longo do caminho, como acontece com o protocolo de rede! Isso acontece porque as informações dos cabeçalhos de transporte só precisam ser analisadas pela máquina que gera o pacote e pela máquina para quem ele é destinado. Atualmente, praticamente todas as máquinas já vêm com os protocolos TCP e UDP instalados. Portanto, como normalmente você vai usar um deles, muito provavelmente você nunca vai precisar instalar um protocolo de transporte! É muito importante que você compreenda como a camada de transporte funciona, por dois motivos. O primeiro é que dependendo da linguagem de programação que você utilize para escrever seus programas, você pode ter que lidar diretamente com detalhes da camada de transporte. Mesmo que você use uma linguagem de programação que esconda esses detalhes, proporcionando-lhe uma visão de “mais alto nível”, como se costuma dizer, o programa ainda vai estar usando a camada de transporte. Portanto, é importante que você saiba como ele está utilizando a rede. O segundo motivo é que para identificar problemas na comunicação feita pelos programas você vai acabar utilizando uma ferramenta como o Wireshark, estudado na Aula 3. Como você viu naquela aula, ele mostra detalhes dos pacotes transmitidos e é importante, por exemplo, que você saiba quais pacotes deveriam ter sido transmitidos pelo seu programa para poder identificar possíveis problemas. Na próxima seção vamos analisar uma funcionalidade que deve ser fornecida por qualquer protocolo da camada de transporte. As demais funcionalidades que podem ser fornecidas por um protocolo de camada de transporte serão explicadas nas seções que abordam o TCP e o UDP. Número de portas Você já sabe que enquanto a camada de enlace possibilita que duas máquinas se comuniquem na mesma rede, a camada de rede permite que máquinas em redes diferentes se comuniquem. Assim sendo, você aprendeu que o protocolo IP é capaz de encaminhar um pacote IP para qualquer máquina na Internet, e para que isso seja possível, ele define uma forma de identificar cada máquina, que é o endereço IP. A questão é que identificar a máquina para quem um pacote deve ser entregue normalmente é apenas uma parte do trabalho, pois geralmente os pacotes precisam ser entregues a programas (aplicações) que executam nas máquinas. Assim sendo, é necessário que exista algum mecanismo para identificar também cada aplicação que pretende transmitir/receber pacotes pela rede. Veja na Figura 1 que apenas com as informações do cabeçalho IP não é possível determinar para qual aplicação na máquina com endereço IP 10.1.1.1 o pacote deve ser entregue. Ou seja, o sistema operacional recebe o pacote ao ver que ele está destinado ao seu endereço IP, mas não tem como determinar para qual aplicação ele é. Figura 1 - Como saber para qual aplicação entregar um pacote? Para resolver esse problema, a camada de transporte oferece um mecanismo de identificação das aplicações chamado deporta. Uma porta nada mais é que um número (de dois bytes) que identifica cada aplicação. Na verdade, a porta identifica um “canal de comunicação” dentro da aplicação, uma vez que uma mesma aplicação pode utilizar várias portas. A Figura 2 mostra uma máquina com duas aplicações, uma está utilizando a porta 25 e a outra a porta 80. Do mesmo modo que o cabeçalho de rede contém os endereços IP de origem e destino, o cabeçalho de transporte contém as portas de origem e destino (que de certo modo equivalem a endereços das aplicações dentro de cada máquina). Assim sendo, o número contido no campo referente à Porta de Destino é utilizado para identificar a aplicação para quem o pacote deve ser entregue. No caso da Figura 2, o pacote será entregue ao servidor Web, pois ele está registrado na porta 80, que é o número da porta de destino contido no pacote. Figura 2 - Identificando a aplicação através do número de porta Definindo qual número de porta utilizar Agora você precisa entender como é definido o número de porta que cada aplicação vai utilizar. A atribuição de portas as aplicações funciona da seguinte maneira: • Uma aplicação que pretende transmitir e/ou receber informações pela rede solicita uma porta ao sistema operacional. Ela pode informar o número da porta desejado ou deixar que o sistema operacional escolha uma. • Caso a aplicação tenha informado o número de porta desejado, o sistema operacional analisa sua lista de números de portas ainda não atribuídas para as aplicações para ter certeza que o número solicitado está livre. Se ele estiver livre, esse número de porta é atribuído à aplicação. Caso o número solicitado já esteja sendo utilizado, será gerado um erro. Quando a aplicação não sugere nenhum número de porta, o sistema operacional escolhe um número dessa lista e o atribui à aplicação. • O sistema operacional retira da lista de portas livres o número da porta fornecido à aplicação. • O sistema operacional insere em uma lista de portas utilizadas o número da porta fornecido e o programa que solicitou a porta. Como você pode observar na Figura 2, quando uma máquina transmite um pacote ela insere no cabeçalho de transporte o seu número de porta e o número da porta da aplicação com quem ela deseja se comunicar. A afirmação anterior levanta duas questões muito importantes. A primeira é que tanto a aplicação cliente (que envia o pacote) quanto a servidora (que receberá o pacote) precisam de números de portas. Portanto, as duas realizaram o procedimento de pedir um número de porta ao sistema operacional. A segunda questão é como as aplicações sabem o número de porta utilizado pela outra, uma vez que o cliente e o servidor quase sempre utilizam portas diferentes. Vamos responder a essa segunda questão a seguir. Porta nas aplicações que atuam como servidores Os servidores utilizam um número de porta padronizado, ou seja, cada tipo de aplicação da Internet (web, e-mail, dns etc.) utiliza uma porta específica. Pegue como exemplo dois tipos de aplicações da Internet que você utiliza: e-mail e web. Os números de porta utilizados na Figura 2 não foram números inventados. Todos os servidores de e-mail utilizam a porta 25, e todos os servidores web utilizam a porta 80. Desse modo, quando um programa cliente de e-mail vai conectar em um servidor ele envia os pacotes para a porta 25. Do mesmo modo, quando o seu browser vai acessar um servidor web ele envia os pacotes para a porta 80 do servidor. Se você criar uma aplicação você pode definir a porta que ela vai utilizar e deixar essa informação disponível para todos. Isso foi feito para as aplicações de banco de dados, por exemplo. O servidor de banco de dados SQL Server, por exemplo, utiliza a porta 1433, enquanto o Servidor de banco de dados MySQL utiliza a porta 3306. Você pode estar pensando: como esse número de porta é utilizado, se você nunca o informa em nenhuma aplicação que utiliza? No browser, por exemplo, você coloca apenas o nome da máquina que quer conectar. A questão é que quando você não informa nenhum número de porta, os programas o inserem automaticamente nos pacotes que eles geram. O número de porta inserido é sempre o número de porta padrão para aquele tipo de aplicação. Quando a aplicação for um browser, o número inserido será 80, que é o número da porta padrão utilizada pelos servidores web. Uma vez que os programas servidores sempre utilizam a mesma porta, talvez você já tenha percebido que eles sempre solicitam um número de porta específico ao sistema operacional. Os servidores web, por exemplo, solicitam a porta 80, e os servidores de e-mail solicitam a porta 25. Como a maioria das aplicações padrão da Internet – e-mail, web, dns, FTP, entre outras – utilizam portas abaixo de 1024, para que um programa possa solicitar uma porta abaixo desse número você precisa estar logado como administrador da máquina. Isso foi feito para evitar que um programa de usuário obtenha a porta utilizada por um desses serviços e, quando a aplicação referente ao serviço for iniciada, a porta já esteja sendo utilizada pela aplicação do usuário. Essas portas abaixo de 1024 são chamadas de portas reservadas. Acesse o site https://www.iana.org/assignments/port-numbers para conhecer os números de portas e suas descrições. Porta nas aplicações que atuam como clientes Naturalmente as aplicações “clientes” também precisam de um número de porta, mas esse número de porta não precisa ser fixo, como no caso das aplicações “servidoras”. Desse modo, as aplicações cliente não especificam a porta que pretendem utilizar. Ao invés disso, elas deixam que o sistema operacional lhes forneça uma porta qualquer. A porta oferecida pelo sistema operacional é sempre maior ou igual que 1024, para evitar conflito com as portas reservadas. A questão é que o cliente sempre envia primeiro um pacote para outra máquina (o servidor), para só depois receber algum pacote. Como o número da porta que a aplicação que envia o pacote está utilizando (no caso o cliente) é inserido no pacote (no campo Porta de Origem), a aplicação que o recebe vai saber qual porta o cliente está utilizando. A Figura 3 ilustra este fato. Quando o browser na máquina com endereço IP 10.1.1.2 (cliente) quer acessar o servidor web na máquina com endereço IP 10.1.1.1 (servidor), ele envia seus pacotes para a porta 80, pois essa é a porta padrão de qualquer servidor web. Como o servidor web só envia alguma coisa para o browser após receber algum pacote dele, e esse pacote contém o número da porta utilizada pelo cliente (no caso 1300), o servidor pode obter o número da porta dos pacotes recebidos. Figura 3 - Como o servidor descobre a porta do cliente As aplicações cliente normalmente não solicitam a porta assim que são iniciadas. Ao invés disso, elas só solicitam a porta no momento que precisam transmitir alguma coisa. Após o término da transmissão elas liberam a porta. Além disso, qualquer aplicação pode usar mais de uma porta, como já dissemos antes. No caso do browser, imagine se você estiver fazendo o download de dois arquivos simultaneamente. Cada download estará usando uma porta diferente no cliente. O mesmo acontece quando você abre várias abas no browser para acessar diversas páginas – cada aba vai utilizar uma porta diferente. 1. O que são portas na camada de transporte? 2. Como o servidor descobre a porta de um cliente? 3. Se um programa executado por um usuário normal solicitar a porta 80 ao sistema operacional, a porta será atribuída a ele? O Protocolo TCP O protocolo TCP é o protocolo de transporte mais utilizado na Internet. A maioria das aplicações que você conhece como e-mail, web, FTP, P2P, entre outras, utiliza TCP, e nesta seção você vai entender o porquê. Vamos analisar separadamente cada uma das funcionalidades oferecidas pelo TCP, mas inicialmente você já deve saber que o TCP procura tornar a rede por onde os pacotes serão transmitidos confiável. Lembre-se que a camada de rede IP não é confiável, pois os pacotes IP podem ser perdidos e podem chegar fora de ordem no destino. Portanto, as duas principais funções do TCP são: • garantir que os pacotes TCP sejam entregues à aplicação de destino na mesma ordem em que foram transmitidos pela aplicação de origem; • garantir que todos os pacotes transmitidos sejam recebidos. Além das duas funções acima, o TCP também oferece outras funcionalidades, como, por exemplo, garantir que a máquina de origem envie os pacotes em uma taxa que o receptor possa processá-los. A isso dá-se o nome de controle de fluxo, e você pode obter mais informações no endereço: https://pt.wikipedia.org/wiki/Transmission_Control_Protocol Vamos agora começar as funcionalidades do TCP. Protocolo orientado à conexão Para conseguir realizar todas essas tarefas o TCP precisa manter uma série de informações sobre a comunicação que está ocorrendo. Por isso, ele requer que uma conexão seja estabelecida entre as duas máquinas que desejam se comunicar, antes que as informações possam realmente ser transmitidas. Estabelecer uma conexão significa que uma máquina vai pedir para conversar com a outra, de modo que ambas se preparem para controlar a comunicação que irá acontecer. Esse controle se refere, por exemplo, à criação de variáveis para armazenar informações sobre o andamento da comunicação. Entre outras informações, o cabeçalho TCP possui um campo formado por diversos bits (chamados de flags) que indicam o tipodo pacote sendo transmitido. Esse tipo pode ser, por exemplo, se é um pacote de pedido de conexão ou um pacote de confirmação. A combinação dos flags definidos (possuem o valor 1) é que indica o tipo do pacote. Para estabelecer uma conexão são trocados três pacotes, conforme mostrado na Figura 4, onde a máquina A estabelece uma conexão com a máquina B. Veja que a máquina A envia um pacote de solicitação de conexão (flag SYN definido), a máquina B responde aceitando a conexão (flags SYN e ACK definidos) e finalmente a máquina A avisa que recebeu a confirmação da conexão (pacote com apenas o flag ACK definido). Esse procedimento é chamado de Three-way-handshake em referência ao fato de serem trocados três pacotes para estabelecer a conexão. Figura 4 - Estabelecimento de uma conexão TCP Os três pacotes trocados durante a fase de estabelecimento da conexão contêm apenas o cabeçalho TCP e não contêm nada na parte de dados! Só após o estabelecimento da conexão é que os dados são, de fato, transmitidos. Conforme estudaremos na próxima seção, o TCP confirma os pacotes recebidos. Portanto, a fase de troca de dados, ou seja, a comunicação propriamente dita é composta de pacotes de dados e as confirmações. Quando não se deseja mais transmitir nada a conexão é fechada. Como uma conexão TCP é full-duplex, ou seja, podem-se transmitir dados nos dois sentidos, cada máquina precisa solicitar o fechamento da conexão. Isso é feito por pacotes com o flag FIN definido. A Figura 5 mostra as três fases de uma conexão TCP: estabelecimento de conexão, transferência de dados, encerramento da conexão. Evidentemente os pacotes mostrados na fase de dados são apenas um exemplo, pois os pacotes trocados dependem de cada situação. O importante é observar que cada máquina pode transmitir dados para a outra, independentemente de quem abriu a conexão. Figura 5 - Três fases de uma comunicação utilizando o TCP Segmentação e blocagem Quando a camada de transporte recebe dados da camada de aplicação para serem transmitidos, ela acrescenta seu cabeçalho à informação recebida e passa a PDU resultante para a camada de rede. Sobre esse cabeçalho você já sabe que ele deve conter o número da porta de origem e da porta de destino. Embora formalmente o correto seja chamar a PDU da camada de transporte de segmento, iremos usar o termo Pacote TCP para nos referirmos a uma PDU TCP, pois esse termo é mais comumente utilizado na prática. Do mesmo modo, quando estivermos estudando o UDP iremos utilizar o termo Pacote UDP para nos referirmos a uma PDU UDP. Figura 6 - Criação de um pacote TCP a partir dos dados recebidos da camada de aplicação A Figura 6 mostra que o pacote TCP é inserido dentro da parte de dados de um pacote IP. Chamamos essa parte de dados deDados’ para ressaltar que ela é composta pelos Dados passados pela camada de aplicação acrescidos do cabeçalho TCP. Entretanto, nem sempre é gerado exatamente um pacote TCP para cada solicitação de transmissão feita pela camada de aplicação. O TCP pode decidir dividir os Dadospassados pela camada de aplicação em duas ou mais partes e enviar cada uma delas em um pacote TCP separado. Esse procedimento de divisão é chamado segmentação. A Figura 7 mostra o caso onde uma solicitação de transmissão da camada de aplicação foi dividida em dois pacotes TCP. Figura 7 - Divisão de uma mensagem de aplicação em dois pacotes TCP Esse processo é chamado segmentação e normalmente é realizado para impedir que o pacote IP que vai ser gerado fique muito grande e precise ser dividido (fragmentação IP) porque não caberia na parte de dados da camada de enlace onde vai ser transmitido – por exemplo, uma rede Ethernet. No exemplo acima, uma mensagem de aplicação foi dividida em dois pacotes TCP, mas pode acontecer também de o TCP juntar duas ou mais mensagens de aplicação em um único pacote TCP. Tal procedimento é chamado Blocagem. Isso acontece devido ao fato de que enviar pacotes muito pequenos reduz o desempenho da rede porque aumenta a quantidade de bytes de cabeçalho transmitidos em relação aos bytes de dados do usuário. Portanto, gasta-se mais banda de rede para informações de controle, como é o caso dos cabeçalhos. A Figura 8 mostra o caso onde o TCP decide juntar duas mensagens de aplicação em um único pacote TCP. Isso tipicamente ocorrerá quando o tamanho das mensagens de aplicação for muito pequeno. Figura 8 - Junção de duas mensagens de aplicação em um pacote TCP Ordenamento dos pacotes Como os pacotes TCP são transmitidos dentro de pacotes IP, eles podem chegar fora de ordem na máquina de destino. Para resolver esses problemas o TCP numera os pacotes que transmite e insere o número de cada pacote em um campo no seu cabeçalho. Desse modo, a máquina de destino pode colocar os pacotes na ordem antes de passá-los para a camada de aplicação. Suponha que a máquina A transmitiu os pacotes 1, 2 e 3 para a máquina B, mas eles chegaram na máquina B na ordem 1, 3 e 2. Ao receber o pacote 1 o TCP o entrega para a camada de aplicação. Depois disso ele recebe o pacote 3, mas como ainda não recebeu o pacote de número 2, ele guarda o pacote 3 em um buffer até que receba o pacote 2. Quando isso acontecer ele entrega o pacote 2 e o pacote 3 para a camada de aplicação, nessa ordem. Embora tenhamos dito que o campo do cabeçalho TCP para identificar os pacotes numera cada pacote (pacote 1, pacote 2 etc.), na verdade ele indica, contando com o primeiro byte do pacote, quantos bytes já foram transmitidos. Desse modo, se desde o início da conexão já tiverem sido transmitidos 5.000 bytes de A para B, o próximo pacote que A transmitir terá como “número do pacote” 5001. Apesar dessa diferença, a forma de usar este valor é a mesma, como se ele realmente fosse o número do pacote. Além disso, cada máquina mantém o seu contador, pois o número de bytes transmitidos em cada sentido da conexão normalmente é diferente. Iremos continuar nos referindo a ele como número do pacote porque deixa o texto muito mais simples, e conceitualmente é a mesma coisa – a diferença diz respeito apenas à forma como a ideia é implementada. Controle de erros Ainda devido ao fato dos pacotes TCP serem transmitidos dentro de pacotes IP, eles podem ser perdidos. Para resolver esse problema, um mecanismo de confirmação dos pacotes recebidos é acrescentado ao esquema de numeração dos pacotes TCP. Para cada pacote transmitido o TCP inicia um temporizador. Cada pacote precisa ser confirmado pelo receptor antes que o temporizador expire. Se o temporizador expirar sem que a confirmação tenha chegado, o pacote é retransmitido pelo próprio TCP. Isso significa que o TCP guarda todos os pacotes ainda não confirmados em um buffer para que possa retransmiti-los caso seja necessário. Isso tudo ocorre sem que a aplicação que enviou o dado tome conhecimento. Ou seja, suponha que sua aplicação solicitou ao TCP que transmitisse uma mensagem e o TCP a colocou em um pacote que foi enviado para a máquina destino. Caso esse pacote fosse perdido, o próprio TCP detectaria e o retransmitiria, sem que seu programa precisasse ficar sabendo que o erro aconteceu! Isso simplifica bastante a escrita de programas. Uma coisa importante é que quando se confirma um pacote de número X, isso significa que todos os pacotes com números menores que X são também confirmados. Portanto, se uma máquina recebeu os pacotes 1, 2 e 4, ela não pode confirmar o 4, pois isso estaria confirmando também os pacotes 1, 2 e 3. Nesse caso, ela confirmaria os pacotes 1 e 2 e esperaria o pacote 3 chegar para poder confirmar o pacote 4. Os pacotes de confirmação contêm o número do pacote sendo confirmado e o flag ACK definido (valor 1). Entretanto, uma confirmação pode ser enviada em um pacote exclusivo para essa finalidade, ou seja, em um pacote que não contém dados, ou pode ser enviada em um pacote contendo dados. Como todos os pacotes TCP contêm o campo de flag ACK e o campo para informar o número do pacote confirmado, normalmente as confirmações são enviadas pegando carona nos pacotes de dados. Só se envia um pacote exclusivo para confirmação quando não há dados para transmitir no sentido da conexão que se necessita transmitir a confirmação. Controle de fluxo Os pacotes TCP que chegam a uma máquina ficam em um buffer até que sejam lidos pela aplicação. Quando a aplicação demora a ler esses dados o buffer pode encher, e se isso acontecer, a máquina não terá onde colocar os dados que chegarem. Para evitar esse problema o TCP possui um mecanismo que controla a quantidade de dados que uma máquina pode enviar para outra. Para isso existe um campo no cabeçalho TCP onde cada máquina informa para a outra a quantidade de espaço livre no seu buffer. Se uma máquina informar 0 (zero), a outra para de transmitir. Sempre que o valor informado for maior que zero, a máquina pode transmitir no máximo a quantidade de bytes informada. Além deste mecanismo, o TCP possui outros mecanismos complexos, que não estudaremos, mas que permitem que o TCP adapte a taxa com que envia os pacotes dependendo de como está o tráfego na rede. Formato do pacote TCP A Figura 9 mostra o formato do cabeçalho TCP. Explicaremos apenas os principais campos. Figura 9 - Formato do pacote TCP Porta de Origem: Porta utilizada pela aplicação transmitindo o pacote. Porta de Destino: Porta que identifica a aplicação para quem o pacote deve ser entregue. Número de sequência: Número de sequência do pacote. Lembre-se que na verdade representa a posição do primeiro byte deste pacote dentro do fluxo de bytes já transmitidos. Se em uma conexão já tivessem sido transmitidos 8499 bytes, o Número deSequência do próximo pacote seria 8500. Número de confirmação: Só é válido quando o flag ACK estiver definido, e indica o número do byte reconhecido. Suponha que se deseja confirmar um pacote recebido que tinha 9000 no campo Número de Sequência e possuía 500 bytes. O valor deste campo seria 9501, indicando que os bytes da conexão até o número 9500 já foram recebidos. Tamanho do cabeçalho. Indica o tamanho do cabeçalho TCP (em número de palavras de 32 bits), ou seja, multiplique o valor do campo por 4 para obter o tamanho do cabeçalho em bytes. É necessário porque podem existir campos opcionais (por isso a indicação Opções no formato do quadro). SYN. Se contém 1, indica que o pacote é um pedido de conexão. ACK. Se contém 1, indica que é um pacote de confirmação. Se SYN também contém 1, indica que é uma confirmação de um pedido de conexão. Se SYN contém 0, é uma confirmação de dados, e o campo Número de Confirmação contém o número do pacote sendo confirmado. FIN. Pedido de encerramento de conexão. RST (Reset). Encerrando uma conexão porque algo estranho aconteceu na conexão. Protege contra ataques ou erros. PSH (Push). Usado pelo remetente para solicitar que os dados sejam entregues à aplicação de destino o mais rápido possível, após o pacote chegar naquela máquina. URG (Urgent). Indica que o valor do campo Ponteiro Urgente é válido. Tamanho da janela. Usado para indicar o tamanho disponível no buffer (veja a explicação sobre controle de fluxo). TCP Checksum. Campo de verificação de erros no cabeçalho TCP. Semelhante ao campo checksum do cabeçalho IP, por exemplo. Ponteiro Urgente. Usado pela origem para indicar onde se encontra algum dado urgente dentro do segmento. Opções. O cabeçalho TCP pode conter campos opcionais. O mais utilizado chama-se MSS (Max Segment Size – Tamanho máximo do segmento) e indica qual deve ser o tamanho máximo de cada pacote TCP gerado pela máquina para tentar evitar que os pacotes IP que vão conter os segmentos sejam fragmentados. O valor colocado neste campo é baseado no tamanho da parte de dados da camada de enlace da máquina gerando o pacote. Dados. Não é um campo do cabeçalho! Ele apenas indica que após o cabeçalho TCP o pacote contém a mensagem recebida da camada de aplicação. 1. Quantos pacotes são necessários para abrir uma conexão TCP? 2. Algum pacote TCP pode ser transmitido sem nenhum byte na sua parte de dados? 3. Para que serve o campo “Tamanho da Janela” no cabeçalho TCP? 4. Explique qual é a função do controle de fluxo. Protocolo UDP O protocolo UDP é um protocolo de transporte bastante simples que procura oferecer às aplicações um serviço de entrega de pacotes básico. Este serviço consiste apenas em colocar a mensagem recebida da camada de aplicação dentro de um segmento (o chamaremos de pacote UDP), utilizando os números de portas para identificar as aplicações. Portanto, as características oferecidas pelo UDP são praticamente as mesmas que o próprio IP oferece, com a diferença que o pacote UDP utiliza números de porta para identificar as aplicações. Uma boa forma de você entender o UDP é comparando-o com o TCP. A seguir listamos as principais diferenças entre os dois protocolos. • O UDP não garante que os pacotes serão entregues na ordem em que foram transmitidos. Ou seja, os pacotes são passados para a camada de aplicação na máquina destino na ordem em que são recebidos por ela – e essa ordem pode ser diferente da que foram transmitidos. • O UDP não garante a entrega de pacotes, pois ele não retransmite pacotes perdidos ou com erro. Na verdade a origem nem sabe se os pacotes foram perdidos. Como você verá mais adiante, existe um campo de checksum no pacote UDP que permite a detecção de pacotes que chegam com erro. Mas os pacotes com erro são descartados sem que esse fato seja avisado à máquina que transmitiu o pacote. • Não existe nenhum mecanismo para controlar a taxa com que os pacotes são enviados para a máquina de destino. Eles são enviados na taxa que a aplicação os gerar. • O UDP não realiza segmentação nem blocagem, ou seja, cada mensagem da camada de aplicação que é passada para o UDP gera exatamente um pacote UDP. A Figura 10 mostra esse procedimento. Observe que Dados’ é formado pelo conteúdo do cabeçalho UDP mais Dados. Figura 10 - Cada mensagem de aplicação gera exatamente um pacote UDP Normalmente quanto mais funcionalidades um protocolo tem, mais campos ele precisa ter no seu cabeçalho. Como o UDP não oferece muitas funcionalidades, isso se reflete no formato do pacote, fazendo com que ele seja bastante simples. A Figura 11 mostra o formato de um pacote UDP. O campo Tamanho da Mensagem é o tamanho total do pacote UDP em bytes.Observe queDados não é um campo do cabeçalho! Ele apenas indica que após o cabeçalho UDP, o pacote contém a mensagem recebida da camada de aplicação. Figura 11 - Formato do pacote UDP 1. O protocolo UDP retransmite pacotes perdidos? 2. Quando uma máquina recebe um pacote UDP com erro, ela avisa à máquina que o enviou? 3. Se o tamanho de uma mensagem passada para o protocolo UDP for muito grande o UDP a divide para ser transmitida em dois ou mais pacotes UDP? Escolhendo qual protocolo utilizar: TCP ou UDP O fato do protocolo UDP ser muito mais simples do que o TCP não significa que ele é pior. É verdade que se você utilizar UDP e precisar de alguma característica que ele não fornece, como numeração dos pacotes, por exemplo, seu programa é que terá que implementar essa função. Por outro lado, o UDP é muito mais leve e rápido do que o TCP, pois seu cabeçalho é menor e ele precisa realizar menos operações. Algumas aplicações, por exemplo, suportam a perda de pacotes, como é o caso de aplicações que transmitem voz sobre o IP. Além disso, o UDP suporta multicast e broadcast que o TCP não suporta. Podemos dizer que não existe um protocolo melhor ou pior, existe o mais adequado às necessidades de cada aplicação. Se sua aplicação, por exemplo, transmite pequenas quantidades de informação de cada vez e pode lidar com a perda de pacotes, considere a possibilidade de usar UDP. Caso sua aplicação precise de confiabilidade na entrega dos pacotes, ou seja, que eles cheguem na ordem correta e que os pacotes perdidos sejam retransmitidos, use TCP. A verdade é que a maioria das aplicações precisa de confiabilidade e, portanto, utilizam TCP. Como exemplos de protocolos que utilizam UDP, podemos citar DNS, DHCP e SNMP. Como exemplos de protocolos que utilizam TCP, podemos citar SMTP, POP3, IMAP, HTTP, FTP, LDAP, SMB e SSH. Como uma aplicação utiliza a camada de transporte Você já sabe que os programas utilizam um protocolo de transporte para transmitirem suas informações. A forma exata que o seu programa terá depende da linguagem de programação que você estiver utilizando e da API de rede que você vai utilizar. Você pode usar sockets, RPC, RMI, entre outras abordagens. Não se preocupe com o que esses nomes significam nesse momento. O que importa é que existem várias formas de fazer um programa transmitir informações pela rede. Nessa seção não pretendemos, de modo algum, ensinar-lhe a programar em rede. Queremos apenas lhe dar uma noção de como a camada de transporte é utilizada pelos programas. Para isso, vamos utilizar uma linguagem bem simples, com nomes de funções fictícios, para exemplificar as funções que um programa utilizaria. Essas funções é que fazem a comunicação do programa com a camada de transporte. Programa TCP cliente Imagine um programa cliente que utiliza TCP para transmitir dados para um servidor, conforme mostrado na Figura 12. Naturalmente o servidor também utiliza TCP. Inicialmente o cliente estabelece uma conexão com o servidor utilizando a funçãoConectar. Depois o programa transmite dados para o servidor usando a função Enviar_dados. Essa função recebe como parâmetros a conexão por onde enviar os dados e os dados a serem transmitidos. Normalmente, após transmitir dados ele deve receber algum dado enviado como resposta aos dados transmitidos. Isso é feito com a função Receber_dados. Essa função recebe como parâmetro a conexão de onde ler os dados e um buffer para onde os dados recebidos devem ser copiados. Apesar de só aparecer uma chamada para cada uma das funções Enviar_dados e Receber_dados, tipicamente um programa chama essas funções diversas vezes. Quando a máquina não deseja mais transmitir (nem receber) dados, ela solicita o fechamento da conexão. Figura 12 - Exemplo de programa cliente usando TCP Para listar as conexões estabelecidas e qual programa está usando cada conexão na sua máquina Linux em um terminal, digite: netstat -tnp Programa TCP servidor O código do programa servidor é ligeiramente diferente do cliente, pois ele precisa realizar algumas tarefas a mais que o cliente. O código do servidor é mostrado na Figura 13. Inicialmente é necessário solicitar a porta desejada ao sistema operacional. Depois disso o programa servidor tipicamente entra em loop infinito, uma vez que os servidores devem executar indefinidamente. Dentro desse loop o programa deve esperar que algum cliente conecte e aceite essa nova conexão. Depois deve ler os dados enviados pelo cliente, que tipicamente contêm alguma solicitação, e processar essa solicitação, que poderia ser, por exemplo, o pedido de uma página web. A seguir o servidor tipicamente envia dados para o cliente, por exemplo, a página web solicitada. De modo semelhante ao cliente, podem existir várias chamadas a Enviar_dados e Receber_dados dentro do loop do servidor. Finalmente, o servidor encerra a conexão e volta para o início do loop para esperar por novas conexões. Quando algum evento causar o encerramento do programa servidor ele libera a porta que havia solicitado ao sistema operacional. Figura 13 - Exemplo de programa servidor usando TCP Para listar as os programas que estão esperando conexões TCP, e em que portas, na sua máquina Linux em um terminal digite: netstat –tlnp Para programas que utilizam UDP, o comando é: netstat -ulnp Resumo Nesta aula você aprendeu os dois principais protocolos de transporte utilizados na Internet, que são o TCP e o UDP, e viu indicações de quando utilizar cada um deles. Você aprendeu que o TCP é o protocolo mais utilizado porque ele oferece confiabilidade na transmissão dos pacotes, retransmitindo os pacotes perdidos e os entregando na mesma ordem em que são transmitidos. Viu também que algumas vezes é melhor utilizar um protocolo mais simples, como é o caso do UDP, seja porque ele é mais rápido que o TCP, porque gera menos sobrecarga, ou porque suporta multicast. Você aprendeu que um conceito básico para qualquer protocolo de transporte é a noção de porta, que serve para identificar as aplicações. Finalmente analisou a forma como os programas interagem com a camada de transporte. Acesse o Laboratório 1 desta aula (Aula 5) no Virtualbox e realize os procedimentos descritos a seguir. 1. Execute o wireshark e o programa servudp1 na Máquina-A. Execute a aplicação cliudp1 na Máquina-B. Analisando as informações fornecidas pelo wireshark, responda: a. Quantos pacotes foram transmitidos nos dois sentidos, ou seja, somando os pacotes transmitidos de B para A mais os pacotes de A para B? b. Qual o conteúdo da parte de dados do(s) pacote(s) UDP transmitido(s) de B para A? c. Quais as portas usadas na Máquina-A e na Máquina-B, respectivamente? 2. Execute o wireshark e o programa servtcp1 na Máquina-A. Execute a aplicação clitcp1 na Máquina-B. Analisando as informações fornecidas pelo wireshark, responda: a. Quantos pacotes foram transmitidos nos dois sentidos, ou seja, somando os pacotes transmitidos de B para A mais os pacotes de A para B? b. Qual o conteúdo da parte de dados do(s) pacote(s) TCP transmitido(s) de B para A? c. Quais as portas usadas na Máquina-A e na Máquina-B, respectivamente? d. Qual máquina, Máquina-A ou Máquina B, solicitou primeiro o encerramento da conexão? Redes de Computadores Aula 7 – Protocolo DHCP: distribuindo automaticamente configurações IP para as estações em uma LAN Nesta aula você aprenderá um protocolo que auxilia enormemente os administradores de redes a atribuírem endereços IPs para os computadores em suas LANs. Verá que, além do endereço IP, é possível passar, via protocolo DHCP, diversas informações de configuração da rede para todo computador que é ligado à rede local. Este protocolo se chama DHCP (Dynamic Host Configuration Protocol, ou seja, Protocolo de Configuração Dinâmica de Computador), e tem como principal função distribuir automaticamente aos computadores que entram na rede todas as informações necessárias para o funcionamento correto na rede em questão. • Entender a necessidade de um protocolo de configuração dinâmica para outros protocolos. • Conhecer como o protocolo DHCP funciona e quais são as informações principais que devem ser repassadas às estações dos clientes. • Saber como configurar um servidor de DHCP no Linux. • Entender o funcionamento do protocolo observando sua execução entre um cliente e um servidor de DHCP. Por que o protocolo DHCP é necessário Você sabe que nas redes que utilizam o protocolo IP, como é o caso da Internet, os dados são transmitidos entre os computadores em pacotes IP, e que cada pacote deve conter o endereço IP da máquina que está enviando o pacote e o endereço IP da máquina para a qual ele deve ser entregue. A Figura 1 mostra o pacote IP que é gerado quando uma máquina A quer enviar dados para uma máquina C. Naturalmente, os demais campos do cabeçalho IP e os cabeçalhos das camadas de transporte e de aplicação foram omitidos. Figura 1 – Transmissão usando um pacote IP Assim, toda máquina que for gerar algum pacote IP na rede deverá possuir um endereço IP atribuído unicamente a ela. Como você já sabe, cada rede de computadores interligada à Internet possui uma faixa de endereços IPs, conhecida como prefixo do endereço de rede, reservada para esta rede. O sufixo no endereço IP identifica a máquina (host) dentro daquela rede. No exemplo da Figura 1, o prefixo seria o endereço 10, e o sufixo a parte final dos endereços, 1.1.1 para a máquina A. A atribuição dos sufixos a cada host em uma rede TCP/IP é uma tarefa delegada ao administrador da rede. Isto é, cabe ao administrador configurar em cada máquina qual endereço IP (prefixo + sufixo) a máquina usará em sua rede. Dependendo do tamanho da rede, esta pode ser uma tarefa árdua. Em redes corporativas, com centenas de computadores a serem configurados manualmente, esta é uma atividade trabalhosa e, muitas vezes, problemática, pois a remoção e adição de novas estações na rede precisa ser coordenada com cuidado para não se ter desperdícios e nem conflitos de endereços. Para complicar ainda mais esta organização dos endereços IPs nas redes, a popularização dos dispositivos móveis (notebooks, smartphones etc.) e o seu uso em nas LANs corporativas intensificou a dinamicidade (entrada e saída) de computadores nestas redes, dificultando enormemente a organização manual dos endereços IP feita pelo administrador. Portanto, hoje em dia é praticamente imperativo em qualquer rede ter um sistema de distribuição de endereços IP para os computadores, facilitando sua administração no que diz respeito à configuração que deve ser feita em cada computador para que o mesmo possa conversar com os outros através dos protocolos da família TCP/IP. Lembre-se de que somente o endereço IP não é suficiente para que um computador trabalhe em redes TCP/IP. É necessário também a máscara de rede e o endereço IP do gateway. Com a máscara de rede, o host terá condições de determinar o prefixo do endereço de sua rede, possibilitando identificar se um IP de destino de um pacote é de sua rede ou não. Caso não seja de sua rede, o host deverá encaminhar este pacote IP para o gateway de sua rede. 1. Imagine-se em um ambiente de rede com vários computadores e que não exista um servidor DHCP. Qual o principal problema que você (como administrador dessa rede) deverá encontrar? 2. Caso um cliente desta rede atribua um mesmo endereço IP de uma máquina já existente, você acha que ambas irão ter acesso? Por quê? Funcionamento do DHCP Agora que sabemos a falta que o DHCP pode acarretar em uma rede, iremos mostrar como tal protocolo funciona, para assim entendermos como ele resolve, de forma automática, o problema de atribuição de endereços IP para nossa rede. Para automatizar a configuração dos dispositivos em uma rede, o IERF (Internet Engineering Task Force) desenvolveu oDynamic Host Configuration Protocol (DHCP). Diferentemente do BOOTP, primeiro protocolo desenvolvido com esta finalidade de passar as configurações IP para os hosts na rede, o DHCP não exige que um administrador acrescente uma entrada para cada computador à base de dados que um servidor utiliza. Em vez disso, o DHCP fornece um mecanismo, baseado em comunicação cliente/servidor, que permite que um computador cliente se junte a uma nova rede e obtenha um endereço IP a partir de um computador servidor de endereços sem intervenção manual. O conceito foi chamado de plug-and-play networing. Mais importante, o DHCP acomoda computadores que executam software servidor e computadores que executam software cliente: • Quando um computador que executa software cliente é movido para uma nova rede, o computador pode usar DHCP para obter informações de configuração sem intervenção manual. • O DHCP permite que a um computador que não é móvel e executa software servidor seja atribuído um endereço IP permanente; o endereço não mudará quando o computador for reiniciado. Para entender estas possibilidades de atribuição de endereços para estes dois tipos de computadores, é importante observarmos os tipos de alocação que o DHCP aceita, que podem ser alocação estática ou dinâmica. Alocação de endereço estática Nessa função, o DHCP possui um banco de dados que vincula endereços físicos (MAC) a endereços IP de maneira estática. Este é o tipo de alocação adequada para aqueles computadores ou dispositivos que executam software servidor. Isto porque, como esta amarração do endereço físico com o endereço IP é estática, não muda, o dispositivo sempre terá o mesmo endereço IP mesmo que ele seja desligado e ligado diversas vezes. Este tipo de alocação é comumente usada em computadores servidores, nos quais os clientes precisam saber previamente seu endereço IP, e este não deve mudar com o tempo, pois implicaria na reconfiguração dos clientes no acesso ao servidor. Este cenário também se aplica a impressoras que possuem interface de rede LAN, nas quais é preciso configurar o protocolo de impressão com o endereço IP da impressora. Alocação de endereço dinâmica O DHCP tem um segundo banco de dados com um pool (faixa de valores) de endereços IP disponíveis. Este segundo banco de dados torna o DHCP dinâmico. Quando um cliente DHCP solicita um endereço IP, o servidor DHCP vai ao poolde endereços IP disponíveis (não utilizados) e atribui um endereço IP por um período de tempo negociável. Quando um cliente DHCP envia um pedido a um servidor DHCP, este verifica primeiro seu banco de dados estático. Se existir no banco de dados estático uma entrada com o endereço físico do cliente que fez a solicitação, o endereço IP permanente deste cliente é retornado. Por outro lado, se a entrada não existir no banco de dados estático, o servidor seleciona um endereço IP do pool de disponíveis, atribui o endereço ao cliente e adiciona a entrada no banco de dados dinâmico. O aspecto dinâmico do DHCP é necessário quando um host muda de uma rede para outra ou é conectado e desconectado de uma rede (como um assinante de um provedor de serviços). O protocolo DHCP fornece endereços IP temporários por um período de tempo limitado. Os endereços atribuídos do pool são temporários. O servidor DHCP faz uma concessão por um período de tempo específico. Quando a concessão expira, o cliente precisa parar de usar o endereço IP ou renovar a concessão. O servidor tem a escolha de concordar ou não com a renovação. Se o servidor discordar, o cliente para de usar o endereço. 1. Qual o principal benefício de se ter um servidor de DHCP em uma rede de computadores? 2. É possível fixar o endereço IP em um determinado dispositivo da rede mesmo se esta está usando o DHCP? 3. Pesquise na Internet o protocolo BOOTP e veja qual era sua principal finalidade. Estados de transição DHCP O cliente DHCP muda de um estado para outro, dependendo das mensagens que recebe ou envia. Veja na Figura 2 os vários estados em que o cliente pode estar; note que a transição de estados ocorre através de trocas de mensagens. Figura 2 – Diagrama de transição do DHCP. Fonte: Forouzan (2008). Veja que o protocolo usa também um esquema de timeout (uma espécie de cronômetro) para fazer concessões. Mas o que acontece em cada estado? Para que eles servem? Veremos a seguir como um estado se comunica com o outro e suas respectivas funções. Estado de inicialização Quando o cliente DHCP inicia pela primeira vez, ele está no estado inicializando. O cliente difunde publicamente (porbroadcast) uma mensagem DHCPDISCOVER (uma mensagem de pedido com a opção DHCPDISCOVER) usando a porta UDP 67. Estado selecionando Após enviar a mensagem DHCPDISCOVER, o cliente vai para o estado selecionando. Os servidores que podem fornecer este tipo de serviço respondem com uma mensagem DHCPOFFER. Nessas mensagens, os servidores oferecem um endereço IP. Eles também podem oferecer a duração da concessão. O padrão é 1 hora. O servidor que envia uma mensagem DHCPOFFER bloqueia o endereço IP oferecido para que ele não esteja disponível a outros clientes. O cliente escolhe uma das ofertas e envia uma mensagem DHCPREQUEST para o servidor selecionado. Então, ele vai para o estado solicitando. Entretanto, se o cliente não receber nenhuma mensagem DHCPOFFER, ele tenta mais quatro vezes, cada uma com duração de 2 segundos. Se não houver resposta a nenhuma dessas mensagens DHCPDISCOVER, o cliente fica em repouso por 5 minutos, antes de tentar novamente. Estado solicitando O cliente permanece no estado solicitando até receber uma mensagem DHCPACK do servidor que cria o vínculo entre o endereço físico do cliente e seu endereço IP. Após o recebimento da mensagem DHCPACK, o cliente vai para o estado vinculado. Estado vinculado Nesse estado, o cliente pode usar o endereço IP até que a concessão expire. Quando 50% do período de concessão for atingido, o cliente envia outra mensagem DHCPREQUEST para solicitar renovação. Então, ele vai para o estado renovando. Quando está no estado vinculado, o cliente também pode cancelar a concessão e ir para o estado inicializando. Estado renovando O cliente permanece no estado renovando até que um desses dois eventos aconteça. Ele pode receber uma mensagem DHCPACK, que renova o contrato de concessão. Nesse caso, o cliente zera seu cronômetro e volta para o estado vinculado. Ou então, se uma mensagem DHCPACK não for recebida e 87,5% do tempo de concessão expirou, o cliente volta para o estado revinculando. Estado revinculando O cliente permanece no estado revinculando até que um de três eventos ocorra. Se o cliente receber uma mensagem DHCPNACK ou a concessão expirar, ele volta para o estado inicializando e tenta obter outro endereço IP. Se o cliente receber uma mensagem DHCPACK, ele vai para o estado vinculado e zera o cronômetro. 1. Qual a primeira mensagem que um cliente envia ao tentar obter um endereço via DHCP? Ela é para um único computador? Por quê? 2. Ao entrar no estado renovando, um cliente fica aguardando a resposta do servidor. Caso essa resposta não chegue, qual o próximo estado a ser chamado? Baseado em quê ele toma esta decisão? Trocando mensagens Agora que conhecemos os estados e como as mensagens são trocadas entre eles, veremos um exemplo de como isso acontece para termos uma noção de como funciona tal procedimento. A Figura 3 mostra a troca de mensagens relacionada ao diagrama de estado de transição, como visto na Figura 2. Figura 3 – Sequência de troca de mensagens no DHCP. Fonte: Forouzan (2008). Instalando e configurando o servidor DHCP Agora vamos praticar os conceitos que vimos até agora. Imagine que temos duas máquinas na rede, chamadas máquina A e máquina B. A primeira coisa que precisamos é de um servidor de DHCP. O software servidor DHCP no Linux é feito através do daemon (processo servidor) dhcpd, e o mesmo vem distribuído no pacote dhcp3-server, conforme podemos observar no comando da Figura 4 (sudo apt-cache search dhcpd), que procura todos os pacotes que possuem alguma referência a este arquivo. É interessante que você, a partir de agora, siga a aula em um computador para poder realizar os comandos descritos a seguir e ter uma melhor ideia de como funcionam. Figura 4 – Pacotes do Linux que possuem alguma referência ao servidor DHCP Como podemos ver na saída de nossa busca, temos um cliente DHCP (dhcp3-client), um Servidor DHCP (dhcp3-server), uma ferramenta auxiliar ao tcpdump para análise de pacotes DHCP, entre outros. Como estamos querendo instalar um servidor DHCP, vamos instalar então este pacote dhcp3-server através do procedimento mostrado na Figura 5. O comando abaixo assume que a máquina possui conectividade com a Internet. Figura 5 – Instalando o servidor DHCP através do comando apt-get install Observe na Figura 5 que, ao ser instalado o pacote, o Linux tentou iniciar o servidor de DHCP e esta ação falhou. Isto aconteceu porque o serviço DHCP ainda não está configurado para a rede em que a máquina está. Assim, vamos configurá-lo. Arquivo de configuração do servidor DHCP O arquivo que contém a configuração do servidor DHCP é o /etc/dhcp3/dhcpd.conf. Abra-o com o editor gedit. Observe pelos comentários nas linhas que começam com “#” que ele está bastante documentado. A configuração neste arquivo é dividida em seções. Os parâmetros iniciais ou fora dos blocos subnet são definidos globalmente, isto é, independente de sub-redes. Os parâmetros definidos dentro de um bloco subnet só se aplicam àquela sub-rede em particular. Isto é importante quando se tem um servidor de DHCP com várias placas de redes ligadas a diferentes sub-redes, sendo ele o servidor DHCP, e em cada rede as informações de concessão de endereços são diferentes. Um exemplo de arquivo de configuração para um servidor de DHCP típico seria o da Figura 6. Figura 6 – Exemplo do arquivo de configuração dhcpd.conf Vamos explicar cada um destes parâmetros na Tabela 1. Parâmetro Linha Descrição server-identifier 01 Identifica o servidor de DHCP por um nome de computador. default-lease-time 02 Duração em segundos da concessão de um endereço IP a um computador. No exemplo, 1 hora. max-lease-time 03 Tempo máximo de concessão de um endereço IP. Após este tempo, se o computador não conseguir renovar esta concessão, ele deve liberar o endereço IP e tentar conseguir outro. subnet ... netmask ... { ... } 05 Bloco de parâmetros para a sub-rede especificada, incluindo sua máscara de rede. Os parâmetros definidos dentro deste bloco são específicos da sub-rede em questão. range IP_inicial IP_final 06 Faixa de endereços IP a serem utilizadas pela alocação dinâmica de endereços. Os endereços inicial e final da faixa de valores devem ser informados. option domain-name 08 Nome do domínio utilizado pelo serviço DNS, o qual será visto na próxima aula. option domain-name-servers 09 Lista dos endereços dos servidores de DNS separados por vírgula, caso haja mais de um. option routers 10 Lista dos endereços dos roteadores (gateways) separados por vírgula, caso haja mais de um. option subnet-mask 11 Máscara de sub-rede utilizada na rede em questão. host ... { ... } 12 Parâmetros específicos para um dispositivo qualquer da rede. Neste bloco é possível fazer a alocação estática de um endereço IP para um computador. hardware ethernet 13 Endereço físico (MAC) da interface de rede do host em questão. fixed-address 14 Endereço IP estático a ser vinculado ao host. option host-name 15 Nome do host na rede usada pelo serviço DNS. Não precisa ser o mesmo nome informado no parâmetro host. O nome, nesta opção, será o que aparecerá nos logs do servidor DHCP para as alocações ocorridas. Tabela 1 – Parâmetros de configuração do servidor DHCP Observe que os parâmetros devem ser finalizados com um ponto-e-vírgula. Os blocos devem iniciar com abre-parênteses e fecha-parênteses, como o bloco subnet entre as linhas 05 e 17, e o bloco host entre as linhas 12 e 16 da Figura 6. 1. Realize a instalação (sem configuração) de um servidor DHCP em uma máquina virtual como mostrado nesta sessão. Ela será necessária para nossa prática (configuração e testes) a seguir. Prática de configuração e testes de um servidor DHCP Em seu computador será necessário criar 2 máquinas virtuais para realização de nossos testes. Assumiremos que a máquina A será o servidor de DHCP (já instalado em nossa atividade da seção anterior), e a máquina B será o cliente. O endereço IP da máquina A é o 10.1.1.1 com máscara 255.255.255.0. Assim, a máquina B deverá conseguir um endereço IP nesta rede 10.1.1.0. Alocação dinâminca O primeiro teste que faremos será utilizando a alocação dinâmica. Isto é, a máquina B deverá pegar um endereço IP qualquer dentro da faixa de endereços configuradas no servidor DHCP da máquina A. A faixa que utilizaremos será a 10.1.1.100 a 10.1.1.200. Assumiremos também que o gateway (máquina intermediária geralmente destinada a interligar redes) desta rede será a máquina A e que ela também é o servidor de DNS para esta rede. Assim, o arquivo de configuração dhcpd.conf fica como o da Figura 7. Figura 7 – Arquivo dhcpd.conf para a alocação dinâmica Digite este texto da Figura 7 usando o gedit e salve-o como /etc/dhcp3/dhcpd.conf da máquina A. Em seguida, inicie o servidor de DHCP, conforme a Figura 8. Figura 8 – Configurando e iniciando o servidor DHCP para alocação dinâmica Vamos agora testar o servidor DHCP com a máquina B. Antes de ligarmos a máquina B, vamos parar o servidor DHCP na máquina A. Depois ligamos a máquina B. Ao final da inicialização da máquina B, se abríssemos um terminal, iríamos verificar que sua interface eth0 não possui nenhum IP! Depois iniciamos o servidor DHCP na máquina A, conforme Figura 9. Figura 9 – Parando e iniciando o servidor DHCP entre a inicialização da máquina B Na máquina B, vamos executar o comando dhclient, que é um software DHCP cliente, conforme a Figura 10. Veja que antes de executarmos esse comando a interface eth0 não tinha um endereço IP definido. Figura 10 – Rodando o software DHCP cliente na máquina B Observe, na Figura 10, a sequência de mensagens entre o cliente e o servidor DHCP, as quais correspondem às quatro primeiras mensagens da Figura 3, comentadas anteriormente. Isto é, o cliente envia por broadcast uma mensagem DHCPDISCOVER. O servidor DHCP, ao receber esta mensagem, verifica o primeiro IP disponível para alocação, neste caso 10.1.1.100, e envia uma mensagem DHCPOFFER com este IP para o cliente. O cliente aceita a oferta e envia uma mensagem DHCPREQUEST do IP 10.1.1.100 para o servidor. Por fim, o servidor confirma a alocação deste IP para este cliente com uma mensagem DHCPACK com este IP. Observe ainda que o cliente deverá realizar um pedido de renovação deste IP após 1385 segundos, conforme indicado pelo texto “renewal in 1385 seconds”. Alocação estática O segundo teste que faremos será utilizando a alocação estática. Isto é, a máquina B deverá pegar um endereço IP pré-configurado pelo servidor DHCP da máquina A, o qual observará o endereço físico (MAC) da máquina B para fazer esta amarração, ou seja, associar o IP ao seu MAC. O endereço IP que utilizaremos para esta alocação estática será o 10.1.1.2, portanto, fora da faixa de endereços IP de alocação dinâmica. É possível utilizar um endereço IP para alocação estática da faixa de endereços para alocação dinâmica? Sim. Isto é considerado como uma reserva de endereço IP dentro da faixa de endereços a serem alocados dinamicamente aos clientes. Ou seja, somente um determinado cliente poderá utilizar aquele IP, amarrando o seu endereço físico (MAC) com aquele endereço lógico (IP). Assim, o arquivo de configuração dhcpd.conf fica como o da Figura 11. Figura 11 – Arquivo dhcpd.conf para a alocação dinâmica Na máquina A, vamos atualizar o arquivo /etc/dhcp3/dhcpd.conf conforme a Figura 11. Em seguida, reiniciamos o servidor de DHCP, conforme Figura 12. Figura 12 – Atualizando a configuração e reiniciando o servidor DHCP Agora, executamos novamente o DHCP cliente na máquina B e observamos o que acontece, conforme a Figura 13. Figura 13 – Atualizando a alocação DHCP no cliente Observe, na Figura 13, que o cliente envia uma mensagem DHCPREQUEST do IP 10.1.1.2 para o servidor e este confirma a alocação para este IP enviando uma mensagem DHCPACK para o cliente. E por que o cliente solicitou justamente este endereço IP do servidor? No momento do reinício do servidor DHCP, este observou, pela nova configuração, que o cliente que já tinha um endereço IP alocado dinamicamente tem agora uma reserva de IP para ele. Isto provoca o envio pelo servidor de uma mensagem DHCPOFFER do endereço IP reservado para este cliente. O cliente poderá requisitar esta alocação deste novo IP com uma mensagem DHCPREQUEST quando expirar o tempo de concessão ou quando desejar renovar seu endereço, que foi o que aconteceu quando rodamos o comando dhclient. 1. Agora que você é capaz de instalar/configurar um servidor DHCP, analise os prós e contras em utilizar a alocação ESTÁTICA e DINÂMICA em uma rede de computadores. 2. Pesquise e cite exemplos de tipos/ambientes de redes em que uma abordagem de alocação será melhor empregada do que a outra. Resumo Nesta aula você aprendeu que existe um protocolo, chamado DHCP, para configurar as máquinas de uma rede automaticamente, no que diz respeito aos seus endereços IP, máscaras, gateways, entre outras informações. Você aprendeu também que o DHCP pode fornecer um endereço dinâmico para cada máquina, dentro de uma faixa de endereços configurados no servidor, ou pode sempre oferecer o mesmo endereço IP. Neste último caso, a vinculação do endereço IP a uma máquina é feita através do endereço MAC de sua placa de rede. Além disso, você estudou as mensagens que são trocadas pelo protocolo DHCP e aprendeu a configurar um servidor DHCP real. Acesse o Laboratório1 desta aula (Aula7) no Virtualbox e realize os seguintes procedimentos. 1. Repita todos os passos vistos nesta aula para configurar seu servidor DHCP na máquina A e o cliente DHCP na máquina B. 2. Execute o analisador de protocolos digitando tcpdump –n na janela de terminal da máquina B. 3. Na máquina A, edite o arquivo /etc/dhcp3/dhcpd.confmudando o IP reservado para a máquina B, colocando agora 10.1.1.3. Reinicie o servidor DHCP na máquina A. 4. Na máquina B, abra outra janela de terminal e execute o comando dhclient. Observe as linhas que aparecem na janela com o tcpdump sendo executado. 5. Na máquina B, na janela de terminal em que rodou o dhclient, observe as mensagens DHCP trocadas entre o cliente e o servidor. Quais foram estas mensagens e por que elas foram enviadas? Redes de Computadores Aula 9 – Autenticação e compartilhamento de arquivos – Parte I Com certeza você já está acostumado ao fato de que para utilizarmos um computador normalmente precisamos informar um nome de usuário e uma senha. Esse procedimento garante a nossa identificação e, com isso, permite que sejam aplicadas regras que controlam o que podemos fazer no computador, como, por exemplo, quais arquivos podemos acessar. Nas redes das empresas, onde existem diversos computadores que podem ser acessados por diferentes pessoas, esses mecanismos de autenticação e controle de acesso são indispensáveis. Além disso, como nessas redes uma pessoa pode usar diferentes computadores, é necessário que exista um mecanismo que disponibilize os arquivos desta pessoa na máquina onde ela estiver conectada em um dado momento. Nesta aula estudaremos um protocolo chamado LDAP, que é muito utilizado para implementar os mecanismos de autenticação. Após o final desta aula você será capaz de: • Instalar um servidor LDAP na Linux. • Configurar o servidor LDAP. • Saber inserir e consultar dados no servidor LDAP usando programas de linha de comando. • Saber inserir e consultar dados no servidor LDAP usando um programa com interface gráfica. Autenticação de usuários e compartilhamento de arquivos Nesta seção vamos estudar como funcionam os serviços de autenticação de usuários e de acesso aos seus arquivos. Inicialmente vamos ver como isso ocorre quando usamos uma única máquina, sem que ela esteja conectada em nenhuma rede. Depois veremos o que muda quando temos várias máquinas em rede. Na primeira aula da disciplina Sistemas de Conectividade, falamos sobre esses serviços de autenticação e compartilhamento. Se desejar, leia novamente a parte referente a este assunto daquela aula. Apenas uma máquina Vamos entender agora como funciona a autenticação e o acesso aos arquivos quando usamos uma única máquina. Suponha que essa máquina é um computador que você tem na sua casa. Como provavelmente um irmão ou um de seus pais pode também querer usar o computador, é importante que cada pessoa tenha privacidade para seus arquivos, ou seja, cada pessoa só possa acessar seus próprios arquivos. Além disso, cada um pode querer personalizar a área de trabalho, ou seja, qual papel de parede quer utilizar e os ícones que quer que apareçam na tela. Para que isso seja possível, é necessário primeiro ter como identificar cada pessoa (que é chamada de usuário), o que é feito através de um nome de usuário e uma senha. Depois é necessário definir o que cada usuário pode fazer na máquina (permissões) e quais arquivos pode acessar. Você pode dizer, por exemplo, que um usuário não pode acessar o CD (ou DVD), ou que não pode usar a impressora, mas normalmente a principal restrição é se ele pode ou não instalar programas na máquina. No que diz respeito aos arquivos, por padrão, cada usuário só pode acessar seus próprios arquivos. Naturalmente um usuário pode liberar o acesso a um ou mais de seus arquivos para outro usuário. Um conjunto de permissões é chamado de Lista de Controle de Acesso, ou ACL. Normalmente as ACLs são aplicadas a cada usuário individualmente. Para simplificar as coisas, são criados grupos e os usuários são inseridos nesses grupos. As ACLs são então definidas para os grupos, e consequentemente são aplicadas a todos os usuários de cada grupo. Em uma máquina tipicamente existem pelo menos dois grupos, um de usuários com direitos normais e outro para usuários com direitos de administrador. Os usuários desse último grupo, administrador, normalmente têm acesso completo à máquina, ou seja, podem realizar qualquer operação, enquanto que usuários de outros grupos têm suas permissões reduzidas. Figura 1 - Grupos de usuários: grupo comum e administradores A Figura 1 mostra dois grupos, um chamado “alunos” e outro “administradores”. Observe que é possível um mesmo usuário pertencer a mais de um grupo, como é o caso do usuário “Pedro”. Naturalmente, neste caso os direitos dele serão obtidos considerando-se as permissões de todos os grupos aos quais ele pertence. A Figura 2 mostra a aplicação de uma ACL para um grupo (alunos) e de outra ACL para um usuário individual. No primeiro caso, as regras de acesso contidas na ACL são aplicadas a cada usuário do grupo, mas de um modo muito mais fácil de fazer do que se fosse necessário fazer isso individualmente para cada um deles. Olhando para Figura 2, observe que “Pedro” terá permissão de instalar programas, pois embora essa permissão não lhe tenha sido atribuída pelas permissões do grupo, foi-lhe concedida pela atribuição das permissões individuais. Figura 2 - Atribuição de permissões a um grupo de usuários e a um único usuário Independentemente se as permissões são concedidas a um grupo ou a um usuário individual, o ponto mais importante para esta aula é que, no caso de uma máquina individual, as informações para autenticação do usuário (nome do usuário e senha), as permissões (ALCs) e os arquivos que o usuário pretende acessar estão todos na mesma máquina. Sendo assim, após a pessoa que vai utilizar a máquina informar seu nome de usuário (username) e sua senha, o sistema operacional verifica se seu usuário existe e se a senha informada está correta Após isso, sempre que o usuário tentar fazer alguma operação no computador, como acessar um arquivo, por exemplo, o sistema operacional verifica através das ACLs se o usuário tem permissão para realizar a operação. No caso do Linux, as informações dos usuários, como seu username, por exemplo, estão no arquivo /etc/passwd, com exceção das senhas, que estão no arquivo /etc/shadow. 1. O que é uma ACL? 2. O que acontece se uma ACLs for atribuída a um grupo de usuários? 3. Suponha que você é casado e tem um filho de 8 anos que sabe mexer bastante no computador, mas você não quer que ele instale nenhum tipo de programa na máquina. No entanto, os outros membros da família podem continuar instalando o que quiserem. Indique o que fazer usando ACLs. Máquinas em uma rede Vamos agora analisar o caso onde existem várias máquinas na rede que o usuário pode utilizar. Isso acontece em várias empresas. Pense um laboratório de informática de uma escola onde os alunos vão estudar, por exemplo. Faça de conta que você é um desses alunos. Evidentemente, em cada dia você poderia utilizar uma máquina diferente. Não dá para ter as informações sobre os usuários (username e senhas) nem seus arquivos gravados em apenas uma máquina, como é feito para uma máquina que não está em rede. Veja que também não faria sentido gravar uma cópia dessas informações em cada máquina, pois sempre que alguma informação fosse alterada na máquina que você estivesse trabalhando, todas as outras máquinas ficariam desatualizadas. A solução é centralizar todas essas informações em uma única máquina (chamada servidor) e instalar um programa nas máquinas que os usuários vão utilizar (chamadas clientes) que dê a impressão de que tudo está acontecendo apenas na máquina local, onde ele está trabalhando. Vamos agora ver o que “dar a impressão de que tudo está acontecendo na máquina local” significa, em termos de autenticação e do acesso aos arquivos. Autenticação Os nomes dos usuários (usernames) e suas senhas ficam gravados em um arquivo no servidor. Nos clientes o programa que verifica o username e senha dos usuários sabe disso, e sempre que uma pessoa tenta utilizar a máquina esse programa envia o username e a senha informados para o servidor. O servidor verifica no seu arquivo de usuários se a senha informada para aquele usuário está correta, e envia uma mensagem de volta ao cliente liberando ou negando o acesso. A Figura 3 mostra o que acontece quando o usuário Carlos (que possui a senha teste123) tenta se logar (entrar) em uma máquina da rede. Como ele informou a senha correta, no passo 4 o servidor envia uma mensagem informando que ele “autenticou com sucesso”, pois a senha informada é a mesma existente no arquivo do servidor, e o acesso à máquina seria liberado. Figura 3 - Esquema de autenticação de usuários em uma rede Compartilhamento de arquivos Vamos agora ver como os arquivos do usuário que estão no servidor são acessados. Do mesmo modo que existe o software de autenticação que é composto por uma parte que executa no servidor e outra que executa no cliente, existe também um software para acesso aos arquivos em rede, que é composto por uma parte que executa no servidor e outra que executa no cliente. Observe que não estamos dizendo que o usuário vai ter que usar um programa especial para acessar seus arquivos. Nada disso! Esse programa faz parte do próprio sistema operacional e cria uma espécie de disco virtual na máquina do cliente. Quando o usuário Carlos tenta, por exemplo, ler alguma informação do arquivo redes.doc (passo 1), uma mensagem solicitando essa operação é enviada ao servidor (passo 2), que faz o acesso ao disco real (passo 3). Neste passo as ACLs são verificadas para ver se o usuário Carlos tem permissão de acessar este arquivo. Caso tenha permissão, no passo 4 o conteúdo do arquivo é enviado para a máquina cliente, e finalmente passado para a aplicação (passo 5). Veja, portanto, que para a aplicação que tentou ler o arquivo não existe nenhuma diferença se o arquivo estava na máquina cliente ou no servidor. É como se existisse realmente um disco na máquina cliente com os arquivos do usuário, mas na verdade ele não existe – daí o nome virtual. Se a máquina cliente for Windows, realmente vai aparecer um novo disco, identificado com uma letra de drive, por exemplo, “D:” ou “F:”. Se o cliente for um Linux, os arquivos e pastas do servidor vão aparecer dentro de alguma pasta existente na máquina local, que foi criada apenas para esta finalidade. A Figura 4 mostra o que acontece quando o usuário Carlos tenta acessar um de seus arquivos (redes.doc) em um servidor Linux a partir de uma máquina cliente. Observe que os arquivos (e pastas) de todos os usuários, incluindo os de Carlos, estão no disco rígido que existe no servidor, dentro da pasta home. Entretanto, o sistema operacional da máquina cliente mostra a relação de arquivos e pastas do servidor como se eles fossem locais. Figura 4 - Compartilhamento de arquivos 1. Explique qual a vantagem oferecida por uma rede que dispõe o serviço de compartilhamento de arquivos. 2. Por que usar autenticação para ingressar (logar) nas máquinas da rede? É importante você saber que a forma como as pastas de Carlos aparecem na máquina cliente depende se esta máquina é Linux ou Windows. Supondo que as pastas de Carlos no servidor fossem “documentos”, “programas” e “vídeos”, a Figura 5 mostra um exemplo de como essas pastas apareceriam se a máquina cliente fosse Windows. Veja que foi criado um disco identificado pela letra “F:”, e as pastas aparecem dentro deste disco. Figura 5 - Como as pastas remotas aparecem em um cliente Windows Assumindo as mesmas pastas de Carlos no servidor, a Figura 6 mostra como essas pastas apareceriam no cliente se a máquina fosse Linux. Observe que as pastas remotas aparecem dentro da pasta Carlos, que foi criada no cliente apenas para essa finalidade. Além disso, veja que neste esquema as pastas remotas “documentos”, “programas” e “vídeos” aparecem completamente integradas às pastas locais da máquina, que, neste caso, são “backup”, “dados” e “musicas”. Figura 6 - Como as pastas remotas aparecem em um cliente Linux 1. Onde ficam os arquivos com os nomes dos usuários e as senhas em uma rede? 2. Como as pastas remotas aparecem na máquina cliente se o usuário estiver utilizando Windows? LDAP Para realizar a autenticação dos usuários em uma rede, falamos que é utilizado um programa composto por uma parte que roda no cliente e outra que roda no servidor. Naturalmente estes programas utilizam algum protocolo para se comunicarem. Existem diversos mecanismos de autenticação que, embora sigam o modelo que você acabou de estudar, utilizam protocolos diferentes. É por isso que, a princípio, um cliente Windows só consegue se autenticar em um servidor Windows, e um cliente Linux em um servidor Linux. Felizmente existem formas de reduzir essas limitações, fazendo, por exemplo, um cliente Windows se autenticar em um servidor Linux. Na próxima aula iremos aprender a fazer a autenticação de um cliente Linux em um servidor Linux. Embora outros protocolos já tenham sido utilizados para realizar esta tarefa, como o NIS, por exemplo, atualmente a forma mais comum de fazer isso é utilizando o protocolo LDAP. Mas fique sabendo que o LDAP é um protocolo que pode ser utilizado para diversas outras finalidades, além da autenticação. Por isso, nesta aula vamos falar sobre o LDAP de modo geral, para na próxima aula você aprender como utilizá-lo para a autenticação. Definição e esquemas O LDAP (Lightweight Directory Access Protocol) é um protocolo para acesso a informações remotas que são organizadas de forma estruturada. A melhor forma de entender o que se quer dizer com “estruturada” é através de um exemplo. Imagine que as informações que você quer disponibilizar sejam referentes aos filmes de uma locadora de DVDs. Para cada filme você iria estruturar as informações sobre ele mais ou menos do modo mostrado na Figura 7, criando uma espécie de modelo, ou formulário, que o LDAP chama de “Esquema”. Figura 7 - Esquema LDAP para informações sobre filmes Cada esquema, além dos nomes dos campos, define o tipo de cada campo. Um campo pode ser, por exemplo, uma string, e outro campo pode ser um inteiro. Além disso, um esquema pode dizer que alguns campos são opcionais e outros são obrigatórios. Assim, quando se vai inserir um objeto de um determinado esquema é necessário informar todos os campos obrigatórios. Por objeto entenda o conjunto de informações referentes a um esquema. Para o esquema DVD, um objeto seria o conjunto de dados de um filme qualquer. Como outro exemplo de esquema, vamos pegar algumas informações que o sistema operacional precisa manter sobre um usuário. O esquema do LDAP para armazenar essas informações é mostrado na Figura 8. Veja que o campo “Nome Completo” não foi informado no novo objeto sendo inserido. Isso é possível caso este campo seja declarado como opcional na definição do esquema Usuário. Figura 8 - Esquema LDAP para informações dos usuários e um objeto exemplo O programa que armazena as informações do LDAP é chamado de “Diretório”, mas como esse nome pode não ser muito claro, é melhor você pensar no LDAP como uma base de dados, ou seja, algo que armazena um conjunto de informações. Todas as informações armazenadas devem ser de um dos tipos de esquemas suportados pelo servidor. Cada servidor define os esquemas que quer suportar. Existem vários esquemas predeterminados, mas é possível criar novos esquemas com os campos que desejarmos. Estrutura em árvore Até aqui você já sabe que o LDAP é um protocolo para acessar informações e que essas informações são organizadas de acordo com algum esquema. Assim sendo, sabe também que um servidor LDAP contém vários objetos de diversos esquemas diferentes. Vamos agora descrever mais uma característica do LDAP que o torna extremamente interessante. Uma base LDAP é organizada em árvore, e os objetos podem ser inseridos em qualquer nó da árvore. Além disso, cada nó tem um nome que é único nessa árvore. A característica descrita acima traz duas grandes vantagens: • Eficiência nas pesquisas. Quando se deseja procurar um objeto, pode-se fazer a busca apenas em um nó específico ou em uma subárvore (iniciando em um determinado nó). • Descentralização. Cada parte da árvore pode ser armazenada em uma máquina diferente, mas todos continuam vendo a árvore inteira. Vamos analisar melhor essas questões. Imagine uma universidade como a UFRN, que tem milhares de alunos e possui várias unidades em diversas cidades, como Natal, Caicó e Mossoró, por exemplo. Em cada uma dessas unidades existem diversos cursos. Para armazenarmos as informações dos usuários da UFRN numa base LDAP, seria interessante organizarmos essas informações em uma árvore LDAP, como a mostrada na Figura 9. Cada nó possui um nome abreviado, que se encontra mostrado dentro de cada círculo na Figura 9, e um nome completo, que para alguns nós está mostrado próximo ao círculo. Veja que o nó raiz é nomeado com “o=ufrn”, onde esse “o” significa organização. Os demais nós se chamam “ou=XXX”, onde XXX é o nome abreviado do nó. A sigla “ou” significaOrganization Unit (Unidade Organizacional). A regra é que você deve nomear o nó raiz da sua árvore com “o” e todos os demais abaixo dele com “ou”. Veja também que o nome de cada nó inclui o nome de todos os nós a partir dele até a raiz. Figura 9 - Árvore LDAP Esta árvore pode, então, ser utilizada para armazenar objetos do esquema “usuários” contendo as informações dos seus alunos. O objeto referente a cada aluno pode ser armazenado no nó do curso que o aluno pertence. Por exemplo, um aluno do curso de Direito da unidade Natal seria armazenado no nó “ou=direito,ou=natal,o=ufrn” enquanto um aluno do curso de Direito de Mossoró seria armazenado no nó “ou=direito,ou=mossoro,o=ufrn”. Desse modo, quando se deseja pesquisar por um aluno, sabendo que curso ele faz, basta realizar a busca indicando em que nó da árvore ela deve ocorrer. Caso não se saiba a qual curso o aluno pertence, a busca pode ser feita a partir do nó raiz da árvore (o=ufrn), passando por todos os nós filhos. No que diz respeito à descentralização, cada unidade poderia manter seu próprio servidor LDAP sendo responsável por manter a subárvore que inicia com o nó que possui o nome da unidade, mas todos os servidores veriam a árvore como ela é mostrada na Figura 9. 1. Qual é o nome completo do nó "Medicina" na Figura 9? Identificação única de cada objeto Cada esquema LDAP pode definir os campos que desejar, mas todos possuem obrigatoriamente um campo chamado “dn” (Distinguished Name – Nome Distinto), que é uma forma de identificar unicamente o objeto em toda a árvore. Portanto, o valor do campo dn de cada objeto na árvore deve ser diferente. Para garantir isso, normalmente o valor utilizado neste campo é o nome e valor de algum campo que é único dentro do nó da árvore onde o objeto está armazenado, concatenado com o nome do nó. Para as informações dos alunos que usam o esquema “usuários”, o valor desse campo que precisa ser único dentro de cada nó poderia ser o campo “username”. Desse modo, o campo dn do usuário Carlos, do curso de Direito de Natal, seria “username=carlos,ou=direito,ou=natal,o=ufrn”. Arquivos LDIF As operações em um servidor LDAP podem ser feitas utilizando-se aplicações que manipulam arquivos textos que possuem um formato especial, chamado LDIF. Embora o formato exato de um arquivo LDIF dependa do tipo de operação que se deseje realizar, a Figura 10 mostra um exemplo de um arquivo para alterar o e-mail do usuário Carlos. Figura 10 - Arquivo LDIF LDAP na prática Vamos ver agora como instalar um servidor LDAP na prática. Usaremos o programa Openldap, que é uma implementação do LDAP de código aberto, e faremos a instalação em uma máquina Linux. Para instalar o pacote Openldap no Linux utilize o comando mostrado na Figura 11. O servidor LDAP está contido no pacote slapd, mas o pacote ldap-utils contém vários programas para realizar operações no servidor. Portanto, ele será necessário, por exemplo, para inserirmos objetos no servidor. Figura 11 - Instalando o Openldap no Linux Para configurar o servidor, precisaremos realizar três passos, que são: • Informar ao servidor os esquemas que pretendemos suportar. Isso define quais tipos de objetos podem ser inseridos no servidor. • Criar o arquivo de configuração e ativar a configuração. • Criar os nós da árvore LDAP. Após esses passos as informações a serem armazenadas podem ser inseridas nos nós da árvore LDAP. Antigamente o LDAP era configurado através de um arquivo chamado slapd.conf. Embora este método ainda possa ser usado, ele está obsoleto. A forma recomendada atualmente para configurar um servidor LDAP é a descrita nesta aula. Informar ao servidor os esquemas suportados É necessário avisar ao servidor quais esquemas são suportados. Naturalmente, isso vai depender de para que você vai utilizar o LDAP. Vamos inserir três esquemas que já vêm com o LDAP e são muito usados para armazenar informações dos usuários Linux. Os esquemas são cosine.ldif, nis.ldif e inetorgperson.ldif. A seguir, mostramos os três comandos (que devem ser executados como root) para instalar esses esquemas. Criar o arquivo de configuração e ativar a configuração Vamos agora criar o arquivo de configuração do servidor. Apesar do arquivo possuir muitos parâmetros, você realmente precisa alterar poucos desses parâmetros. Como um deles será a senha do administrador do LDAP, antes de alterarmos o arquivo vamos gerar uma senha. Isso pode ser feito com o comando slappaswd, conforme mostrado na Figura 12. No exemplo, a senha informada foi teste123, o que gerou a senha criptografada “{SSHA}vVi6+5ZybDaVo90pIXg9XqF1pYkk67+s”. Perceba que a senha criptografada depende dos caracteres usados na senha. Figura 12 - Gerando senha para o arquivo de configuração do LDAP Na Figura 13 mostramos um arquivo de configuração com os objetos e seus valores que foi criado para a árvore mostrada na Figura 9, e você pode utilizar como base para o seu servidor LDAP. Os únicos valores que você precisa alterar estão marcados em vermelho e significam: • olcSuffix: o=ufrn. O nome do nó raiz da sua árvore. • olcRootDN: cn=admin,o=ufrn. O nome do usuário administrador do LDAP. Você usará esse nome sempre que for realizar operações no LDAP que precisem de privilégios de administrador. Basta alterar valor o=ufrn para o valor que tiver utilizado no campo olcSuffix (descrito no item anterior). • olcRootPW: {SSHA}vVi6+5ZybDaVo90pIXg9XqF1pYkk67+s. A senha do usuário administrador do LDAP definido no item anterior. Coloque aqui o valor obtido com o comando slappasswd, conforme mostrado na Figura 12. • olcAccess. São as permissões para o usuário administrador do LDAP tem. Portanto, onde aparecedn="cn=admin,o=ufrn", coloque o mesmo valor que informou para o campo olcRootDN, explicado anteriormente. Figura 13 - Arquivo de configuração do servidor LDAP Salve este arquivo (com suas modificações) no diretório /etc/ldap com o nome ldapconfig.ldif e digite o seguinte comando (como root) para ativar suas configurações. Criar os nós da árvore LDAP Como última etapa para terminar a criação da nossa base LDAP, vamos criar os nós da árvore. Posteriormente, as aplicações que forem utilizar essa base LDAP vão inserir os objetos contendo as informações a serem armazenadas dentro desses nós. Como exemplo, vamos criar os nós “ufrn”, “Caicó”, “Natal” e “Mossoró”. A criação desses nós também é feita utilizando um arquivo ldif, que está mostrado na Figura 13. As primeiras quatro linhas definem o nó raiz (isso é indicado pelo campo objectClass: top). O campo “objectClass: organization” define que o nó é do tipo Organização (“o”). O campo “o” define o nome abreviado desse nó, e o campo “dn”,além de definir o nome completo do nó (que é único), indica em que posição da árvore o nó se encontra. Como o valor de dn (o=ufrn) é o próprio nome do nó, significa que não existe ninguém acima dele – como deve ser, já que ele é o nó raiz. Cada grupo de três linhas define um nó abaixo da raiz. Cada nó é do tipo “ou”, conforme indicado pelo campo “objectClass: organizationalUnit”. O valor do campo “ou” indica o nome abreviado do nó, e o campo dn indica tanto o nome completo (que é único) quanto a posição onde o nó se encontra na árvore. A posição é calculada retirando-se o valor do nome abreviado do valor do campo dn. Assim, para o nó “caico”, que possui o nome abreviado “ou=caico”, se retirarmos isso do valor do dn “ou=caico,o=ufrn”, temos que esse nó fica abaixo do nó com o dn “o=ufrn”, que é o nó raiz. Figura 14 - Arquivo LDIF para criar os nós da árvore Se quiséssemos inserir o nó “Medicina” abaixo do nó “Mossoró”, bastava inserir as linhas mostradas na Figura 15 no arquivo mostrado na Figura 14. Lembre-se que deve existir uma linha em branco entre a definição de dois nós da árvore. Figura 15 - Inserindo o nó Medicina abaixo do nó Mossoró Salve o arquivo mostrado na Figura 14 como cria_arvore.ldif na pasta /etc/ldap. Agora que o arquivo ldif está criado, basta executar o seguinte comando para que os nós da árvore sejam criados. Será pedida a senha do usuário “cn=admin,o=ufrn”, que foi o usuário que você informou no arquivo de configuração do servidor LDAP (parâmetro olcRootDN), mostrado na Figura 12. Portanto, informe a senha colocada neste mesmo arquivo (no parâmetro olcRootPW), que foi gerada com o comando slappasswd, mostrado na Figura 12. Se você informar a senha errada, aparecerá a mensagem “ldap_bind: Invalid credentials (49)”. Feito isso, seu servidor está pronto para ser utilizado! Se quiser verificar como está sua árvore, você pode digitar o comando ldapsearch, conforme mostrado na Figura 16. Figura 16 - Mostrando os nós criados O ldapsearch mostra o conteúdo do servidor incluindo os nós da árvore e os objetos armazenados nesses nós. No nosso caso, só apareceram os nós porque ainda não temos nenhum objeto armazenado dentro deles. Além disso, observe que o ldapadd pode ser utilizado para inserir os objetos dentro dos nós. O que determina o tipo de informação inserida é o conteúdo do arquivo ldif! 1. O que significam os parâmetros olcRootDN eolcRootPW no arquivo de configuração do servidor LDAP? 2. Na árvore LDAP criada, quantas e quais são as unidades organizacionais? 3. Quais são os parâmetros que não podem faltar para se criar uma árvore LDAP? Inserindo objetos dentro dos nós da árvore Nós vimos até agora como criar uma árvore LDAP. Nesta seção será mostrado o procedimento para inserir objetos dentro dos nós da árvore. Podemos ver que é semelhante ao procedimento de criar os nós. Basta criar um arquivo ldif e executar o comando ldapadd. Naturalmente, os dados contidos no arquivo ldif são referentes ao objeto que será incluído no diretório. Ou seja, os campos a serem informados dependem do esquema ao qual o objeto pertence. Na listagem a seguir, o objeto a ser criado pertence aos esquemas inetOrgPerson, posixAccount, shadowAccount. Sabemos disso olhando os valores dos campos objectClass. Portanto, cada campo informado é de um desses esquemas. Figura 17 - Arquivo LDIF para inserção de um objeto dentro de um nó da árvore Supondo que este arquivo foi salvo com o nome cria_usuario.ldif na pasta /etc/ldap, bastaria digitar o comando a seguir para que o objeto contendo os dados do usuário fossem inseridos no LDAP. Acessando o LDAP graficamente Embora tenhamos mostrado que é possível utilizar a base LDAP através de comandos digitados no terminal, existem ferramentas gráficas (chamadas LDAP Browsers) para realizar todas as operações. Normalmente o processo de criação da base de dados é realizado do modo que lhe mostramos, ou seja, através de comandos. Depois, normalmente usamos uma ferramenta gráfica, como o Jxplorer, cuja tela inicial é mostrada na Figura 18. Figura 18 - Jxplorer: programa para administrar um servidor LDAP graficamente A primeira coisa a fazer é conectar no servidor LDAP. Para isso, clique no menu “File”e depois em “Connect”. Será então mostrada a tela da Figura 18. O campo Host deve ser preenchido com o endereço IP do servidor ou seu nome de DNS. O servidor LDAP utiliza o protocolo TCP e espera conexões na porta 389. Por isso, digite 389 no campo Port. Base DN é o nó da árvore a partir do qual queremos ver as informações após a conexão. User Level indica o tipo de autenticação utilizado para se conectar no servidor. Caso não esteja utilizando criptografia, informe “User + Password”. Nos camposUser DN e Password informe os dados do administrador do LDAP. Depois clique em OK para conectar no servidor. Figura 19 - Tela de login do Jxplorer Após a conexão será mostrada a tela da Figura 19. Veja que para o nosso servidor de exemplo já aparecem os nós da árvore criada. Clicando sobre um nó, na parte direita aparecem os campos que compõem o nó. Não entraremos em maiores detalhes sobre a interface deste programa, mas em “Schema” você pode ver todos os esquemas que este servidor suporta, e os campos que existem em cada um. Veja por exemplo o esquema posixAccount, que será usado para a autenticação dos usuários. Figura 20 - Dados do servidor LDAP exemplo LDAP x bancos relacionais (SQL) Caso você já saiba o que é um banco de dados relacional (um banco SQL), pode estar se perguntando qual a diferença entre ele e o LDAP. Resumidamente, podemos ressaltar três pontos principais: • Um banco SQL armazena suas informações em tabelas, enquanto no LDAP elas são armazenadas em forma de árvore. • Embora um diretório LDAP suporte atualizações, ele é otimizado para operações de leitura. Portanto, se o número de modificações na sua base de dados é muito alto, seria mais indicado utilizar uma base SQL que uma LDAP. • Várias aplicações já suportam LDAP, e existem esquemas criados especificamente para essas aplicações. Cada vez mais o LDAP está se tornando o padrão para o armazenamento de dados de diversas aplicações, como e-mail, browsers, DNS, entre outras. Um browser, por exemplo, pode guardar sua lista de sites favoritos em um servidor LDAP. 1. Após aprender as duas formas de efetuar a instalação e a configuração do LDAP, com qual delas vocês se familiarizou mais? Você acha que uma tem vantagem em relação a outra? Com isso, finalizamos essa aula na expectativa de que você tenha aprendido os conceitos que foram abordados. Para expandir os conhecimentos sugerimos que você faça pesquisas nos sites de buscas da Internet sobre os assuntos estudados, tendo sempre o cuidado de acessar materiais de fontes confiáveis. Resumo Nesta aula você aprendeu que em uma rede é necessário que existam os serviços de autenticação de usuários e de compartilhamento de arquivos para que o usuário possa utilizar qualquer máquina da rede de modo transparente. Viu que estes serviços utilizam um programa no cliente e outro no servidor e que esses programas se comunicam usando algum protocolo. No caso da autenticação, aprendeu que o LDAP é um dos protocolos mais usados para essa finalidade. Nesta aula você aprendeu que o LDAP pode ser usado também para qualquer outra finalidade onde seja necessário armazenar e consultar informações de modo centralizado. Aprendeu a instalar e configurar o servidor Openldap no Linux e a usar ferramentas em modo texto e com interface gráfica para manipular a base LDAP. Acesse o Laboratório1 desta aula (Aula 9) no Virtualbox e realize os procedimentos descritos a seguir. 1. Instale um servidor LDAP na máquina-A e crie uma árvore contendo um nó raiz, chamado “o=metropoledigital”, e dois nós filhos da raiz, chamados “ou=cursos” e “ou=pessoas”. 2. Insira no nó “ou=pessoas” um objeto semelhante ao criado na Figura 16, com a diferença que a pessoa se chama “Maria”. 3. Instale o jxplorer e verifique se o objeto do usuário criado no passo anterior foi realmente incluído no nó "ou=pessoas". Redes de Computadores Aula 10 – Autenticação e compartilhamento de arquivos: parte II Na aula passada você aprendeu sobre a necessidade de um serviço de autenticação e compartilhamento de arquivos e estudou a teoria sobre esses serviços. Além disso, viu que o LDAP é um dos protocolos mais utilizados para implementar o serviço de autenticação. Sobre o LDAP você aprendeu o protocolo e viu como instalar e configurar um servidor, mas não se preocupou muito com a finalidade para a qual ele seria utilizado. Nesta aula, você vai aprender a usar o LDAP para criar o serviço de autenticação. Vai aprender também a configurar o serviço de compartilhamento de arquivos usando o protocolo NFS. Usaremos LDAP+NFS para que clientes Linux autentiquem e acessem arquivos de um servidor Linux. Ao final desta aula você será capaz de: • Criar um serviço de autenticação usando LDAP, onde máquinas cientes Linux se autenticam em um servidor Linux. • Criar um serviço de compartilhamento de arquivos no Linux usando NFS, de modo que os arquivos dos usuários que estão no servidor possam ser acessados da máquina do cliente. Usando o LDAP para autenticação Vamos agora ver como utilizar um servidor LDAP para armazenar as informações sobre os usuários e autenticá-los quando tentarem utilizar uma máquina cliente. Estrutura da árvore LDAP para autenticação de usuários Na aula passada vimos um exemplo de uma árvore LDAP, onde tínhamos um nó raiz do tipo “o” para representar a organização no qual usamos “o=ufrn”. Todos os outros nós da árvore eram nós do tipo “ou” e representavam lugares ou cursos. Como você vê, uma OU pode representar qualquer coisa. Quando vamos usar o LDAP para autenticar usuários é muito comum criarmos mais três nós do tipo “ou” abaixo de cada nó que representa o lugar onde os usuários serão cadastrados. Esses três novos nós são “Pessoas”, “Grupos” e “Máquinas”, e serão utilizados para armazenarem, respectivamente, os objetos que representam os usuários, os grupos de usuários e os computadores. Para esta aula vamos usar como árvore LDAP a árvore mostrada na Figura 1. Esta árvore é muito semelhante à utilizada na aula passada, com a diferença que eliminamos os nós referentes aos cursos. Portanto, os alunos de todos os cursos de cada unidade serão cadastrados no nó da unidade. Veja que só fizemos isso para a árvore ficar menor e simplificar a nossa explicação. Em um caso real provavelmente deixaríamos os nós com os nomes dos cursos! Observe também que acrescentamos os três novos nós citados no parágrafo anterior abaixo do nó de cada unidade. Figura 1 - Árvore LDAP usada nesta aula Para criar esses novos nós poderíamos fazer o seguinte: Todas as informações da sua base LDAP, incluindo os nós e os dados inseridos dentro de cada nó, são armazenadas em arquivos no diretório /var/lib/ldap. Portanto, se quiser apagar toda sua base e começar uma nova, faça o seguinte: • Pare o serviço LDAP com o comando: /etc/init.d/slapd stop • Apague todos os arquivos da pasta /var/lib/ldap com o comando: rm /var/lib/ldap/* • Inicie serviço LDAP novamente com o comando: /etc/init.d/slapd start Vamos assumir para o texto desta aula que um servidor LDAP está instalado (com a árvore mostrada na Figura1) em uma máquina com endereço IP 10.1.1.1. Desse modo, sempre que você vir este endereço em algum lugar nessa aula é porque o endereço IP do servidor LDAP deve ser informado. Simplificando a gerência de usuários e grupos Na aula passada você também aprendeu a inserir usuários na sua base LDAP. Porém, você viu como fazer isso criando um arquivo ldif com as informações dos usuários e usando o programa ldapadd para ler esse arquivo e inserir as informações. Existe uma forma muito mais simples de manipular usuários e grupos, que é utilizar os programas de um pacote chamado ldapscripts. 1. Para instalar esse pacote, digite: apt-get install ldapscripts Os programas do pacote ldapscripts utilizam um arquivo de configuração chamado ldapcripts.conf, que fica no diretório /etc/ldapscripts. 2. Edite este arquivo de acordo com a configuração do seu servidor LDAP. Na Figura 2 mostramos os valores usados para nosso servidor LDAP de exemplo. Figura 2 - Arquivo ldapscripts.conf Os três primeiros parâmetros informam, respectivamente, o IP do servidor LDAP, o usuário administrador e um arquivo que contenha a senha desse usuário. Explicaremos em breve como criar esse arquivo. Os parâmetros GSUFFIX, USUFFIX e MSUFFIX indicam, respectivamente, os nomes dos nós onde os grupos, os usuários e as máquinas serão inseridos. O valor do parâmetro SUFFIX será concatenado aos parâmetros anteriores para formar o nome completo daqueles nós. Ou seja, um usuário, por exemplo, será inserido no nó “ou=pessoas,ou=natal,o=ufrn”, e um grupo será inserido em “ou=grupos,ou=natal,o=ufrn”. Os parâmetros GIDSTAR, UIDSTART e MIDSTAR indicam, respectivamente, os identificadores numéricos iniciais que serão atribuídos aos grupos, usuários e as máquinas. O primeiro usuário, por exemplo, terá o identificador 10000, o segundo usuário 10001 e assim sucessivamente. A última linha fará com que durante o cadastro de novos usuários seja solicitada uma senha para ele. Para criar o arquivo contendo a senha do administrador do LDAP (cn=admin,o=ufrn) digite os comandos a seguir, substituindosenha pela senha desse usuário. Essa senha é a mesma senha que foi colocada no parâmetro olcRootDN do arquivo de configuração do servidor (veja Figura 13 da Aula 9). sudo sh -c "echo -n 'senha' > /etc/ldapscripts/ldapscripts.passwd" sudo chmod 400 /etc/ldapscripts/ldapscripts.passwd Agora basta utilizar um dos programas do pacote ldapscripts para manipular usuários e grupos. Os comandos devem ser digitados pelo usuário root, pois eles precisam ler o arquivo ldapscripts.passwd. A Tabela 1 mostra exemplos dos comandos mais utilizados e o seu significado. Comando Significado ldapadduser maria alunos Cria o usuário maria e o insere no grupoalunos ldapsetpasswd maria Troca a senha do usuário maria ldapdeleteuser maria Exclui o usuário maria ldapaddgroup teste Cria o grupo teste ldapdeletegroup tese Exclui o grupo teste ldapaddusertogroup maria teste Insere o usuário maria no grupo teste Tabela 1 - Comandos do pacote ldapscripts Saiba que o comando para criação de usuários na LDAP não cria a pasta do usuário. Isso precisa ser feito manualmente pelo usuário root. 1. Para que serve o pacote ldapscripts? 2. Uma forma de saber mais dobre cada comando é usando o man (manual do programa). Use o man para saber mais detalhes de cada um dos comandos acima. Ex.: no Linux, em um terminal execute: man ldapadduser Criando as pastas dos usuários no servidor Lembre-se que ao criar os usuários no LDAP as pastas deles não são criadas. Portanto, é necessário que elas sejam criadas manualmente. Para isso basta utilizar o comando mkdir. Como exemplo, segue o comando para criar a pasta de Maria. sudo mkdir /home/maria Depois disso é necessário trocar o proprietário da pasta e o grupo para os referentes à Maria. Supondo que maria pertence ao grupo alunos, o comando seria: sudo chown -R maria:alunos /home/maria Mas atenção! A máquina onde estão os arquivos de Maria precisa estar configurada para autenticar no servidor LDAP. A confusão é que quando esta máquina é o próprio LDAP, costuma-se achar que ela já está configurada para isso. Mas não é verdade! Mesmo a máquina onde o LDAP está instalado precisa ser configurada para autenticar no LDAP (que é ela mesma). A configuração é a mesma que mostramos anteriormente para as máquinas clientes. Afinal, ela é um cliente dela mesma. Parece confuso, mas você precisa pensar em termos de programas, e não de máquinas. Os programas da máquina que autenticam os usuários não sabem que o programa LDAP está na própria máquina. LDAP como servidor de autenticação de usuários Finalmente, agora vamos ver como criamos um servidor de autenticação usando o LDAP. Na verdade o lado do servidor está pronto! Ou seja, tudo que tínhamos de fazer na máquina onde o programa slapd executa já foi feito, pois para o LDAP não interessa para que ele vai ser usado. Todo LDAP tem uma árvore definida e objetos criados nessa árvore. A única coisa que fizemos especificamente para o serviço de autenticação foi criar os nós “pessoas”, “grupos” e “maquinas”. Portanto, a configuração do serviço de autenticação vai se concentrar nas máquinas clientes, ou seja, nas máquinas que vão autenticar os usuários remotamente usando o LDAP. A primeira coisa a fazer em cada máquina cliente é instalar o pacote libnss-ldap, conforme mostrado a seguir. Isso irá instalar vários outros pacotes que também são utilizados, que são: auth-client-config ldap-auth-client ldap-auth-config libpam-ldap. apt-get install libnss-ldap Durante a instalação, o libnss-ldap executa um programa de configuração que faz sete perguntas. Essas perguntas e as respostas que deveriam ser dadas para nosso exemplo (texto em negrito) são mostradas a seguir. 1ª - Qual o protocolo e endereço IP do servidor LDAP? ldap://10.1.1.1/ 2ª – Nó da árvore a partir do qual os usuários serão pesquisados? ou=natal,o=ufrn 3ª - Versão do protocolo LDAP a ser utilizada? 3 4ª - Tornar o usuário root local administrador do LDAP? Sim 5ª - O diretório LDAP requer login? Não 6ª – Usuário administrador do LDAP? cn=admin,o=ufrn 7ª – Senha do usuário administrador do LDAP? coloque-a-senha-do-admin-aqui Na verdade, após responder essas perguntas será criado o arquivo ldap.conf na pasta /etc, com o conteúdo mostrado na Figura 3. Existem diversas outras linhas no arquivo que é gerado, mas elas estão todas comentadas (iniciam com #). Portanto, não são usadas! Figura 3 - Arquivo /etc/ldap.conf Caso queira refazer essa configuração, ou seja, alterar algum valor que informou, você pode editar o arquivo manualmente ou digitar o comando a seguir, que irá executar o programa de configuração novamente. sudo dpkg-reconfigure ldap-auth-config Ao respondermos às sete perguntas anteriores, deixamos o libnss-ldap configurado. Agora precisamos dizer ao sistema operacional que use o ldap para a autenticação. Isso é feito com o comando a seguir. sudo auth-client-config -t nss -p lac_ldap O que esse comando faz é apenas alterar o conteúdo do arquivo nsswitch.conf para solicitar que ele também use o ldap para obter as informações dos nomes dos usuários, dos grupos e as senhas dos usuários. Isso é feito alterando o valor das três primeiras linhas do arquivo. A Figura 4 mostra o arquivo antes da alteração e depois. Figura 4 - Arquivo /etc/nsswitch.conf antes e depois da configuração do pacote libnss-ldap Para testar se sua máquina cliente está conseguindo obter as informações do LDAP, digite o comando getent, conforme mostrado a seguir, para obter informações de um usuário do LDAP. O comando mostrado obtém informações do usuário maria(assumindo que ele existe no ldap). sudo getent passwd maria maria:x:10001:10001:Maria Silva:/home/maria:/bin/bash Outra forma de testar a autenticação é realizando um ssh na máquina cliente para a própria máquina cliente com um usuário que existe no ldap. O exemplo a seguir utiliza o usuário maria. ssh maria@localhost Se o login for bem sucedido é porque o cliente conseguiu autenticar no LDAP. 1. Qual o arquivo de configuração onde se informa em qual servidor LDAP uma máquina cliente deve se autenticar? 2. Veja o que acontece se digitar o comando getent passwd (sem especificar nenhum usuário). NFS Agora vamos estudar o serviço de compartilhamento de arquivo, que, conforme falamos antes, normalmente é utilizado em conjunto com o serviço de autenticação. Ou seja, não adianta muito o serviço de autenticação permitir que você utilize qualquer máquina da rede se seus arquivos não aparecerem lá. Na sessão anterior, quando sugerirmos que Maria executasse um ssh para a própria máquina cliente, após o login iria aparecer a mensagem “Could not chdir to home directory /home/maria: No such file or directory”. Isso significa que a pasta demaria (/home/Maria) não existe na máquina local. Ela conseguirá efetuar o login, mas o Shell iniciará na pasta / (raiz)! Nesta sessão vamos aprender a usar o NFS (Network File System) para compartilhar pastas pela rede. O que o NFS faz é associar uma pasta do cliente com uma pasta do servidor. Desse modo, sempre que o cliente acessar essa pasta, na verdade ele estará acessando a pasta do servidor. Com isso, a pasta de Maria que está no servidor iria “aparecer” na máquina cliente. A Figura 5 mostra como estariam os discos (e as pastas) na máquina servidora, onde existem os arquivos de Maria, e a máquina cliente, onde não existe nada relacionado àquele usuário. Figura 5 - Discos do servidor e do cliente antes do compartilhamento Para compartilhar as pastas de Maria são necessários três passos: 1. O servidor precisa exportar a pasta de maria (/home/maria). Por exportar entenda liberar o acesso a esta pasta (e suas subpastas) para as máquinas da rede. 2. A máquina cliente precisa criar uma pasta local para onde a pasta remota será associada. Essa pasta local é chamada ponto de montagem. 3. A máquina cliente precisa associar a pasta local com a pasta do servidor. Esse processo é chamado de montar a pasta. A Figura 6 mostra como ficaria a configuração das máquinas após os três passos anteriores serem realizados. Neste ponto a máquina cliente acessa as pastas docs e mp3 de maria como se elas fossem pastas locais (da máquina cliente). Figura 6 - Configuração após compartilhamento A pasta no cliente onde é feita a associação não precisa ser igual à pasta do servidor. No exemplo acima, a pasta /home/maria do servidor poderia, por exemplo, ter sido associada a uma pasta /usuários na máquina cliente. 1. O que significa o termo ponto de montagem para o NFS? 2. O que significa o termo montar uma pasta para o NFS? 3. É possível apagar uma pasta NFS que está montada em uma máquina cliente? 4. Use o comando ls –la no /home e veja quais as permissões das pastas. Instalando e configurando o NFS Para instalar o NFS é necessário instalar dois pacotes na máquina que vai atuar como servidora, ou seja, onde estão os arquivos e pastas a serem compartilhados. Para isso, digite o comando a seguir. apt-get install nfs-kernel-server portmap O Portmap É importante saber que, diferentemente da maioria dos programas que atuam como servidor, o NFS não escuta em uma porta fixa. Ou seja, enquanto um servidor web sempre escuta na porta 80, um servidor de e-mail na porta 25, e um servidor de DNS na porta 53, a cada vez que você executa NFS ele pode usar uma porta diferente. Portanto, a princípio, o programa cliente não tem como saber a porta onde o servidor está. Para resolver isso o NFS informa a um programa chamado portmap em qual porta ele está escutando. Como o portmap sempre escuta em uma mesma porta (porta 111), os clientes sabem que porta é essa, e perguntam ao portmap em qual porta o NFS está escutando. A Figura 7 mostra os passos até que o cliente localize o servidor NFS. Figura 7 - Localizando o servidor NFS através do Portmap Do que foi dito no parágrafo anterior, o mais importante é que você se lembre que, além do NFS, o portmap também tem que estar executando, senão o NFS não vai funcionar. Você não precisa iniciar o portmap manualmente, pois ele já é iniciado automaticamente. De qualquer modo, o comando para iniciá-lo é: /etc/init.d/portmap start Configurando o NFS Vamos agora ver como realizar os três passos que citamos anteriormente para compartilhar uma pasta. Passo 1 Para exportar uma pasta basta inserir uma linha para ela no arquivo /etc/exports, conforme mostrado a seguir. Figura 8 - Arquivo /etc/exports O * significa que qualquer máquina da rede pode montar essa pasta. Ao invés do asterisco poderia ter sido colocado um endereço IP, por exemplo. Nesse caso, apenas a máquina informada poderia montar a pasta. O “rw” significa que o cliente pode ler e escrever na pasta. Se você desejasse que o cliente pudesse apenas ler o conteúdo da pasta, bastava informar “ro” ao invés de “rw”. Sync faz com que as operações solicitadas pelo cliente sejam executadas imediatamente. A opção “root_squash” significa que o usuário root da máquina cliente não terá direito de root nessa pasta. Ou seja, ele se comportará como um usuário comum. Depois de inserir a linha mostrada no arquivo, basta executar o comando a seguir para ativar o compartilhamento. exportfs -a Se quiser ver as pastas compartilhas atualmente, digite o mesmo comando sem o “-a”. Além disso, para cancelar o compartilhamento, basta apagar a linha do arquivo e executar novamente o comando “exportfs –a”. Passo 2 Crie o ponto de montagem na máquina cliente. Ou seja, crie a pasta local para a qual pasta remota estará associada. Depois modifique o proprietário e o grupo dela. Esse dois procedimentos são mostrados a seguir. mkdir /home/maria chown –R maria:alunos /home/maria Naturalmente, essa máquina deve estar configurada para autenticar no servidor LDAP para poder reconhecer os usuários (no caso maria). Passo 3 A máquina cliente precisa instalar o programa a seguir. apt-get install nfs-common Depois, basta fazer a associação da pasta local com a pasta remota. Para isso insira a linha a seguir no arquivo /etc/fstab. Figura 9 - Linha a ser adicionada ao arquivo /etc/fstab O texto “10.1.1.1:/home/Maria” indica a pasta remota e a máquina onde ela está. O texto seguinte, “home/Maria”, é o nome da pasta local que ficará associada à pasta remota. Depois temos “nfs”, que indica o tipo de sistema de arquivos. Os demais parâmetros são para melhorar o desempenho, além de não travar a máquina cliente caso a conexão com o servidor pare de funcionar. Você pode usar sempre esses mesmos parâmetros, alterando, evidentemente, o IP do servidor e os nomes das pastas. Quando a máquina for reiniciada, o arquivo /etc/fstab será lido e a pasta será montada. Entretanto, você provavelmente não vai querer reiniciar seu servidor só porque está compartilhando uma pasta. Por isso, você pode executar o comando a seguir para ativar a configuração imediatamente, ou seja, para montar a pasta remota localmente, conforme indicado no arquivo /etc/fstab mount -a Pronto! Agora pode acessar a pasta local /home/Maria, que na verdade estará acessando a pasta do servidor. Voltando ao exemplo que tínhamos utilizado para testar a autenticação do LDAP no cliente, Maria pode agora fazer ssh para a própria máquina cliente. Após o login ela estará na sua pasta, e se ela digitar o comando ls, o resultado apresentado seria o mostrado a seguir. Assim sendo, as pastas docs e mp3 que estão no servidor aparecem como se estivessem na máquina local. ls /home/maria docs mp3 1. Se o portmap não estiver executando o NFS vai funcionar? 2. Qual a função do comando “exportfs –a”? 3. Acesse o endereçohttps://www.vivaolinux.com.br/artigo/FSTAB-Sua-funcao-e-parametros e veja qual a função do arquivo FSTAB do Linux. Todos os usuários Em uma rede real você tipicamente exportaria o /home do servidor e o montaria no /home dos clientes. Isso faz com que os usuários possam utilizar qualquer máquina da rede. Monitorando o NFS Vamos ver agora mais alguns comandos importantes quando utilizamos o NFS. Nos exemplos, assuma que a máquina que está atuando como servidor NFS se chama Máquina-A. Programas que compõem o NFS Quando explicamos o portmap dissemos que o NFS obtém uma porta e a informa ao portmap, e que os clientes perguntam ao portmap pela porta que o NFS está utilizando. Tudo aquilo está correto, mas temos duas coisas a acrescentar. A primeira é que esse modo de funcionamento utilizando o portmap acontece porque o NFS usa um protocolo chamado RPC (Remote Procedure Call) para trocar suas mensagens. Qualquer programa que use RPC utiliza esse esquema do portmap. A segunda coisa a acrescentar é que embora tenhamos nos referido ao NFS como se fosse um único programa, na verdade o NFS é composto por vários programas, e todos usam RPC. Existe, por exemplo, um programa responsável por tratar as requisições de montagem, chamado mountd, outro responsável por realmente atender às operações que acessam os arquivos e pastas, chamado nfs, e um terceiro, chamado nlockmgr, encarregado de fazer o lock dos arquivos. Os programas que compõem a implementação do NFS no servidor são mostrados na Figura 10. Veja que essa listagem foi obtida com o comandorpcinfo. Figura 10 - Programas que compõem o NFS Vamos agora parar o serviço NFS e repetir o comando acima. Para parar o NFS basta digitar o comando a seguir. /etc/init.d/nfs-kernel-server stop O resultado é mostrado na Figura 11. Veja que os programas nfs, mountd, nlockmgr não estão mais executando. Figura 11 - Programas RPC executando após a parada do NFS Para iniciar novamente o NFS basta digitar o comando a seguir. Quando detectar algum problema no NFS primeiro certifique-se que todos os programas estão executando, conforme mostrado na Figura 10. Caso detecte algum problema você pode parar e reiniciar o serviço NFS com os comandos mostrados. /etc/init.d/nfs-kernel-server start Lembre-se que os programas mostrados na Figura 10 foram os programas executando na máquina servidora NFS. Na máquina cliente o único programa do NFS executando é o nlockmgr. Os programas portmap e status não são propriamente do NFS! Eles são programas para suportar o RPC; portanto, sempre devem estar executando. Clientes acessando o servidor Para verificar quais máquinas clientes montaram cada pasta do servidor, você pode utilizar o comando showmount, conforme mostrado na Figura 12. Veja que a máquina cliente 10.1.1.5 montou a pasta /home/Maria. Figura 12 - Pastas sendo acessadas pelas máquinas clientes Verificando se uma montagem foi bem sucedida O cliente pode verificar se ele conseguiu montar uma pasta do servidor simplesmente tentando acessar a pasta. Mas se ele quiser, pode digitar o comando mount, sem nenhum parâmetro, e ver se aparece uma linha relativa à pasta do servidor. Para o nosso exemplo, essa linha seria como a mostrada a seguir. Veja que são mostrados todos os parâmetros usados no arquivofstab. 10.1.1.1:/home/maria on /home/maria type nfs (rw,rsize=8192,wsize=8192,timeo=14,intr,addr=10.1.1.1) 1. Execute o comando man rpcinfo para ver outras opções do comando rpcinfo; 2. Olhando para o quadro acima, explique o que significam os seguintes dados: (10.1.1.1, o nfs e o rw); 3. Explique qual a vantagem de ter uma pasta remota montada localmente. Você deve ter percebido que essa aula foi bastante prática. Nós tentamos fazer com que o assunto fosse apresentado de forma clara, sempre mostrando alguns exemplos de uso. Deixamos claro que é muito importante que você tente praticar o máximo possível. Leituras Complementares Deixamos aqui algumas sugestões de leituras para enriquecer um pouco a aula e para que você possa ler alguns assuntos com mais detalhes, pois não temos como abordar profundamente todos os assuntos em uma aula. Perceba que alguns links indicados abordam também outros serviços. • Resumo Nesta aula você aprendeu a realizar umas das tarefas mais importantes em qualquer rede, que é criar um serviço de autenticação de usuários e compartilhamento de arquivos. Você viu que isso permite que os usuários utilizem qualquer máquina da rede, e ainda assim, seus arquivos vão estar disponíveis naquela máquina. Para implementar o serviço de autenticação nós utilizamos um servidor LDAP, e para realizar o compartilhamento das pastas nós usamos o NFS. Nesta aula utilizamos apenas máquinas Linux, tanto nos clientes quanto no servidor. Acesse o Laboratório1 desta aula (Aula 10) no Virtualbox e realize os procedimentos descritos a seguir. 1. Configure a máquina-B para autenticar seus usuários no servidor LDAP existente na máquina-A. 2. Supondo que a máquina-B já está autenticando no LDAP da máquina-A, qual comando poderia ser utilizado na máquina-B para obter as informações do usuário pedroque existe na máquina-A? 3. Qual linha, e em qual arquivo, precisa ser adicionada à máquina-A para compartilhar (exportar) a pasta /home/pedro apenas para a máquina cliente 10.1.1.5, com permissões de leitura e escrita? 4. Qual linha, e em qual arquivo, precisa ser adicionada à máquina-B para montar a pasta compartilhada no item anterior? Redes de Computadores Aula 11 – Autenticação e compartilhamento de arquivos: parte III Na aula passada você aprendeu a utilizar o LDAP e o NFS para criar um serviço de autenticação e compartilhamento para máquinas Linux acessarem um servidor também Linux. Nesta aula você vai aprender que é possível fazer um servidor Linux se comportar como um servidor de autenticação e compartilhamento Windows, utilizando um protocolo conhecido como Samba. Desse modo, você pode criar uma rede onde o servidor é Linux e as máquinas que os usuários utilizam são Windows. Este tipo de uso é bastante comum atualmente, pois com isso se consegue usufruir dos benefícios de se ter o sistema operacional Linux na máquina que trabalha como servidor, sem ter que obrigar os usuários que utilizam o Windows a mudar de sistema operacional. Ao final desta aula você será capaz de: • Instalar e configurar um servidor Samba em uma máquina Linux fazendo com que ela forneça os serviços de autenticação e compartilhamento para máquinas Windows. • Configurar as máquinas Windows para usarem o servidor Samba. • Utilizar os comandos necessários para criar os usuários no servidor Samba. • Compartilhar pastas no servidor Samba. Samba O esquema que mostramos para autenticar usuários e compartilhar pastas usando o LDAP e o NFS é muito usado em redes compostas apenas por servidores Linux. As redes Windows usam uma solução baseada no protocolo SMB/CIFS para realizar essas tarefas. O protocolo SMB (Server Message Block) é um protocolo de camada de aplicação que permite o compartilhamento de recursos na rede, onde um recurso pode ser uma pasta ou uma impressora, por exemplo. Quando foi criado, em 1984, o SMB executava sobre os protocolos NetBIOS/NetBEUI, mas a partir de 1996 ele passou a ser executado sobre o TCP/UDP, e foi renomeado para CIFS (Common Internet FileSystem). Como a maioria dos protocolos, o CIFS é composto por uma parte que executa nas máquinas clientes e outra que executa no servidor. Portanto, se você possui máquinas clientes Windows que precisam se autenticar e acessar as pastas de um servidor, esse servidor precisa implementar o protocolo CIFS. Felizmente, existe uma implementação deste protocolo para Linux, que é o Samba. Embora a palavra “samba” nos faça lembra do nosso gênero musical, a escolha desse nome não teve nada a ver com ele. O termo Samba está relacionado às letras s, m e b do protocolo SMB. Portanto, com o Samba é possível ter máquinas Windows acessando pastas de uma máquina Linux, bem como ter uma máquina Linux acessando pastas de máquina Windows. Além de pastas, é possível acessar outros recursos, como impressoras, por exemplo. Caso tenha curiosidade, a história do samba pode ser encontrada no link abaixo: https://www.samba.org/samba/docs/10years.html 1. O que é CIFS? 2. O que é Samba? 3. O que motivou a criação do Samba? 4. O Samba pode ser instalado no Windows? Instalação do Samba Para instalar o Samba, basta digitar o comando a seguir. Isso irá instalar o pacote do servidor (samba), do cliente (smbclient) e da documentação (samba-doc). apt-get install samba smbclient samba-doc Você pode estar pensando se é necessário instalar o pacote samba na máquina cliente e o pacote smbclientna máquina servidora. Lembre-se que qualquer máquina que deseje compartilhar uma pasta se torna um servidor, e qualquer máquina que precise acessar um recurso de outra máquina se torna um cliente. Portanto, normalmente você instala esses dois pacotes nas duas máquinas. Observe, entretanto, que a configuração do Samba que você faz é que vai realmente dizer se você é um servidor ou não. Além disso, podemos pensar em dois tipos de servidores. Um tipo são as máquinas que apenas compartilham pastas, como seria o caso de você compartilhar uma pasta para um amigo pegar alguns arquivos de sua máquina. O outro tipo de servidor são máquinas que vão possuir a relação de usuários da rede e, além de compartilhar pastas, vão atuar como servidores de autenticação. Portanto, os programas necessários para esses dois tipos de servidor são os mesmos, mas a definição de qual tipo a máquina será vai ser feita através dos arquivos de configuração do samba. Para verificar a versão do samba que foi instalada, digite o comando a seguir. A saída desse comando é algo como “Version 3.4.7”, indicando que a versão é 3.4.7. smdb –V Configuração do servidor Samba A configuração que iremos realizar será para uma máquina Linux atuando como servidor de autenticação e compartilhamento de pastas, conforme ilustrado na Figura 1. Os usuários da rede serão criados na máquina Linux e suas pastas serão compartilhadas para as máquinas clientes Windows. Figura 1 - Cenário de rede usado que será utilizado para a configuração do Samba O servidor Samba pode armazenar as informações dos usuários e grupos em arquivos que ele mesmo cria ou armazená-los em um servidor LDAP. Nesta aula iremos configurar o servidor Samba usando os arquivos locais. A configuração para usar o LDAP, entretanto, é bastante parecida com a que será mostrada. As mudanças dizem respeito principalmente ao conteúdo do arquivo de configuração do Samba (smb.conf) que estudaremos a seguir. Toda a configuração do Samba é controlada pelo arquivo /etc/samba/smb.conf. Este arquivo é dividido em seções, que são identificadas por nomes entre colchetes. Com exceção de algumas seções que têm nomes especiais e possuem comportamentos especiais, como [global], [home] e [netlogon], por exemplo, cada seção identifica um compartilhamento criado no servidor. A Figura 2 mostra um exemplo das seções no arquivo smb.conf. Após o nome de cada seção, existem linhas (representadas nessa figura por uma sequência de pontos) que definem nomes de parâmetros e seus valores, e controlam como o compartilhamento vai se comportar. Estudaremos esses parâmetros daqui a pouco, mas como exemplo de um parâmetro temos o nome da pasta sendo compartilhada. No caso da Figura 2, por exemplo, o nome [Programas] significa que vai ser criado um compartilhamento chamado Programas, mas o nome da pasta sendo compartilhada será definido por um parâmetro dentro desta seção, ou seja, em uma linha seguinte à linha [Programas]. Essa pasta poderia ser, por exemplo, /usr/local/programas. Saiba que o nome do compartilhamento e da pasta sendo compartilhada não precisa ser o mesmo. Figura 2 - Estrutura em seções do arquivo smb.conf Vamos agora estudar as seções que possuem nomes especiais, e depois estudaremos como compartilhar uma pasta qualquer da máquina. Para isso, vamos mostrar um exemplo do conteúdo do arquivo smb.conf que você pode utilizar como base quando for configurar um servidor. Como o arquivo é muito grande, vamos apresentar cada seção separadamente, mas lembre-se que todas elas estão no mesmo arquivo. Além disso, indicamos em negrito os parâmetros que você pode precisar modificar ao reutilizar este arquivo quando for instalar seu servidor. Após a apresentação de todas as seções do arquivo, faremos uma explicação de cada parâmetro. Se desejar, vá um pouco adiante no texto e leia a descrição dos parâmetros e depois volte para ver o conteúdo do arquivo. Lembre-se que sempre que alterar alguma coisa no arquivo é necessário reiniciar o Samba com o comando mostrado a seguir. /etc/init.d/samba restart 1. Utilize o comando smbstatus para verificar o status e descreva a sua saída. 2. Explique resumidamente o que é um compartilhamento no Samba. 3. Quais são os daemons que são iniciados no Samba? 4. Para que serve cada uma das seções listadas acima: global, homes e netlogon? Seção [global] A seção [global] é a única que não cria nenhum compartilhamento, mas é a mais importante, pois é nessa seção que são definidos todos os parâmetros relacionados à configuração do servidor (com exceção da criação das pastas compartilhadas propriamente ditas). Realmente as coisas teriam ficado mais claras se tivessem sido criados dois arquivos para configurar o Samba. Um teria apenas os parâmetros que você define nessa seção global, já que eles configuram a parte do servidor que não tem nada a ver com compartilhamento, e outro arquivo teria o conteúdo das demais seções, que definem os compartilhamentos. Entretanto, desse modo você teria dois arquivos para configurar! Por isso, tudo foi colocado nesse único arquivo chamadosmb.conf. Figura 4 - Samba apenas como compartilhamento de pastas Quando um parâmetro não aparecer no arquivo de configuração é porque está sendo usado o valor padrão para aquele parâmetro. 1. Qual o nome do arquivo de configuração do Samba? 2. Qual o nome do parâmetro que define o nome do domínio? 3. Pesquise e descubra quais os possíveis valores que podem ser usados no campo security e o que representa cada um deles. 4. Em que seção deveriam ficar as impressoras compartilhadas na rede? 5. Posso inserir quantos compartilhamentos no Samba? Seção [home] A seção [home] ativa o compartilhamento das pastas dos usuários. Caso não se deseje ativar este compartilhamento, basta comentar todas as linhas desta seção. Uma coisa importante a observar é que não será criado um compartilhamento chamado homes. Ao invés disso será criado um compartilhamento para cada usuário, e cada compartilhamento terá o nome do referido usuário. Figura 5 - Seção [homes] do arquivo smb.conf A Figura 5 mostra o exemplo de como uma seção normalmente é configurada. Como o parâmetro browseable é configurado para no, o usuário não conseguirá ver sua pasta se for acessá-la navegando pelas pastas de rede. Ele terá que realizar o mapeamento indicando o nome do compartilhamento dela explicitamente com o comando net use. Supondo que o usuário Maria existe no servidor, ela poderia mapear a sua pasta com o comando: “net use F: servidormaria”. Esse comando seria digitado na máquina cliente Windows, onde mariafez seu logon. Naturalmente deve-se substituir “servidor” pelo nome da máquina que for utilizada. O parâmetro valid users indica quem pode acessar o compartilhamento. Como o valor %S é substituído pelo nome do compartilhamento, isso significa que cada usuário só pode acessar seu próprio compartilhamento. Você vai estudar os demais parâmetros mais à frente, pois como eles podem ser utilizados também em outras seções do arquivo smb.conf, não faz sentido ficarmos repetindo a explicação várias vezes. Você verá que todas as seções que estudaremos a seguir possuem um parâmetro chamado path,que diz o nome da pasta a ser compartilhada, mas a seção [homes] não tem! É que ela não precisa, pois obtém a pasta de cada usuário do arquivo /etc/passwd. Seção [netlogon] Esta seção é opcional e configura se haverá suporte para execução de scripts após o logon do usuário. Dizendo em outras palavras, é possível querermos executar um arquivo com comando do sistema operacional (é chamado de script) após o usuário se autenticar. Um exemplo de script seria criar um arquivo contendo apenas o comando net use mostrado anteriormente (na seção home) para mapear a pasta do usuário. Desse modo, assim que o usuário conectasse no servidor sua pasta já seria mapeada automaticamente. A Figura 6 mostra um exemplo do conteúdo da seção [netlogon]. Figura 6 - Seção [netlogon] do arquivo smb.conf Falta definir qual será o nome do arquivo a ser executado e a pasta onde ele está. A pasta onde o script é pesquisado é definida no parâmetro path. Portanto, para o arquivo mostrado na Figura 11, os scripts seriam pesquisados na pasta /var/samba/netlogon. Lembre-se que você é que tem que criar essa pasta e definir suas permissões, pois ela não existe. Para isso, digite os comandos a seguir. Depois é só copiar os scripts que desejar para dentro dela. mkdir –p /var/samba/netlogon chmod 775 /var/samba/netlogon O nome do arquivo a ser pesquisado nessa pasta é especificado pelo parâmetro logon script na seção [global]. Se você olhar o exemplo dessa seção que mostramos na Figura 3, verá que utilizamos “logon script = %U.bat”. Observe que “%U” será substituído pelo nome do usuário que se autenticar. Desse modo, quando o usuário maria se autenticar o arquivo pesquisado será maria.bat. Quando o usuário pedro se autenticar, o arquivo pesquisado será pedro.bat, e assim por diante. Com isso, é possível ter um arquivo de script para cada usuário. Veja que só faz sentido criar esta seção quando a máquina Samba for configurada para ser um servidor de autenticação. Caso não se deseje ativar este recurso, basta comentar todas as linhas desta seção. Como existe uma diferença nos caracteres utilizados pelo Linux e pelo Windows para representar o final de linha (ou seja, quando você aperta a tecla ENTER enquanto digita o conteúdo de um arquivo), o arquivo de script deve ser criado em uma máquina Windows e copiado para o servidor Samba. Do mesmo modo que fizemos para a seção [homes], os demais comandos da seção [netlogon] serão explicados posteriormente. 1. Cite o nome de três seções especiais do arquivosmb.conf? 2. A seção [homes] cria um compartilhamento chamado Homes? 3. Usa-se nos scripts parâmetros que substituem nomes de usuários %S, nomes máquinas %M etc. Pesquise nos manuais do Samba outros parâmetros possíveis. Seções predefinidas adicionais Além das seções com nomes especiais que já estudamos, existem algumas outras que podem ser definidas caso se deseje, como uma seção para compartilhar impressoras, chamada [printers], e outra para armazenar as configurações da área de trabalho do usuário na máquina cliente (como menus, ícones, programas do menu iniciar etc.), chamada [profiles]. Não estudaremos essas seções nesta aula. Seções para compartilhamentos adicionais Como dissemos anteriormente, além dos compartilhamentos que acabamos de descrever é possível compartilhar qualquer pasta que desejar. Basta criar uma seção com o nome que se pretende dar para o compartilhamento entre colchetes, indicar o nome da pasta a ser compartilhada no parâmetro path e definir outros parâmetros que se desejar. Tipicamente são parâmetros de permissões de acesso, como os que fornecem ou retiram o acesso de escrita na pasta. A Figura 7 mostra um exemplo simples onde é feito o compartilhamento da pasta /usr/local/programas que, suponha, contém apenas programas para os usuários baixarem suas máquinas, como antivírus, browsers, entre outros. Essa pasta será acessada através de um compartilhamento chamado Programas, que pode ser acessado através do ambiente de rede. Como no nosso exemplo, a pasta contém apenas programas; o conteúdo foi compartilhado em modo somente leitura, ou seja, o conteúdo da pasta não pode ser modificado. Isso significa que os arquivos da pasta não podem ser alterados nem apagados, e que outros arquivos ou pastas não podem ser criados nesta pasta. Adicionalmente, se quiséssemos que apenas o usuário Pedro acessasse este compartilhamento, poderíamos acrescentar a linha “valid users = Pedro” nesta seção. Figura 7 - Exemplo de seção para compartilhar uma pasta Você pode criar quantas seções dessas desejar. Ou seja, podem existir várias seções para compartilhas pastas adicionais. 1. Para que serve o parâmetro path? 2. Todo servidor Samba deve ter uma seção chamada [programas]? Descrição dos parâmetros do arquivo smb.conf Segue uma explicação dos principais parâmetros do arquivo smb.conf. Observe que enquanto alguns desses parâmetros são exclusivos da seção [global], outros podem aparecer em qualquer seção. workgroup: nome do domínio ou grupo de trabalho do qual a máquina fará parte. O nome do grupo aparece na tela de login, na opção "fazer logon em:". Você sempre precisa alterar esse parâmetro para o valor usado na sua rede. netbiosname: nome da máquina. Utilizado quando o Samba não consegue resolver o nome do servidor pelo DNS. serverstring: comentário sobre o servidor. keepalive: opção utilizada para verificar se algum cliente travou. A sua representação é em segundos, e o padrão é 5 minutos (300s). O valor 0 indica a não verificação de clientes. Sugerimos o valor 30 para redes com poucas máquinas. deadtime: opção que fecha todas as seções de clientes inativos. Um cliente é considerado inativo quando não está enviando nem recebendo dados. Por padrão o valor é definido como 0, o qual diz que nunca será fechado. logon script: determina o script que será usado após o usuário efetuar logon. A opção %U diz que o script possui o nome do usuário, ou seja, cada usuário teria um script. logon path: indica o diretório que contém os scripts de logon. Especificar um "logon path" sem caminho evita armazenar dados do usuário no servidor, além de não gerar mensagem de erro. logon drive: força o mapeamento do diretório home do usuário como unidade j: na máquina cliente. A letra da unidade poderá ser trocada por qualquer outra que esteja disponível na máquina cliente. Sugerimos a escolha de letras distante das utilizadas, pois isso garantirá o sucesso do mapeamento. domain logon: local onde se permite o logon do Samba. Marque como Yes quando a máquina for servidor de autenticação. oslevel: valor usado para especificar qual servidor vencerá a eleição, caso haja uma, para manter a lista das máquinas na rede. Como o valor padrão das máquinas Windows é 32, indicamos usar o valor 100 para garantir que esta máquina vencerá a eleição. prefered master: usado para permitir efetuar eleição para o domain master. Marque como Yes quando a máquina for ser servidor de autenticação. domain master: define o servidor master do domínio. Marque como Yes quando a máquina for servidor de autenticação. security: controla como será o controle de acesso às pastas compartilhadas. Os dois principais modos são user e share. O primeiro é usado quando a máquina Samba for atuar como servidor de autenticação, e o segundo quando se desejar apenas compartilhar pastas da máquina com outras pessoas. passwd program: caminho onde se localiza o binário responsável pela troca de senha. O padrão é /usr/bin/passwd %u, onde %u representa o nome do usuário. passwd chat: usado em troca de senha a partir da estação cliente Windows. comment: utilizado para comentário. path: caminho da pasta sendo compartilhada. No caso da seção [netlogon], define o local onde os arquivos de scripts são procurados. guest ok: permissão de acesso de visitantes. Melhor usar No. browseable: permissão de deixar a pasta compartilhada visível no ambiente de rede. Para a seção [homes] é melhor usar No. Para as demais pastas que você compartilhe, criando outras seções no arquivo, normalmente pode deixar Yes. writable:permissão para escrita na pasta. Depende de cada pasta. Normalmente Yes. read only:significa que não se pode gravar nessa pasta. Normalmente No. create mask: define as permissões (bits rwx) dos arquivos criados. Sugestão: 0700 (equivale a rwxr-xr-x). directory mask: define as permissões (bits rwx) das pastas criadas. Sugestão: 0700. 1. É necessário utilizar todos os parâmetros estudados até aqui na configuração do arquivo do Samba? 2. Existe um número obrigatório de parâmetros a serem usados na configuração do Samba? Administração do Samba Nesta seção vamos ver como administrar o servidor Samba. Para explicar os comandos, vamos mostrar exemplos utilizando nomes de usuários. Naturalmente, basta que você os substitua pelos nomes que desejar quando for instalar seu próprio servidor. Criando usuários 1. criar o usuário pedro, digite o comando a seguir. useradd pedro 2. Para inserir uma senha para pedro,digite o comando a seguir. 3. Para excluir o usuário pedro,digite o comando a seguir. smbpasswd -a pedro smbpasswd –x pedro Criando uma conta de máquina Para que uma máquina Windows possa fazer parte da rede com Samba (entrar no domínio, como é chamado), é necessário que exista um cadastro para a máquina no Samba. Esse cadastro é referido como uma “conta de máquina”, e para uma máquina chamada “Maquina-A”, ele é feito digitando-se os comandos a seguir. useradd –d /dev/null –s /bin/false Maquina-A$ Smbpasswd –a –m Maquina-A A primeira linha cria a conta de máquina. Veja que nessa linha o nome da máquina é informado acrescentado de um “$”. Na linha seguinte, que cadastra uma senha para a máquina, o nome da máquina é informado normalmente (sem o “$”). Além da conta de máquina é necessário criar um usuário administrador que será utilizado na hora de inserir a máquinas no domínio. Para criar esse usuário com o nome root utilize o comando a seguir. useradd –a root Será pedida uma senha que você deve memorizar, pois é esse usuário e esta senha que você irá informar quando for colocar uma máquina Windows na rede Samba, ou seja, quando for inserir a máquina no domínio. Configuração das máquinas clientes Windows Agora que os usuários já estão criados e já existe uma conta para a máquina que pretende acessar o servidor Samba, basta inserir a máquina no domínio. Incluindo uma máquina Windows no Samba Agora se deve acessar como administrador a máquina cliente Windows que se pretende inserir no domínio. Portanto, faça o login no Windows como usuário “Administrador” e depois clique com o Botão direito em Meu Computador. Depois clique em Propriedades. Ou então clique em Iniciar -> Configurações -> Painel de Controle ->Sistema. Agora vá em Propriedades do Sistema, conforme mostrado na Figura 8, e clique em Alterar. Figura 8 - Configurando uma máquina Windows para entrar no domínio Irá aparecer a tela mostrada na Figura 9. Em Domínio, digite o domínio definido no parâmetro workgroup do arquivo smb.conf. No exemplo que utilizamos nesta aula esse valor foi Metropole. Após clicar no botão Ok,entre com o usuário e a senha que você criou para inserir as máquinas no domínio, conforme explicado na seção “Criando uma conta de máquina”. O usuário que utilizamos como exemplo naquela seção foi root. Figura 9 - Informando a senha para inserir a máquina Windows no domínio Se tudo der certo, irá aparecer a tela mostrada na Figura 10, indicando que a máquina entrou no domínio. Caso a máquina não consiga fazer isso, execute o comando mostrado a seguir e tente inserir a máquina novamente no domínio, refazendo as tarefas mostradas nas Figuras 8 e 9. echo “/bin/false” >> /etc/shells Figura 10 - Confirmação de que a máquina entrou no domínio Uma vez que a máquina cliente Windows tenha conseguido entrar no domínio, na sua tela de logon aparecerá o nome do novo domínio. Basta selecioná-lo e informar o nome do usuário e a senha criados no servidor Samba. Seguindo o nosso exemplo, poderia ser utilizado o usuário pedro. Figura 11 - Novo domínio na tela de logon da máquina cliente Windows Finalizamos esta aula enfatizando a necessidade de praticar os assuntos abordados, bem como a leitura dos sites sugeridos para conhecer mais detalhes sobre o Samba. A partir dos links, você terá contato com os possíveis problemas que acontecem na prática, bem como começar a interagir nos fóruns de discussão, fazendo perguntas e deixando comentários sobre seus avanços – esta é a melhor forma de aprender. Leituras Complementares Para que você possa se aprofundar um pouco mais e conhecer detalhes não abordados aqui, recomendamos acessar os links a seguir para enriquecer o aprendizado: • Resumo Nesta aula você aprendeu como integrar máquinas Windows e Linux em uma rede. Você viu como é possível usar um servidor Linux para substituir um servidor Windows, de modo que ele autentique os usuários das máquinas Windows e forneça o acesso às pastas dos usuários. Aprendeu que o responsável por tornar isso possível é o protocolo Samba, que é uma implementação Linux dos protocolos usados pela Microsoft para esta finalidade. Você aprendeu também que, além de criar as contas para os usuários que irão se conectar às máquinas clientes, também é necessário criar no servidor Samba uma conta de máquina para cada uma das máquinas cliente Windows. Acesse o Laboratório1 desta aula (Aula 11) no Virtualbox e realize os procedimentos descritos a seguir. • Crie uma seção no arquivo smb.conf que compartilhe a pasta de cada usuário para que ele possa acessá-la. • Crie uma seção no arquivo smb.conf que faça com que o arquivo “comando.bat”, que está na pasta /usr/local/exe, seja executado após cada usuário se autenticar no servidor Samba. • Crie uma seção no arquivo smb.conf que compartilhe a pasta /opt/fotos com o nome “Fotos” e permita que todos os usuários possam escrever nesta pasta. Redes de Computadores Aula 15 – Gerenciamento de redes – SNMP, RMON e CACTI Nesta aula você vai aprender conceitos sobre os protocolos SNMP e RMON a fim de aplicá-los na prática junto a uma ferramenta muito importante na gerência de nossa rede, o CACTI. O funcionamento do CACTI baseia-se na tradução dos dados coletados pelo protocolo SNMP e na utilização de scripts para o modo gráfico, de modo a tornar mais fácil a análise dos dados coletados. Desse modo, podemos saber o que realmente se passa na rede em tempo real. Para podermos entender o funcionamento da ferramenta é interessante entendermos o que na verdade é o CACTI, conceitos de SNMP, RMON e como chegamos até ele. Ao final desta aula você será capaz de: • Entender os conceitos e funcionamento do protocolo SNMP. • Instalar e configurar um cliente SNMP em ambiente Linux. • Entender o padrão RMON de gerenciamento de redes. • Entender o funcionamento do CACTI. • Instalar e configurar o CACTI. • Criar/analisar gráficos para visualização em tempo real dos equipamentos monitorados pelo CACTI. Gerência de redes Estamos na última aula desta disciplina e você já estudou uma série de protocolos e serviços que são utilizados nas redes de computadores. Portanto, você já sabe que quando for instalar uma rede na prática, vai ter que instalar e configurar vários desses protocolos e programas. Mas será que após a fase de instalação e configuração o trabalho termina? De modo algum! Depois que a rede está em funcionamento, podem acontecer diversos problemas, como, por exemplo: • Equipamentos que deixam de funcionar completamente. Switchs, roteadores ou mesmo computadores podem apresentar problemas de hardware e pararem de funcionar. • Equipamentos que apresentam erros esporádicos. Os problemas de hardware descritos no item anterior podem se apresentar apenas de tempos em tempos, dificultando ainda mais a sua detecção. Pense, por exemplo, em uma placa de rede que de vez em quando apresenta problemas, fazendo com que os quadros sejam perdidos. • Programas que param de funcionar. Os programas podem parar de funcionar ou passarem a executar de modo muito lento devido a bugs ou a ataques. • Tráfego elevado que deixa a rede lenta. É muito comum os usuários de uma rede reclamarem que ela está lenta. Isso pode acontecer por várias razões, incluindo: i) máquinas foram infectadas com vírus e estão gerando tráfego adicional; ii) os usuários estão utilizando aplicações que geram muito tráfego, mas que não fazem parte dos negócios da empresa, como Peer-to-Peer (P2P), por exemplo; iii) a própria rede da empresa cresceu e os links precisam ser redimensionados (ter suas velocidades aumentadas). • O link de Internet, ou entre matriz e filiais, deixa de funcionar. Muitas dessas conexões atualmente são por links de rádio, que deixam de funcionar quando as antenas sofrem alguma mudança na sua posição (devido ao vento, por exemplo). Outras vezes esses links são fornecidos por outras empresas (por exemplo, Oi e Embratel) e, portanto, manter esses links funcionando é de responsabilidade delas. Diante de tantos possíveis problemas que podem ocorrer, você não deve esperar que as pessoas reclamem que algo não está funcionando bem para que só então você tome conhecimento desse fato e faça algo para corrigir o problema. Você deve se antecipar às reclamações e tentar resolver o problema o mais rápido possível, de preferência antes mesmo que as pessoas tomem conhecimento dele. Suponha, por exemplo, que um switch parou de funcionar. Caso você detecte esse problema logo depois que ele ocorra, você pode trocar o switch com defeito por outro. Naturalmente, as pessoas que estavam usando a parte da rede afetada por esse switch vão notar o problema. Entretanto, a rede vai voltar a funcionar muito mais rapidamente do que se você tivesse que esperar alguém reclamar do problema para que você fizesse a troca. Além disso, muitas pessoas poderiam não estar usando a rede naquele momento, e essas pessoas nem mesmo iriam notar que o problema aconteceu. Situação semelhante se aplica ao caso de um link que conecta uma matriz e uma filial, ou conecta a empresa a Internet. Você pode monitorar a utilização desse link constantemente. Quando notar que ele está frequentemente sendo usado na sua capacidade máxima, você pode tomar algumas medidas, como, por exemplo, solicitar que a capacidade do link seja aumentada, ou identificar e controlar quais aplicações estão gerando o tráfego em excesso. Independentemente dos problemas que podem ocorrer, dificilmente uma rede vai permanecer sempre do mesmo modo que foi instalada. Normalmente a rede da empresa cresce de tamanho, ou então precisa ser reconfigurada para atender mudanças na própria empresa. Nesses casos, você não vai querer ir fisicamente até o local onde está cada equipamento que você precisa reconfigurar para poder fazer essa tarefa. Até porque estes equipamentos podem estar em outro prédio na mesma cidade, ou mesmo em uma cidade diferente! Com certeza você vai preferir reconfigurar os equipamentos sem sair da sala onde você trabalha, utilizando, para isso, a própria rede da empresa. Veja que identificamos dois tipos de tarefa que precisam ser realizadas para manter as redes em bom funcionamento, que são Monitoramento e Configuração. Essas duas tarefas juntas recebem o nome de Gerenciamento de redes. Nesta aula você verá que a forma mais comum de fazer gerenciamento de redes é utilizar um protocolo que realize a comunicação com os equipamentos que precisam ser monitorados e/ou configurados. Como você vai começar a estudar na próxima seção, esse protocolo é o SNMP. 1. Quais os principais problemas que podem existir em um ambiente de rede? 2. Imagine que você seja um gerente de redes de uma empresa. Escolha algum problema citado acima e cite alguma atitude que tomaria quando o enfrentasse. 3. Quais as duas principais tarefas que precisam ser realizadas para que uma rede funcione corretamente? O Protocolo SNMP O protocolo SNMP (Simple Network Management Protocol - Protocolo Simples de Gerência de Rede) é um protocolo utilizado para gerência de redes e envia suas mensagens dentro de pacotes UDP. Usar um protocolo padronizado facilita o intercâmbio de informação entre os dispositivos de rede, como servidores e switches. Desse modo, o SNMP possibilita aos administradores de rede gerenciar o desempenho da rede e encontrar seus eventuais problemas. Os dados são obtidos através de requisições de um gerente a um ou mais agentes utilizando os serviços do protocolo de transporte para enviar e receber suas mensagens. Portanto, o SNMP define uma forma padronizada de se solicitar informações aos equipamentos. Atualmente, praticamente todos os equipamentos de rede que suportam gerenciamento entendem o protocolo SNMP. O gerenciamento da rede através do SNMP permite o acompanhamento simples e fácil do estado, em tempo real, da rede, podendo ser utilizado para gerenciar diferentes tipos de sistemas. Idealmente todos os equipamentos de rede deveriam suportar gerenciamento. Entretanto, para reduzir os custos, existem modelos de equipamentos que não o fazem. Isso acontece, por exemplo, com vários modelos de switchs. Na sua rede, procure usar sempre equipamentos gerenciáveis. Acabamos de dizer que o SNMP define uma forma padronizada de solicitarmos informações aos equipamentos. Mas, quais informações podemos solicitar? Será que o SNMP define exatamente quais são essas informações? Se o SNMP tivesse fixado o conjunto de valores que poderiam ser solicitados, ele ficaria muito limitado, e não suportaria, por exemplo, novos tipos de equipamentos que surgiram depois que ele foi criado. Portanto, a solução foi separar o protocolo que faz as solicitações das informações que ele solicita. O conjunto de informações que o SNMP pode solicitar é chamado de MIB (Management Information Base – Base de Informações de Gerenciamento). Parece confuso, mas significa que o SNMP é apenas capaz de solicitar uma informação, mas não precisa saber o que aquela pergunta significa. Quem tem que saber é o equipamento que recebe a solicitação. Esse modelo permite que novas MIBs sejam adicionadas sem que o SNMP sofra nenhuma alteração. O SNMP possui um conjunto limitado de comandos que são baseados no mecanismo de busca/alteração, permitindo as operações de alteração de um valor de um objeto, de obtenção dos valores de um objeto e algumas variações dessas operações. A utilização de um número limitado de operações baseadas em tais mecanismos o torna um protocolo de fácil implementação, simples, estável e flexível. Como conseqüência, reduz o tráfego de mensagens de gerenciamento através da rede e permite a introdução de novas características. A simplicidade do SNMP facilitou sua inclusão em equipamentos de interconexão. No final da década de 1990, a solução SNMP já era tão difundida que se estabelecera como padrão de gerência de redes de computadores. Hoje, praticamente todos os equipamentos de interconexão dão suporte ao SNMP, bem como muitos outros dispositivos (nobreaks, modems etc.), e sistemas de software (servidores Web, sistemas de bancos de dados etc.). 1. Qual a principal função do protocolo SNMP? 2. Cite um exemplo de algum equipamento de rede que dê suporte ao protocolo. 3. Qual é o protocolo de transporte utilizado para transmitir mensagens SNMP? 4. O que é uma MIB? Como o protocolo funciona? O funcionamento do SNMP é baseado em dois tipos de dispositivos, o agente e o gerente. Cada máquina gerenciada é vista como um conjunto de variáveis que representam informações referentes ao seu estado atual. Estas informações ficam disponíveis ao gerente através de consulta e podem ser alteradas por ele. Cada máquina gerenciada pelo SNMP deve possuir um agente e uma base de informações, a MIB, que define as variáveis de gerência que todo elemento gerenciado deve ter, independentemente de sua função particular. Como alguns exemplos de variáveis que podem existir nas MIBS, podemos citar número de conexões TCP estabelecidas no momento, número de pacotes UDP transmitidos, número de quadros recebidos com erro, velocidade de uma porta de switch, modelo do equipamento etc. O mundo da gerência SNMP criou várias dezenas de módulos MIBs desde o início da década de 90. Alguns são padronizados, enquanto outros são ditos patenteados. Um módulo MIB patenteado é elaborado pelo fabricante de um elemento gerenciado particular para armazenar variáveis de gerência consideradas importantes, mas que não fazem parte dos padrões descritos pelo protocolo. Figura 1 - Exemplo de rede sendo gerenciada por um servidor SNMP Como podemos ver na Figura 1, a arquitetura do SNMP é composta basicamente de: • Agente SNMP: É um processo executado na máquina gerenciada, responsável pela manutenção das informações de gerência da máquina. As funções principais de um agente são de atender as requisições enviadas pelo gerente e enviar automaticamente informações de gerenciamento ao gerente, quando previamente programado. Um agente pode estar instalado em um switch, um roteador, uma impressora, um computador, entre outros. • Gerente SNMP: É um programa executado em uma estação servidora que permite a obtenção e o envio de informações de gerenciamento junto aos dispositivos gerenciados mediante a comunicação com um ou mais agentes. O gerente fica responsável pelo monitoramento, relatórios e decisões na ocorrência de problemas, enquanto que o agente fica responsável pelas funções de envio e alteração das informações e também pela notificação da ocorrência de eventos específicos ao gerente. A comunicação entre o agente e o gerente pode acontecer de duas formas. Na primeira forma o gerente solicita informações aos agentes quando desejar. Isso pode ser feito por uma pessoa usando o software de gerência, que em determinado momento resolve solicitar alguma informação de um equipamento específico. Uma variação desta forma é o gerente deixar agendado que periodicamente sejam enviadas requisições aos agentes solicitando informações. Desse modo, sempre que os dados são recebidos eles são analisados automaticamente e alguma ação é gerada. Essa ação pode ser, por exemplo, apenas a geração de relatórios ou o envio de alarmes quando determinadas situações são detectadas. A outra forma de comunicação entre o gerente e os agentes, que é conhecida como Traps, consiste em configurar os agentes para enviarem informações ao gerente sempre que uma determinada situação ocorrer. Normalmente estas situações estão relacionadas à ocorrência de problemas, como, por exemplo, a recepção de um elevado número de quadros com erro. Em redes reais o mais comum é utilizar a primeira forma de comunicação, com o agendamento de solicitações periódicas no gerente. Mas a utilização em conjunto das duas formas também é empregada, configurando-se adicionalmente alguns equipamentos para utilizarem Traps em determinadas situações. MIB-II Uma das MIBs mais utilizadas é a MIB-II. Os objetos (variáveis) desta MIB são divididos nos grupos mostrados a Tabela 1. Nome do grupo Contém informações sobre System Informações básicas sobre o sistema Interfaces Interfaces de rede at Tradução de endereços Ip Protocolo IP Icmp Protocolo ICMP Tcp Protocolo TCP Udp Protocolo UDP Egp Protocolo EGP Transmission Meios de transmissão snmp Protocolo SNMP Tabela 1 - Grupos da MIB-II Cada um desses grupos contém uma série de objetos. Na Tabela 2 podemos ver exemplos de objetos de alguns grupos da MIB-II. Grupo System sysDescr Descrição do equipamento. Pode incluir fabricante e modelo. sysUpTime Tempo desde a última reinicialização. sysContact Nome de uma pessoa de contato responsável pelo equipamento. Grupo Interfaces ifNumber Número de interfaces de rede existentes na máquina. ifOperStatus Estado atual da interface (ativada ou desativada). ifInOctets Número de bytes recebidos pela interface. Grupo IP ipForwarding Se o roteamento está ativo (se atuando como roteador). ipInHdrErrors Número de pacotes recebidos e descartados devido a erros. Grupo TCP tcpCurrentEstab Número de conexões TCP estabelecidas no momento. Tabela 2 - Exemplos de objetos da MIB-II Cada objeto de uma MIB, seja a MIB-II ou qualquer outra, é identificado por um identificador numérico. O objetoipForwarding, por exemplo, é identificado por 1.3.6.1.2.1.4.1, enquanto ipInHdrErrors é identificado por 1.3.6.1.2.1.4.4. Se você prestar bem atenção nesses números verá que eles têm um prefixo em comum (1.3.6.1.2.1.4), diferido um do outro apenas pelo último número. Isso acontece porque eles pertencem ao mesmo grupo, e esse prefixo comum é exatamente o prefixo do grupo. Do mesmo modo, grupos diferentes também possuem um prefixo em comum (de tamanho menor) para identificar alguma relação entre os grupos. 1. Explique como o protocolo funciona, mencionando a função do gerente e do cliente em uma rede, assim como os dois se relacionam. 2. Discorra como funciona a forma de comunicação viaTraps. 3. Como vimos acima, o grupo system da MIB-II contém informações básicas sobre o sistema. Cite algum objeto que pertença a esse grupo, assim como sua função. 4. Qual objeto da MIB-II indica se um determinado cliente é um roteador? As mensagens do protocolo SNMP O SNMP possui mensagens para solicitar o valor dos objetos e para definir seus valores. Existem basicamente quatro tipos de mensagens, que são: GET, GET-NEXT, SET e TRAP. A primeira é utilizada tanto para solicitar o valor de um (ou mais) objeto(s) quando para informar o valor. Ou seja, é tanto uma mensagem de requisição quanto de resposta. Ela sempre contém dois campos para cada objeto: um é o seu nome e o outro o valor. Nas requisições, o campo valor é deixado em branco. GET-NEXT tem a mesma finalidade de GET, mas é utilizada para ler estruturas como tabelas. SET define o valor de um objeto. Todas essas três são enviadas pelo gerente para o agente. A mensagem TRAP é enviada pelo agente para o gerente para informar o valor de um objeto, sem que o mesmo tenha sido solicitado. Evidentemente os nomes de objetos utilizados nessas mensagens devem estar definidos em alguma MIB sendo utilizada pelos equipamentos. Uma mensagem SNMP possui três partes principais: • Version: Contém a versão do SNMP. Tanto o gerente como o agente devem utilizar a mesma versão. Mensagens contendo versões diferentes são descartadas. • Community: Identifica a comunidade, que é uma string (texto) que funciona como uma forma de controle de acesso. É possível definir quais variáveis de uma MIB podem ser acessadas por cada nome de comunidade, além do tipo de acesso permitido (leitura ou escrita). • SNMP PDU: Trata-se realmente o corpo da mensagem, e irá conter os objetos sendo solicitados (nas solicitações) e seus valores (nas respostas). Em uma mesma mensagem podem ser solicitados vários objetos. A Figura 2 mostra uma requisição feita pela estação gerente a um agente. Observe que foi solicitado o objetoifNumber (número de interfaces na máquina) e ele que faz parte da MIB instalada no agente. Na verdade a MIB tem que estar instalada tanto no agente quanto no gerente, senão o gerente não ia saber como perguntar. Veja também que o agente responde informando que a máquina agente tem duas interfaces. Figura 2 - Requisição e resposta SNMP 1. Quais os 4 tipos básicos de mensagens do SNMP? Escolha uma e defina. 2. Para que serve o campo COMMUNITY em uma mensagem SNMP? 3. Como é possível saber se uma mensagem GET é uma requisição ou uma resposta? Existem as versões 1, 2 e 3 do SNMP. Nas versões 2 e 3 foram adicionados principalmente mecanismos para melhorar o desempenho e a segurança. Instalando e configurando o SNMP em servidores Linux Agora que já entendemos como o protocolo funciona, vamos instalá-lo em nossa máquina. Para isso, vamos instalar um pacote chamado “snmpd”, que é um programa que implementa um agente SNMP. Naturalmente ele irá executar em background. apt-get install snmpd Após a instalação, é necessário alterar algumas configurações no arquivo snmpd.conf. Para isso, abra o arquivo em um editor de textos com o comando a seguir. gedit /etc/snmp/snmpd.conf Na sessão “Acess Control” comente a linha "com2sec paranoid default public" e descomente a "com2sec readonly default public". Não se esqueça de mudar o nome do “source”, ou seja, o IP de nosso gerente (estação de gerência) e o nome da comunidade SNMP, que é uma espécie de “senha” para outras pessoas não terem acesso aos dados livremente. #com2sec paranoid default public com2sec readonly ip_maq_gerencia sua_comunidade_snmp #com2sec readwrite default private Note que demos a permissão somente de leitura, através do parâmetro “readonly”; caso seu gerente SNMP necessite realizar alguma escrita, descomente a linha mais abaixo que contém “readwrite”. Após isso, reinicie o snmpd através do comando: /etc/init.d/snmpd restart E, finalmente, para testarmos se está tudo ok, basta digitarmos no console o comando a seguir. Ele mostra todos os objetos do grupo system da MIB-II. snmpwalk -Os -c sua_comunidade -v 1 ip_maq_gerencia system Caso positivo, deverão ser retornadas várias informações da MIB na tela. 1. Pesquise por algum programa que tenha uma interface gráfica e permita ver a MIB. Normalmente esses programas são chamados MIB Browsers. Instale o programa e tente acessar o seu agente. 2. Qual(is) monitoramento(s) você conseguiu fazer de seu sistema? 3. Se mudarmos a comunidade de cliente para “public”, você acha que o servidor ainda conseguirá monitorá-lo (levando em conta que você usou as configurações descritas acima)? O protocolo de gerenciamento RMON Agora que já conhecemos o protocolo SNMP e vimos que ele é bastante útil no monitoramento de nossos equipamentos da rede, iremos ver um protocolo baseado no SNMP, ou seja, que também irá nos auxiliar no gerenciamento, chamado RMON. Ele é um padrão IETF (Internet Engineering Task Force) de gerenciamento de redes cuja sigla representa Remote Network Monitoring MIB. Basicamente ele é uma MIB padrão do protocolo SNMP. Este padrão para monitoramento remoto oferece uma arquitetura de gerenciamento distribuída para análise de tráfego, resolução de problemas, demonstração de tendências e gerenciamento proativo de redes de modo geral. Dentre os protocolos de gerenciamento, o RMON é um dos primeiros a permitir o gerenciamento proativo (ele pode fazer a previsão de acontecimentos), sendo então esta a grande vantagem dele em relação às outras arquiteturas de gerenciamento. Sendo assim, o trabalho de gerenciamento é simplificado e os problemas são resolvidos de forma facilitada. Assim, aumenta a disponibilidade da rede e caem os custos de manutenção de forma significativa; além disso, o RMON provê uma interoperabilidade independente do fabricante, características muito importantes a serem analisadas nos dias atuais em um ambiente de gerência. Entendendo o funcionamento do RMON O padrão RMON foi desenvolvido no intuito de resolver questões que outros protocolos de gerenciamento não eram capazes. Com base nestas questões, a RFC 1757 define objetivos gerenciais que o padrão RMON deve observar: • Operação Offline - Existem situações em que uma estação de gerenciamento não estará em contato contínuo com seus dispositivos de gerenciamento remoto. Esta situação pode ocorrer como consequência de projeto, a fim de que se reduzam os custos de comunicação, ou por falha da rede, quando a comunicação entre a estação de gerenciamento e o monitor fica comprometida em sua qualidade. • Monitoramento Proativo - Dados os recursos disponíveis no monitor, é normalmente desejável que ele execute rotinas de diagnóstico de forma contínua e que acumule os dados de desempenho da rede. O monitor estará sempre disponível no início de uma falha; assim, ele poderá notificar a estação de gerenciamento da falha. • Detecção e Notificação de Problemas - O monitor pode ser configurado para reconhecer condições de erro e verificá-las continuamente. No advento de uma destas condições, o evento pode ser registrado e as estações de gerenciamento notificadas. • Valor Agregado aos Dados - Considerando o fato de que os dispositivos de gerenciamento remoto representam recursos dedicados exclusivamente a funções de gerenciamento e considerando também que eles se localizam diretamente nas porções monitoradas da rede, pode-se dizer que estes dispositivos permitem a agregação de valor aos dados coletados. Por exemplo, indicando quais os hostsque geram a maior quantidade de tráfego ou erros, um dispositivo pode oferecer (à estação de gerenciamento) informações preciosas para a resolução de toda uma classe de problemas. • Gerenciamento Múltiplo - Uma organização pode ter mais de uma estação de gerenciamento para as várias unidades da empresa, de acordo com a finalidade de cada uma delas. 1. É correto afirmar que o RMON é uma MIB padrão do protocolo SNMP? 2. Cite qual a principal vantagem de gerenciamento que o RMON introduziu, explicando como ela funciona. 3. Quais objetivos gerenciais o padrão RMON deve observar? Escolha um e fale sobre ele. Como vimos anteriormente, o RMON foi baseado no SNMP; sendo assim, ele fez diversas melhorias em cima desse protocolo de gerenciamento. Dentre as principais, podemos destacar: • Controle de Monitoramento Remoto: Com o objetivo de efetivamente gerenciar um monitor remoto, a RMON MIB contém características que possibilitam o controle efetivo da estação de gerenciamento. Estas características podem ser agrupadas em duas categorias: o Configuração: baseada em tabelas de controle e tabelas de dados. o Invocação de uma ação: um objeto é utilizado para representar um comando, e uma invocação de uma ação pode ser solicitada modificando o valor dele. • Múltiplos Gerentes: Na arquitetura RMON, agentes podem estar sujeitos ao gerenciamento de muitas estações gerentes. No caso de um agente RMON ser compartilhado, alguns problemas podem surgir. São eles: o Requisições concorrentes; o Captura de um recurso por um determinado gerente por um longo período de tempo; o Recursos podem ser atribuídos para um gerente que ficou offline antes de liberar o recurso. Para tentar solucionar tais problemas, uma combinação de requisitos deve ser utilizada. Os requisitos são: o Inclusão de um campo de dono para cada linha da tabela de controle; o Reconhecimento da não necessidade de utilização de um recurso; o Possibilidade de negociação de um recurso entre gerentes; o A possibilidade de um operador cancelar reservas de recursos; o Durante uma reinicialização, um gerente poder liberar recursos. • Gerenciamento de Tabelas: Atividade que não era muito clara no padrão SNMP original, inclui convenções textuais e regras mais claras e disciplinadas para a adição e remoção de linhas. 1. Cite quais problemas podem existir em um ambiente de monitoramento quando são usados múltiplos gerentes. 2. Uma MIB RMON contém características que possibilitam o controle efetivo da estação de gerenciamento? Cobertura das versões do RMON Existem duas versões do RMON: RMON1 e RMON2. O RMON1 define 10 grupos de MIBs para um monitoramento básico da rede, o qual pode ser encontrado na maioria de nossos hardwares de rede. Já o RMON2 é uma extensão do RMON1 que foca em camadas de rede mais altas, como podemos ver na Figura 3. Sendo assim, ele tem ênfase nas camadas de rede até a de nível de aplicação, cujo principal objetivo é permitir a gerência de aplicações para monitorar pacotes por todas as camadas de rede. Esta é a principal diferença para o RMON1, visto que este somente monitora os pacotes em nível de Enlace e Físico. Figura 3 - Monitoramento das versões RMON no modelo OSI 1. Qual a principal diferença entre o RMON1 e o RMON2? 2. Faça uma pesquisa sobre quais tipos de monitoramento são possíveis com cada versão do RMON, fazendo relação com a camada que cada um analisa. Entendendo o funcionamento do CACTI Vamos agora estudar um software, chamado CACTI, que utiliza o SNMP para coletar informações, mas possui como grande benefício o fato de processar essas informações de modo a mostrá-las de uma forma muito mais amigável e útil para o administrador da rede. O CACTI usa um frontend chamado RRDtool, que é um programa desenvolvido usando a linguagem de programação "C" para guardar os dados coletados em arquivos no formato ".rrd". O número de registros em um arquivo “.rrd” nunca aumenta, significando que antigos registros são frequentemente removidos. Isto implica na obtenção de figuras precisas para dados recentemente registrados, onde as figuras baseadas em dados muito antigos possuem valores aproximados. Por padrão, você pode obter gráficos diários, semanais, mensais e anuais. Seguem abaixo algumas figuras dos gráficos que podem ser gerados com a ferramenta. Figura 4 - Gráfico do tráfego diário A Figura 4 mostra um exemplo de saída do CACTI, onde é mostrado o consumo de banda do link Internet. Isso possibilita vermos se o link está sobrecarregado e em que horários do dia isso acontece. A Figura 5 mostra uma visão detalhada do funcionamento do servidor de e-mail, indicando a quantidade de mensagens enviadas, recebidas e descartadas. Além disso, também é mostrado o total de spams e vírus recebidos. Figura 5 - Gráfico com estatísticas de e-mails 1. Qual protocolo que o CACTI utiliza para monitoramento da rede? 2. Entre no site do projeto CACTI (www.cacti.net/screenshots.php) e veja mais exemplos de gráficos que podem ser gerados com a ferramenta. Instalação do CACTI A instalação do CACTI em servidores com distribuições Linux baseadas no Debian, como é o caso do Ubuntu, é bem simples. Para instalar o pacote, digite o comando a seguir. apt-get install cacti Você verá que existem dependências que serão automaticamente instaladas, como, por exemplo, PHP, MySQL eApache. Durante a instalação, um assistente solicita algumas informações importantes, como: • Uma nova senha para o usuário root do MySQL (caso não estiver instalado anteriormente em sua máquina). • Servidor web deverá ser configurado para o Cacti; escolhemos o apache2. • Em seguida o assistente pergunta se deve utilizar o utilitário dbconfig-common para configurar o banco de dados do cacti; informamos que sim. • Uma senha e a sua confirmação para banco de dados do cacti. • A senha do usuário administrativo (root) do MySQL com a qual será criada a base de dados para o cacti; esta é a mesma senha do usuário root criada durante a configuração do MySQL. Depois de terminada a instalação e configuração dos pacotes, vamos à configuração do sistema Cacti, que é bem simples. Primeiramente acessamos o endereço https://localhost/cacti em nosso navegador web e terminamos a instalação do cacti clicando em “Next”, “Next” e “Finish”. Depois de terminada a instalação, já teremos a tela de login, então informamos o usuário como sendo “admin” com senha “admin” (padrão). Depois disso é solicitada a criação da nova senha do nosso usuário administrativo. Configurando o CACTI Para começarmos a monitorar com o cacti devemos criar um “device” através do menu “Console” -> “Create devices for network”. Na tela de “devices” clicamos em “add” no canto superior direito. Para criar um device precisamos preencher o campo “Descripton” (uma descrição para o device), “Hostname” (o nome completo de domínio do deviceou o endereço IP do mesmo caso não tenhamos um DNS configurado para resolver o nome do device) e “Host Template”, no nosso caso ”Generic SNMP-enabled Host”. Na parte de SNMP Options, modificaremos essencialmente a versão do nosso protocolo e o nome da SNMP Community, de acordo com a suportada pelo device que estamos adicionando. Ao clicarmos em “create”, o device é criado e já podemos observar se a comunicação com o device via SNMP foi estabelecida com sucesso através da seção “SNMP Information” no topo da tela. Nesta mesma tela de configuração do device adicionamos os templates dos gráficos que precisamos. Na seção “Associated Graph Templates” adicionamos os gráficos “ucd/net - CPU Usage”, “ucd/net - Load Average” e “ucd/net - Memory Usage”. O template do gráfico para coleta das estatísticas da interface é feito na seção “Associated Data Queries” e já é configurado por padrão. Para finalizar, criamos os gráficos através do link “Create Graphs for this Host” na mesma tela do device (parte superior) ou pelo menu “Console” -> Create graphs for your new devices”. Na tela de criação dos gráficos selecionamos os gráficos que precisamos e clicamos em “create”. A partir deste momento e dentro de no máximo cinco minutos já podemos acompanhar os gráficos pelo menu “graphs”. É possível distribuir nossos gráficos em uma árvore para melhor organização dos mesmos. Para isso, devemos ir em “Graph Management” e em seguida selecionar os gráficos para a alteração. Marque os 3 gráficos recém criados e, onde existe “Choose an Action”, escolha “Place on a Tree” e em seguida clique em “GO”; depois escolha em que parte da árvore eles serão inseridos (como nossa árvore está somente com uma raiz, só existe a opção “root”). Um detalhe importante para o SNMP funcionar é ativar o protocolo SNMP para coleta remota em servidores a serem monitorados, como visto anteriormente nesta aula. A configuração de outros equipamentos, como por exemplo switchs, é bem semelhante à mostrada anteriormente, sendo necessário apenas mudar o nosso Graph Template para “SNMP – Generic OID Template” e selecionar que interfaces queremos monitorar. 1. Após ter criado o nosso gráfico descrito acima, tente criar outros tipos de gráficos que o programa oferece. 2. Para melhor visualização da configuração do programa, veja este vídeo que mostra uma configuração básica do Cacti: 3. Faça uma pesquisa sobre outro software muito utilizado no monitoramento de redes, o NAGIOS. Veja como ele funciona e faça uma relação de suas funcionalidades com as descritas acima. Resumo Nesta aula você aprendeu que toda rede precisa ser monitorada constantemente para identificação de falhas e problemas de desempenho; além disso, reconfigurações também precisam ser realizadas com frequência. Aprendemos também que essas tarefas de monitoramento e configuração formam o que é chamado de gerência de redes. Vimos que a forma mais comum de realizar a gerência de uma rede é através do protocolo SNMP, que utiliza como base para seu monitoramento agentes e gerentes. Aprendemos que as MIBs são de vital importância para o nosso gerenciamento e que precisam estar instaladas nos equipamentos. Finalmente, você aprendeu sobre o CACTI, que é um software muito utilizado como apoio na tarefa de gerência. 1. Quais as mensagens do protocolo SNMP que estudamos nesta aula? 2. O que é uma mensagem Trap? 3. O que é uma comunidade SNMP? 4. Cite quatro grupos e dois objetos de cada grupo para a MIB-II. 5. Qual a reação entre RMON e SNMP? 6. Quais elementos você iria monitorar com o CACTI em um switch? Redes de Computadores Aula 8 – Sistema de Nomes de Domínio (DNS) Nesta aula você aprenderá um serviço que nos auxilia enormemente na designação de endereços para os computadores, permitindo o uso de nomes ao invés de números, o que facilita as suas memorizações. Você verá que, através do Sistema de Nomes de Domínio (DNS –Domain Name System), é possível distribuir esta tarefa árdua de dar nomes a todos os computadores da Internet no mundo inteiro, de maneira estruturada e bem organizada. Para isso, será preciso você perceber o funcionamento do DNS como um grande sistema distribuído, onde parte do processamento e do armazenamento das informações é distribuído de maneira hierárquica em todos os computadores que fazem parte desse sistema. • Entender por que um protocolo de resolução de nomes é necessário. • Conhecer a arquitetura do DNS para entender como as informações dos endereços são organizadas e distribuídas entre os computadores deste sistema. • Saber como configurar um servidor de DNS no Linux. • Entender o funcionamento do protocolo observando sua execução entre um cliente e um servidor de DNS. Por que um sistema de nomes é necessário Para identificarem uma máquina, os protocolos TCP/IP usam o endereço IP, que identifica unicamente um host conectado à Internet. Entretanto, as pessoas preferem usar nomes, em vez de endereços numéricos, pois é muito mais fácil memorizar. Assim, seria interessante termos um sistema que pudesse fazer o mapeamento de um nome para um endereço IP, ainda que este sistema pudesse também mapear um endereço IP para um nome, a fim de identificar a qual host determinado endereço IP está relacionado. Quando a Internet era pequena, o mapeamento era feito usando um arquivo de host. O arquivo de host tinha apenas duas colunas: endereço IP e nome. Todo host podia armazenar o arquivo de host em seu disco e atualizá-lo periodicamente com base em um arquivo de host mestre. Se um programa ou um usuário quisesse fazer o mapeamento de um nome para um endereço, o host consultava o arquivo de host e encontrava o mapeamento. Por questões de compatibilidade com as aplicações legadas, até hoje existe o arquivo de host em máquinas que possuem a pilha de protocolos TCP/IP instalada. Nos computadores com sistemas operacionais derivados do UNIX, como o Linux, este arquivo é o /etc/hosts. Nele, em cada linha, há um mapeamento endereço IP - nome. Assim, se não houver um sistema de nomes (como o DNS, que iremos estudar nesta aula) rodando na rede, é possível fazer a tradução de nomes para endereços IP através do mapeamento neste arquivo. Atualmente, entretanto, devido ao tamanho das redes de computadores e a Internet, é praticamente impossível ter um único arquivo de host para relacionar cada endereço IP a um nome, e vice-versa. O arquivo de host seria grande demais para armazenar em cada host. Além disso, seria impossível atualizar todos os arquivos de host sempre que houvesse uma alteração. Uma solução seria armazenar o arquivo de host inteiro em um único computador e permitir o acesso a essa informação centralizada a cada computador que precisasse do mapeamento. Mas isso geraria um volume de tráfego enorme na Internet, além de uma árdua tarefa de designar nomes únicos para todos os computadores no mundo. Outra solução, a utilizada atualmente, é dividir esse enorme volume de informações em partes menores e armazenar cada parte em um computador diferente. Nesse método, o host, que precisa de mapeamento, pode entrar em contato com o computador mais próximo que contenha a informação necessária. Esse método é usado pelo Sistema de Nome de Domínio (DNS). Nomes de Domínio Para não serem duplicados, os nomes atribuídos aos computadores na Internet devem ser cuidadosamente selecionados, de tal forma que eles devem ser únicos, pois os endereços IP já são. O espaço de nomes definido no DNS que faz o mapeamento de cada endereço para um nome único é organizado de maneira hierárquica. No espaço de nomes hierárquicos do DNS, cada nome é constituído por várias partes, na forma de uma árvore similar ao de um diretório (ou pasta) no sistema de arquivos do computador. A primeira parte do nome (diretório) define a natureza da organização ou o país; a segunda pode definir o nome de uma organização ou a sua natureza, caso a primeira parte tenha sido o país; a terceira pode definir departamentos na organização, e assim por diante. Nessa estrutura hierárquica, a autoridade para atribuir e controlar os espaços de nomes fica descentralizada e dividida entre as organizações. Uma autoridade central pode atribuir a parte do nome que define a natureza e o nome da organização. A responsabilidade pelo restante do nome pode ser transferida para a própria organização. A organização pode adicionar prefixos no nome para definir seu host ou seus recursos. A gerência de uma organização não precisa se preocupar com o fato de o prefixo escolhido para um host ser adotado por outra, pois, mesmo que parte de um endereço seja igual, o endereço inteiro é diferente. Na Figura 1, vemos a estrutura do espaço de nomes de domínio usado no DNS. Ela é representada como uma árvore que pode ter no máximo 128 níveis: do nível 0 (raiz) ao nível 127. Na prática, um pequeno número de níveis é utilizado. Figura 1 – Espaço de nomes de domínio. Fonte: Forouzan (2008). 1. Qual o principal problema que o DNS visa solucionar? 2. Faça uma pesquisa para descobrir que órgão nacional é responsável pelo domínio .BR. Dica de site: . Cada nó na árvore tem um rótulo, que é uma série com, no máximo, 63 caracteres. O rótulo da raiz é uma série nula (vazia). O DNS exige que os filhos de um nó (nós que ramificam do mesmo nó) tenham rótulos diferentes, o que garante a unicidade dos nomes de domínio. Cada nó na árvore tem um nome de domínio. Um nome de domínio completo é uma sequência de rótulos separados por pontos (.). Os nomes de domínio são sempre lidos do nó a raiz. A Figura 2 mostra um exemplo da árvore de domínios para o nome do domínio metropoledigital.ufrn.edu.br. Figura 2 – Nomes de domínio e rótulos Um nome de domínio totalmente qualificado (FQDN – Fully Qualified Domain Name) é um nome que contém o nome completo de um host. Ele contém todos os rótulos, do mais específico ao mais geral, que definem exclusivamente o nome do host. Por exemplo, o nome do domínio www.metropoledigital.ufrn.edu.br é o FQDN de um computador chamado www, instalado no Metrópole Digital, da Instituição UFRN, que é uma entidade educacional (edu), que fica no Brasil (br). Um domínio é uma subárvore do espaço de nomes de domínio. O nome do domínio é o mesmo do nó que está no topo da subárvore. A Figura 3 mostra alguns domínios. Note que um domínio pode, ele mesmo, ser dividido em domínios (ou subdomínios, como às vezes são chamados). Figura 3 - Domínios e subdomínios Fonte: Forouzan (2008). 1. É possível que um rótulo de domínio tenha 70 caracteres? 2. O que é um nome de domínio totalmente qualificado? Cite um exemplo. Distribuição do espaço de nomes As informações contidas no espaço de nomes de domínio devem ser armazenadas em computadores servidores espalhados na Internet, chamados de servidores DNS. Como vimos na Figura 3, o DNS permite que os domínios sejam subdivididos em domínios menores, chamados de subdomínios. Cada servidor de DNS pode ser responsável (ter autoridade) por um domínio grande ou pequeno. Um servidor é responsável ou tem autoridade sobre o que é chamado de zona. Podemos definir uma zona como uma parte da árvore de nomes. Se um servidor aceitar a responsabilidade por um domínio e não o dividir em domínios menores, o “domínio” e a “zona” se referem à mesma coisa. O servidor faz um banco de dados chamado arquivo da zona e mantém todas as informações sobre cada nó sob esse domínio neste arquivo. Entretanto, se um servidor dividir seu domínio em subdomínios e delegar parte da sua autoridade a outros servidores, “domínio” e “zona” referem-se a coisas diferentes. As informações sobre os nós nos subdomínios são armazenados na zona dos servidores dos níveis inferiores, com o servidor original mantendo uma referência para esses servidores de nível mais baixo. É claro que o servidor original não fica totalmente liberado da responsabilidade: ele ainda tem uma zona, mas as informações detalhadas são mantidas pelos servidores de nível mais baixo. A Figura 4 mostra um exemplo do domínio br (nomes de domínios no Brasil), com subdomínios com diferentes servidores DNS sendo responsáveis pelas diferentes zonas. Nesta Figura, “Delegação” indica uma transferência de responsabilidade na administração dos nomes a partir daquele ponto na árvore DNS. Figura 4 – Zonas e domínios 1. Como são chamados os computadores na Internet em que ficam contidas as informações do espaço de nomes de domínio? 2. Explique como é feita a delegação da responsabilidade na administração dos nomes na árvore DNS. Servidores principais e secundários O DNS define dois tipos de servidores: principais e secundários. Um servidor principal é aquele que teve delegada a autoridade sobre uma zona. Ele é responsável por criar, manter e atualizar as informações desta zona. Ele armazena estas informações de zona em um arquivo no disco local. Um servidor secundário é aquele que transfere as informações completas sobre uma zona de um servidor principal e armazena o arquivo em seu disco local. O servidor secundário não cria nem atualiza os arquivos de zonas. Resolução DNS O DNS é projetado como um aplicativo cliente-servidor. Um host que precisa fazer o mapeamento de um nome para um endereço IP (ou vice-versa) chama um cliente DNS denominado resolvedor (resolver). O cliente (resolvedor) acessa o servidor DNS mais próximo com um pedido de resolução (mapeamento), ou seja, suponhamos que um cliente queira saber o IP da máquina www.google.com.br. Ele irá fazer uma requisição ao servidor DNS de sua rede (ou o mais próximo a ele) perguntando se ele conhece essa máquina. Se o servidor tiver a informação, ele atende ao cliente; caso contrário, ele remete o cliente para outros servidores ou pede a outros servidores para que forneçam a informação. Isto vai depender de como o cliente solicitou a consulta (mapeamento) ao servidor, porque a consulta pode ser recursiva ou interativa. Na consulta recursiva, usada normalmente por um host qualquer e o seu servidor de DNS, o cliente (resolvedor) espera que o servidor forneça a resposta final ao que ele perguntou. Se o servidor for a autoridade do nome de domínio, ele verifica seu banco de dados e responde. Se o servidor não for a autoridade, ele deverá percorrer a árvore a partir da raiz a fim de chegar ao servidor que possui autoridade sobre o domínio consultado, e deste obter a resposta desejada pelo cliente. A Figura 5 mostra este processo entre um cliente e seu servidor DNS, no qual o cliente pergunta pelo endereço IP associado ao nome exemplo.foo.eng.br. • A mensagem 1 é a pergunta do cliente ao seu servidor de DNS. • A mensagem 2 é a consulta do servidor DNS recursivo à raiz perguntando pelo servidor do domínio br. • A mensagem 3 é a resposta à pergunta 2 na qual o servidor raiz passa a referência (o endereço IP) do servidor DNS responsável pelo domínio br. Figura 5 – Resolução recursiva entre um cliente e um servidor DNS • Na mensagem 4, o servidor recursivo pergunta ao servidor com autoridade sobre o domínio br a referência ao domínio eng.br. • A mensagem 5 é a resposta à pergunta da mensagem 4, já referenciando o servidor do domínio foo.eng.br, pois o servidor de br também tem autoridade sobre o domínio eng.br. • Nas mensagens 6 e 7, estão a pergunta e a resposta, respectivamente, ao servidor com autoridade sobre o nome perguntado inicialmente pelo cliente. • Por fim, na mensagem 8 o servidor DNS recursivo repassa a resposta obtida ao cliente. A consulta interativa é a utilizada entre os servidores de DNS para percorrer a árvore de nomes de domínio. Por exemplo, na Figura 5, as consultas do servidor DNS consultado inicialmente pelo cliente aos outros servidores DNS, iniciando pela raiz, foram feitas de forma interativa. Isto é, há uma interação entre os servidores de DNS a fim de percorrer a árvore de nomes para encontrar a informação consultada. 1. Quais as diferenças entre um Servidor DNS Principal e um Secundário? 2. Caso seja feita uma consulta de um nome que não exista em um Servidor DNS, qual a ação que ele deverá tomar? 3. Explique, através de um exemplo, como é feita a consulta interativa. Uso de cache Sempre que um servidor recebe uma consulta de um nome que não está em seu domínio, precisa procurar um endereço IP de servidor em seu banco de dados. A redução desse tempo de pesquisa aumentaria a eficiência. O DNS trata disso com um mecanismo chamado cache. Quando um servidor solicita um mapeamento a outro servidor e recebe a resposta, armazena essa informação em sua memória cache, antes de enviá-la ao cliente. Se o mesmo cliente ou outro solicitar o mesmo mapeamento, o servidor pode verificar sua memória cache e resolver o problema. Entretanto, para informar o cliente de que a resposta está vindo da memória cachê, e não de uma origem autorizada, o servidor marca a resposta como não autorizada. Arquivo de zona O banco de dados que contém as informações sobre um domínio é chamado de arquivo de zona. Este é um arquivo texto contendo vários registros de dados a respeito daquele domínio. Tais registros são conhecidos como registro de recursos (resource records). Uma mensagem de consulta DNS deve conter o nome pesquisado e o tipo de registro a ser pesquisado. Os tipos de registros de recursos definidos em um arquivo de zona são os mostrados na Tabela 1. Registro Significado SOA Indica onde começa a autoridade (sobre o domínio). NS Indica um servidor de nomes para uma zona. A Mapeamento de nome a um endereço IP (IPv4). AAAA Mapeamento de nome a um endereço IP (IPv6). MX Indica um mail exchanger (servidor de e-mail) para um domínio. CNAME Mapeia um nome alternativo (apelido) para um host. PTR Mapeamento de um endereço IP a um nome de host. HINFO Informação de um host. Fornece a descrição do hardware e do sistema operacional usados por um host. WKS Serviços conhecidos. Define os serviços de rede que um host fornece. Tabela 1 – Tipos de registros de recursos no DNS A Figura 8 mostra um exemplo de arquivo de zona típico para uma empresa fictícia cujo domínio de Internet seria empresa.com.br. Figura 8 – Exemplo de arquivo de zona Neste arquivo de zona podemos notar o uso dos registros de recursos vistos na Tabela 1, como por exemplo o SOA e o CNAME. Cada linha simples (que não possui sub-registros) é composta dos seguintes campos: nome, a palavra “IN” indicando Internet, o tipo de registro e os dados sobre o mapeamento. O segundo campo contendo sempre “IN” se dá em razão de que o projeto do sistema DNS previa a resolução de nomes para outras redes ou serviços, o que atualmente não acontece. No arquivo de zona da Figura 8, o caractere arroba (@) é uma referência simplificada ao próprio domínio; neste caso, significa “empresa.com.br”. O caractere ponto-e-vírgula (;) representa início de comentário e não é interpretado pelo servidor DNS. Os nomes que não encerrarem com um ponto final (.) serão acrescidos com o nome do domínio. Neste exemplo, o nome www, que aparece sem o ponto final, será interpretado como www.empresa.com.br. O significado de cada linha é explicado na Tabela 2. Registro Linhas Descrição SOA 01 Mostra informações sobre a autoridade do domínio. Os dois primeiros dados são o servidor DNS principal e o e-mail (substituindo o arroba “@” por ponto “.”) do administrador deste domínio. Os demais dados, listados entre parênteses, dizem respeito ao controle das atualizações deste mapa entre os servidores de DNS que obtêm informações sobre o domínio. NS 09 e 10 Indica o servidor de nomes do domínio. Normalmente, usa-se o FQDN do servidor DNS. Neste exemplo, temos dois servidores de DNS para o domínio, um primário e um secundário. MX 11 Indica o servidor de e-mail do domínio. Este registro exige um número antes do nome do servidor. Este número indica a prioridade (em sequência direta) para se utilizar cada servidor, caso haja mais de um. A 12, 14 e 15 Mapeamento de um nome para um endereço IP. Observe que todos os nomes de hosts utilizados no mapa da zona possuem um registro A para o endereço IP correspondente. CNAME 13 Define um segundo nome (canonical name ou apelido) para um determinado nome de host. Isto é particularmente interessante quando temos vários serviços no mesmo host, e se utiliza o nome do serviço para o host correspondente. Neste exemplo, o serviço de mail é também servido pelo servidor web. Se o endereço IP deste servidor multisserviço mudar, basta mudar apenas em um registro. Tabela 2 - Tipos de registros tipicamente utilizados em um mapa de domínio 1. Qual o principal benefício de se usar o sistema DNS em uma rede de computadores? 2. É possível usar algum outro mecanismo de tradução de nomes para endereços IP sem usar o sistema DNS? 3. Pesquise na Internet quais são os servidores raiz do sistema DNS e onde eles estão localizados no mundo. Sempre que alterar o conteúdo do arquivo de uma zona, como o mostrado na Figura 8, altere o valor do campo “serial” para o valor que estiver lá + 1. Os servidores de DNS secundários usam esse número para saber se houve alterações no arquivo. Configurando um servidor DNS Agora, para uma melhor fixação dos conceitos que vimos até aqui, é de extrema importância que você esteja em seu computador para que possa reproduzir os passos que serão descritos a seguir. A primeira coisa que precisamos é de um servidor de DNS. Vamos assumir que temos duas máquinas, chamadas A e B, e a máquina A será o servidor, enquanto a máquina B será o cliente de DNS. O software servidor DNS no Linux é feito através do daemon (processo servidor)named, e o mesmo vem distribuído no pacote bind9. Vamos assumir que este pacote já está instalado na máquina A. BIND (Berkeley Internet Name Domain) é uma implementação pioneira de um software servidor de DNS publicamente distribuída nos sistemas operacionais UNIX e seus derivados, como o Linux. Ele, em sua versão 9, é o servidor DNS mais utilizado no mundo. O arquivo principal de configuração do servidor BIND é o /etc/bind/named.conf. A partir da versão 9 do BIND, este arquivo possui apenas referências a outros três arquivos dentro do diretório /etc/bind, dividindo os dados das configurações nestes arquivos: named.conf.options, named.conf.local e named.conf.default-zones. O named.conf.options contém as opções gerais de configuração do servidor DNS. O named.conf.default-zonespossui referência para as zonas padrão (default) no sistema DNS, como o nome padrão localhost que todo computador recebe quando possui conectividade TCP/IP. O arquivo que iremos alterar será o named.conf.local, pois ele deve possuir referências para as zonas que serão criadas neste servidor DNS. Com o gedit, abra o arquivo /etc/bind/named.conf.local e insira, logo após os comentários (linhas começando com duas barras “//”), as linhas de configuração da zona “metropoledigital.ufrn.br”, a qual iremos assumir neste servidor DNS, conforme a Figura 9. Esta configuração de zona exige pelo menos dois parâmetros: o tipo de servidor (primário ou secundário) e qual o arquivo contendo as informações sobre a zona. Neste exemplo, este servidor será do tipo primário (master). Se ele fosse secundário (slave), a próxima linha deverá conter o parâmetro “masters”, onde deveriam ser informados os endereços IP dos servidores primários. O segundo parâmetro será “/etc/bind/metropoledigital.ufrn.br.zone”, no qual colocaremos as informações sobre os registros desta zona. Figura 9 - Inserindo a zona no servidor DNS Agora criaremos nosso arquivo da zona “metropoledigital.ufrn.br”. Ainda no gedit, clique no ícone “Novo” para abrir uma nova aba para um outro arquivo. Digite o texto (sem a numeração das linhas) de acordo com a Figura 10. Ao final da digitação, salve este arquivo como /etc/bind/metropoledigital.ufrn.br.zone. Figura 10 – Arquivo de zona Salve os dois arquivos e feche o gedit. Agora, reinicie o servidor de DNS, bind, conforme a Figura 11. Figura 11 – Reiniciando o servidor DNS após a criação da zona Neste ponto, ao ligarmos a máquina B, podemos observar que ela está pegando sua configuração IP por DHCP (feita na aula anterior). Uma das informações fornecidas por DHCP foi o endereço IP do servidor de DNS. Este é o parâmetro principal para o resolvedor (cliente DNS) na máquina B. Para observar todos os parâmetros informados ao resolvedor, podemos exibir o conteúdo do arquivo /etc/resolv.conf nesta máquina, conforme Figura 12. Este arquivo que, neste caso, foi criado dinamicamente pelo DHCP é quem diz à máquina quem é o seu servidor de DNS. Figura 12 – Arquivo de configuração do cliente DNS Observe, na Figura 12, que o endereço IP do servidor de DNS é informado no parâmetro nameserver. Os parâmetros domain e search definem o nome do domínio para a nomenclatura das máquinas e o domínio padrão de pesquisa FQDN quando alguém informar somente o primeiro nome de um host, respectivamente. Vamos agora praticar algumas pesquisas de DNS na máquina B consultando o nosso servidor de DNS instalado na máquina A. Para isso, utilizaremos o aplicativo de linha de comando chamado nslookup. Este aplicativo é um cliente do serviço DNS muito utilizado pelos administradores de rede para investigar as configurações deste serviço em qualquer rede ligada à Internet. Ao digitar nslookup na linha de comando, você abre este aplicativo, que lhe apresenta outro prompt de comandos, no qual você digitará aquilo que deseja pesquisar no sistema DNS. Podemos testá-lo digitando o nome www e pressionando a tecla ENTER, conforme a Figura 13. Figura 13 – Prompt de comandos do nslookup Observe o que ele mostra como resultado. Primeiro as informações do servidor de DNS consultado, seu endereço IP e a porta UDP utilizada na consulta. Em seguida, ele mostra o nome FQDN consultado e o endereço IP mapeado para este nome. Ele acrescentou o domínio “metropoledigital.ufrn.br” ao nome pesquisado devido o parâmetro search do /etc/resolv.conf, na Figura 12. Veja que não especificamos o tipo de registro que queríamos e ele respondeu com uma resposta do tipo “A” (address), endereço IP do nome, pois este é o tipo de consulta/registro padrão. Vamos agora mudar o tipo de consulta ao nosso servidor. Para isso, precisamos digitar o comando “set type=X”, onde X representa o tipo de registro de recurso no DNS, conforme a Tabela 1. Vamos pedir o registro SOA do domínio desta prática, conforme a Figura 14. Figura 14 – Solicitação do registro SOA do nosso domínio Vamos, agora, testar consultar um nome no qual nosso servidor não possui autoridade. Conforme a Figura 15, vamos consultar o registro SOA do domínio ufrn.br e observar o resultado. Figura 15 – Consultando o registro SOA de outro domínio Veja que apareceu a linha “Non-authoritative answer:”, seguido da resposta contendo o registro SOA do domínio pesquisado. Esta linha indica que o servidor que respondeu a consulta não tem autoridade sobre o domínio (nome) consultado, e que ele obteve esta resposta a partir de consulta a outros servidores, no esquema de consultas recursivas percorrendo a árvore hierárquica dos nomes. Ainda na Figura 15, observe que é possível saber qual é o servidor que pode dar uma resposta autorizada sobre o domínio em questão, neste exemplo um dos dois servidores listados, os quais são os servidores primário e secundário do domínio. Para realizar uma consulta sobre o servidsetor DNS de um nome, deve-se mudar a consulta para o registro NS com o comando "set type=ns" e, em seguida, digita-se o nome do domínio a ser consultado, conforme a Figura 16. Figura 16 – Consultando os servidores de nomes de um domínio. Observe na Figura 16 que a pergunta aos servidores de nomes do domínio do Google retornou quatro servidores. Vamos utilizar um deles para a consulta ao registro MX (servidor de e-mail) do domínio gmail.com, conforme a Figura 17. Para mudarmos o servidor a ser consultado, devemos digitar o comando “serverIP_ou_nome_do_servidor”. Figura 17 - Consultando outro servidor de DNS Observe na Figura 17 que não surgiu mais a linha informando uma resposta não autorizada. Isto porque a resposta veio de um servidor que tem autoridade sobre o domínio consultado. Neste exemplo, gmail.com é o domínio do serviço de e-mail do Google. Observe também que apareceu uma lista de servidores de e-mail, cada um com sua prioridade (quanto menor o número, maior a prioridade) para entrega das mensagens neste domínio. 1. Caso seja necessário saber se o nosso servidor DNS está funcionando, qual nome do processo deveremos procurar na lista de processos ativos? Dica: execute no terminal o comando “ps aux”. 2. Para que serve o comando nslookup? 3. Pesquise sobre uma ferramenta muito usada no Linux para consulta de DNS chamada dig. Veja suas funcionalidades e como ela é usada. Dica: Lembre-se que existem manuais de vários programas no Linux; para verificar o manual do dig, use o comando “man dig”. Após tudo isso que lhe foi apresentado, podemos então ver como o protocolo DNS pode ser útil em um ambiente de rede, pois nunca seremos capazes de decorar todos os endereços IPs das máquinas que acessamos em nossa rede ou até mesmo na Internet. A configuração correta do serviço é de extrema importância para que ele funcione como desejado; assim, devemos sempre procurar ver qual a melhor distribuição dos domínios a serem usados, a fim de que sejam administrados mais facilmente pelo gestor da rede. Que tal você acessar: • Registro.BR - • OpenDNS - • Procure também informações sobre o No-IP. Resumo Nesta aula você aprendeu que em uma rede é necessário que exista um serviço de tradução de nomes para endereços IP a fim de evitar que tenhamos que decorar números ao acessarmos serviços em computadores na rede. Este serviço de nomear todos os computadores ligados à Internet em todo o planeta é o DNS. Você observou que ele trabalha de maneira distribuída e segue uma estrutura de distribuição baseada em árvore hierárquica. Nesta árvore de servidores, cada um fica responsável pelo domínio de nomes da organização do qual faz parte, permitindo criar subárvores para nova divisão dos nomes dentro deste domínio. Você também percebeu que, para aumentar a disponibilidade do sistema, há os servidores primários e os secundários, e para otimizar os tempos das consultas os servidores guardam temporariamente as consultas já realizadas em cache. Por fim, praticou a configuração de um servidor DNS para um domínio de nomes e realizou consultas a este e outros domínios a partir de um aplicativo cliente, o nslookup. Em seu computador, abra o VirtualBox e acesse o Laboratório1 desta aula (Aula8). Em seguida, realize os seguintes procedimentos. 1. Repita todos os passos vistos nesta aula para configurar seu servidor DNS na máquina A. 2. Altere o arquivo da zona do domínio metropoledigital.ufrn.br acrescentando o nome ftp para o IP 10.1.1.20, e incremente o número serial desta zona. Reinicie o bind9 para que estas alterações tenham efeito. 3. Faça consultas DNS ao servidor procurando pelo nome ftp. 4. Repita o passo 2, colocando agora ftp como um apelido pra www e execute nova consulta a este nome. O que mudou nas respostas? Formação de web pagina Desenvolvimento Web Aula 1 – Ambiente de desenvolvimento para Web em Java Olá, caro aluno. Hoje iniciaremos nossos estudos dentro da disciplina de Desenvolvimento Web. A linguagem de programação que utilizaremos será a linguagem Java, inclusive por ter todos os recursos necessários para entender como se constroem aplicações Web. Você irá aprender diversos conceitos e tecnologias de programação para a Web, como por exemplo, Servlets, JSP e Ajax. As aulas dessa disciplina foram preparadas com muito esmero e dedicação e esperamos que ao final o nosso objetivo seja alcançado por todos. As nossas aulas terão exemplos práticos e atividades que ajudarão você a fixar o conhecimento e a estimular o seu aprendizado. Também teremos atividades extra-aula que ajudarão a dar mais um passo nesta disciplina. Esperamos que você tenha muitas dúvidas e que juntos possamos aprender: você comigo e eu com você. Lembre-se: qualquer dúvida, não hesite, pergunte. Muito bem, vamos agora começar o nosso assunto. Nessa aula você aprenderá a instalar e configurar o nosso ambiente de desenvolvimento e debug. Para isso, vamos aprender a organizar e instalar três ferramentas muito importantes para o nosso estudo. São elas: Eclipse, Apache Tomcat e o JDK. Então, vamos colocar a mão na massa?! Ao ler esta aula, você estará apto a: • Instalar os softwares necessários para construir, executar edebugar suas aplicações em um ambiente J2EE. Baixando, instalando e configurando nosso ambiente Dos três softwares mencionados na apresentação, o que você realmente precisa é do Apache Tomcat, que é uma implementação de código aberto para os ambientes de Servlets e JSP, e o do JDK, que traz consigo as classes padrão Java e a máquina virtual para executar estas classes. O Eclipse é apenas uma ferramenta que facilita o nosso desenvolvimento, mas nada nos impediria de construirmos nossas aplicações sem ele. Porém, o esforço e o tempo necessários para isso não compensariam não usá-lo. Vamos primeiro baixar e instalar o JDK. Para isso, faça o download do JDK versão 6 na seguinte URL: . Uma vez concluído o download, vamos agora instalar o JDK. Para isso, use a instalação padrão, sem maiores intervenções. Depois de instalar o JDK, vamos baixar e descompactar o Apache Tomcat, um dos servidores Web mais conhecidos no mercado. Para isso acesse a seguinte URL: . Apesar de ter uma versão para instalação, é necessário instalar o Apache Tomcat. Basta baixar o seu arquivo zip e descompactá-lo no diretório de sua preferência. Entretanto, para fins dessa aula, indicamos que você descompacte o arquivo na pasta c:desen. Assim, ao término deste passo você terá uma pasta chamada apache-tomcat-6.0.29 dentro da pasta c:desen. Pronto, viu como foi fácil ter o ambiente mínimo necessário para executar suas futuras aulas? Porém, o nosso objetivo é fazer com que você use um ambiente de desenvolvimento integrado e que facilite a construção e testes das suas aplicações; então, para isso vamos agora instalar e configurar o Eclipse. Aqui, a primeira coisa a fazer é baixar o Eclipse. O arquivo para download poderá ser encontrado aqui: . Depois de baixá-lo, você terá que descompactar o arquivo e, mais uma vez, a pasta destino será c:desen. Quando o processo de descompactação terminar, você terá o diretório eclipse dentro da pasta c:desen. Bom, agora tudo foi instalado. Então, vamos configurar nosso ambiente. Para isso execute o arquivo eclipse.exe, que está em c:desenclipse. Assim que você executar este arquivo, uma janela como a mostrada a seguir será exibida pedindo que você escolha o seu workspace. Neste caso, vamos criar um diretório chamado workspace em c:desen e escolhê-lo. Figura 1 Feito isso, a próxima janela que será exibida será como a da figura abaixo. Nesta janela você encontrará algumas opções para quem é iniciante, porém vamos direto para a opção workbench (opção indicada figura abaixo), pois essas opções podem ser exibidas a qualquer momento. Figura 2 Pronto, agora estamos vendo o nosso ambiente de desenvolvimento que de agora em diante nos acompanhará na execução das nossas atividades. Bom, antes de instalarmos o Eclipse nós instalamos o JDK e o Apache Tomcat. E agora, como integrar o JDK e o Apache Tomcat com o Eclipse? Com relação ao JDK nós não precisamos nos preocupar, pois oEclipse, ao ser executado, descobre onde o JDK foi instalado, então nenhuma configuração é necessária. E o Apache Tomcat? Bom, a versão do Eclipse que nós baixamos e instalamos é uma versão para desenvolvedores J2EE e nesta versão ela vem com um plugin chamado Web Tools Platform (WTP). Esseplugin permite a integração do Eclipse com a maioria dos servidores J2EE do mercado, entre eles o Apache Tomcat. Para que essa integração funcione temos que informar ao plugin onde o Apache Tomcat foi instalado. Então vamos lá: No menu do Eclipse, selecione a opção Window -> Prefences. Uma janela como a da figura abaixo será exibida. Clique na seta ao lado de Server, como indicado na Figura 3. Figura 3 Ao fazer isso, as opções de servidores serão exibidas. Clique em Runtime Environment e em seguida no botão Add..., como mostrado na figura a seguir. Figura 4 Uma nova janela será exibida, como mostrado na Figura 5. Selecione a opção Apache, em seguida a opção Apache Tomcat v6.0, marque o checkbox Create a new local server e, por último, a opção Next, como indicado na figura. Figura 5 Na próxima janela, clique no botão Browse, selecione o diretório C:desenapache-tomcat-6.0.29 e depois clique em Finish, como mostrado na figura a seguir. Figura 6 Para concluir a integração, confirme tudo clicando no botão OK. Agora temos o Eclipse integrado com o Apache Tomcat e, a partir de agora, quando criarmos nossos projetos WEB já poderemos informar ao Eclipse que estes serão testados neste Apache Tomcat. Além disso, como veremos mais à frente, o plugin do WTP nos permite executar o Apache Tomcat de dentro doEclipse em dois modos de execução diferentes e em um deles podemos fazer debug das classes. Não é legal? Bom, agora vamos criar o nosso primeiro projeto WEB usando o Eclipse. Este projeto será criado e integrado com o Apache Tomcat que nós configuramos no passo anterior. Usando o menu do Eclipse, selecione File -> New -> Other..., como mostrado na Figura 7. Figura 7 Na tela seguinte, selecione a opção Web, Dynamic Web Project e em seguida Next, como mostrado na Figura 8. Figura 8 Na tela seguinte, defina o nome do projeto como “aula” e em seguida clique em Next, como mostrado na figura abaixo. Figura 9 Nas telas seguintes, basta clicar no botão Next e Finish. Agora vamos dizer que o deploy desta aplicação será feita no nosso Apache Tomcat antes configurado. Para isso, vamos seguir os seguintes passos: • Clique na visão Servers e, em seguida, com o botão direito do mouse, Tomcat v6.0 Server at localhost [Stopped], como mostra a figura a seguir. Figura 10 Na janela que se abrirá selecione a opção Add and Remove..., como mostrado na Figura 11. Figura 11 Na janela seguinte, selecione o projeto “aula”, clique em Add e em seguida em Finish, como mostrado a seguir. Figura 12 Agora, para testarmos nossa aplicação, vamos criar um arquivo index.jsp na raiz do nosso diretório WEB. Para isso, clique com o botão direito na pasta WebContent no seu projeto “aula” e escolha a opção New -> JSP File, como mostrado na Figura 13. Figura 13 Na nova janela que será exibida, digite no campo File name o nome index.jsp e em seguida clique no botão Finish, como mostrado na próxima figura. Figura 14 No arquivo que será aberto no seu workspace, digite dentro da tag o texto Alou Mundo!!!, como mostrado na Figura 15. Figura 15 Agora vamos colocar nosso servidor pra executar. Novamente clique na visão Servers e em seguida no botão start, como mostrado na Figura 16 a seguir. Figura 16 Na figura acima existe a segunda opção para iniciar o servidor. Nesta opção o servidor é iniciado no mode debug, que nos permite, em tempo de execução, debugar nossas classes e JSPs. Não é legal? Entretanto, esta opção só deve ser usada quando realmente queremos debugar alguma coisa, pois ela deixa o Eclipse um pouco mais lento. Agora vamos ver como ficou nossa primeira página. Para isso, selecione Window -> Show view -> Other, conforme a Figura 17. Figura 17 Em seguida selecione General -> Internal Web Browser, conforme indicado na Figura 18. Figura 18 Na nova visão, digite a url e tecle Enter. Veja o resultado na próxima figura. Figura 19 Viu como foi fácil? Por último, vamos dar uma olhada na estrutura do projeto que foi criado. Perceba que toda a estrutura de uma aplicação WEB foi criada pelo plugin do WTP, inclusive o descritor de deploy web.xml (veremos mais à frente esta estrutura e para que serve este arquivo). Para seguirmos incrementando nossa aplicação, basta criar nossos servlets na pasta Java Resources: src e os nossos JSPs, imagens, css, js dentro da pasta WebContent, que é a pasta raiz da nossa aplicação WEB, como veremos nas próximas aulas. Perceba também que existe um item no nosso projeto chamado Deployment Descriptor. Ao clicar nele, automaticamente será aberto o arquivo web.xml. Tudo é mostrado na figura abaixo. Figura 20 Pronto, concluímos a configuração do Eclipse, Apache Tomcat, JDK e do nosso ambiente de desenvolvimento. Muito fácil, não é? Leitura complementar Acesse os sites das ferramentas Eclipse e Tomcat e procure ler seus manuais (visão geral, tutoriais introdutórios etc.), de forma a complementar seu entendimento sobre essas ferramentas. Resumo A aula de hoje foi introdutória. Você aprendeu como configurar o nosso ambiente de desenvolvimento, criar uma aplicação WEB e como integrá-la com o nosso servidor. Isso nos permite evoluir para as nossas próximas atividades, bem como construir nossos servlets e JSPs dentro de um projeto que é preconfigurado pelo plugin do eclipse WTP. 1. Descreva os passos necessários para estar com o ambiente Eclipse + Tomcat pronto para começar a programação Web. Desenvolvimento Web Aula 2 – Introdução aos Servlets Olá, meu amigo. A partir desta aula você começará a aprender as tecnologias Java utilizadas para a programação de sistemas para a Internet. Na disciplina de Autoria Web, você aprendeu a desenvolver as chamadas páginas Web estáticas, ou seja, aquelas cujo conteúdo é definido previamente, antes do acesso do usuário. Agora é uma boa oportunidade para você relembrar os principais marcadores HTML utilizados na construção das páginas Web estáticas. Isso porque eles continuarão a ser utilizados aqui. Então, na dúvida, não hesite em rever AGORA e no decorrer da aula de hoje os principais conceitos (marcadores HTML, BODY, FORM, A, INPUT etc.) sobre esse assunto! Bom, na aula de hoje você irá começar a entender como funciona a programação de páginas Web dinâmicas em Java. Pense no seguinte: como você iria fazer para implementar uma calculadora com as operações básicas de soma, subtração, divisão e multiplicação? Você já sabe criar páginas HTML para representar a calculadora e certamente sabe o código em Java que implementa essas operações Java. Já a integração dessas coisas você aprenderá agora, nessa aula! Boa leitura. Ao final desta aula você será capaz de: • Entender o ciclo de vida dos Servlets. • Compreender o funcionamento dos métodos de processamento de requisições Web dos Servlets. • Saber a configuração da aplicação Web necessária para o funcionamento dos Servlets. Páginas Web estáticas versus dinâmicas Para começar, vamos ilustrar a diferença entre páginas estáticas e dinâmicas. Pense em um exemplo de sistema que implementa as operações básicas de uma calculadora. Sua página de entrada pode ser a mostrada na Figura 1. Ao digitar 20 e 10 para os campos valor 1 e valor 2, respectivamente, e então clicar no botão *, a tela da Figura 2 é apresentada. Entretanto, se for clicado o botão +, a tela da Figura 3 é que deve ser apresentada. Figura 1 – Tela HTML de um sistema de calculadora na Web Figura 2 – Tela dinâmica de resposta para a multiplicação de 2 x 10 Figura 3 – Tela dinâmica de resposta para a soma de 2 com 10 Como podemos notar, a montagem da página Web de resposta da calculadora precisa ser feita de forma dinâmica, ou seja, em tempo de execução . Dizer que uma página é dinâmica é equivalente a dizer que ela é gerada dinamicamente ou gerada em tempo de execução. Em todos esses casos, o que queremos dizer é que o conteúdo da página não pode ser definido previamente em sua totalidade. No caso da calculadora, o conteúdo da página de resultado a ser apresentado depende dos valores informados pelo usuário! Para criarmos páginas Web dinâmicas em Java, fazemos uso dos chamados Servlets, como iremos lhe mostrar a seguir. Tempo de execução é utilizado em contraste ao chamado tempo de compilação. No tempo de compilação, o programador está alterando o código e compilando para achar erros e gerar os arquivos executáveis. Depois disso, podemos rodar o programa. O período durante o qual o programa está rodando é chamado de tempo de execução. Tecnologia de Servlets Java Para gerarmos páginas dinâmicas no servidor de aplicações Web, fazemos uso de componentes de software chamado Servlets. Você se lembra o que são classes em Java, certo? Aqueles módulos do programa que possuem atributos e métodos. Pois bem, os Servlets são nada mais que classes em Java que, em geral, herdam direta ou indiretamente de uma classe especial chamada HttpServlet existente no pacote javax.servlet.http, como visto no exemplo a seguir. Relembrando, classe é um tipo de dado que descreve o que determinados objetos possuem em termos de atributos e métodos. 01 package aula02; 02 03 import java.io.IOException; 04 import java.io.PrintWriter; 05 06 import javax.servlet.ServletException; 07 import javax.servlet.http.HttpServlet; 08 import javax.servlet.http.HttpServletRequest; 09 import javax.servlet.http.HttpServletResponse; 10 11 public class ServletExemplo extends HttpServlet { 12 protected void doGet(HttpServletRequest request, 13 HttpServletResponse response) 14 throws ServletException, IOException { 15 PrintWriter saida = response.getWriter(); 16 saida.write("Olá!"); 17 saida.close(); 18 } 19 } Atenção Caso esteja em dúvida sobre conceitos de orientação a objetos como herança, polimorfismo e sobrescrita de método, aproveite para reler a aula sobre esse assunto da disciplina de Programação Orientada a Objetos. De maneira geral, a classe ServletExemplo herda de HttpServlet e sobrescreve o método de nome doGet, cujos detalhes serão vistos mais adiante. O conteúdo da página resultante pela execução do Servlet é escrito através do objeto saída, sendo, neste caso, igual ao texto "Olá!". Para isso, acessamos o objeto que representa o canal de comunicação do servidor para o cliente (response.getWriter()). Em seguida, utilizamos o método write(String msg) desse objeto para escrever o conteúdo da página HTML a ser gerada. O parâmetro passado para esse método é uma String que vai formar o conteúdo da página. Na verdade, você pode chamar o método write várias vezes. Por fim, chamamos o método close() para fechar a conexão. Nesse momento, o navegador Web do usuário irá saber que todo o conteúdo da página já foi gerado e pode ser apresentado. Detalhes sobre os parâmetros request e response do método doGet serão apresentados na próxima aula. Para visualizarmos o resultado da execução do ServletExemplo, primeiro precisamos informar ao servidor Web que ServletExemplo é um Servlet e que ele pode ser executado pelos usuários da aplicação Web. Isso é feito alterando-se o arquivo web.xml, como mostrado a seguir. Objetos são representações das coisas reais ou virtuais do mundo real no programa orientado a objetos. Objetos possuem atributos e métodos. Métodos são funções ou procedimentos que estão atrelados a um objeto. Os métodos são responsáveis, entre outras coisas, por processar dados (parâmetros do método etc.). 01 02 08 ProgramacaoWeb 09 10 index.html 11 12 13 14 ServletExemplo 15 ServletExemplo 16 aula02.ServletExemplo 17 18 19 ServletExemplo 20 /ServletExemplo 21 22 Observe os marcadores e . O primeiro é utilizado para indicar a existência do ServletExemplo. O marcador tem como objetivo informar ao servidor o nome completo da classe Java. Já o marcador indica um apelido para o Servlet, no caso o nome ServletExemplo, o qual será usado no restante do arquivo web.xml. Por fim, os marcadores e são utilizados para fins de documentação. Com relação ao marcador , ele é utilizado para habilitar o ServletExemplo a ser executado via Web por um usuário do sistema. Isso é feito mapeando-o a uma URL do sistema. O marcador indica o nome (apelido) do Servlet enquanto o marcador indica a URL à qual o Servlet está associado. No exemplo mostrado, o ServletExemplo está associado à URL relativa /ServletExemplo. Caso o nome de sua aplicação Web no servidor seja ProgramacaoWeb e considerando que o servidor Web está rodando na porta 8080, a URL para este Servlet é https://localhost:8080/ProgramacaoWeb/ServletExemplo. Esse trabalho de configuração deve ser feito para todos os Servlets criados, o que pode ser um trabalho nada motivador quando for necessário criar dezenas de Servlets. Felizmente, ferramentas de desenvolvimento como o Eclipse realizam esse trabalho de configuração de forma automática. Para isso, basta utilizar a opção New / Servlet do Eclipse ao invés de New / Class. Dica Utilize a opção New / Servlet do Eclipse ao invés de New / Class para criar novos Servlets. Assim, a configuração dos Servlets no arquivo web.xml será feita automaticamente. Para executar um Servlet no Eclipse, utilize a opção Run as / Run on Server que aparece ao se clicar com o botão direito em cima do Servlet (veja a Figura 4). Figura 4 – Executando um Servlet no Eclipse Na primeira vez, caso o seu projeto não esteja associado a um servidor Web integrado ao Eclipse, a Figura 5 será apresentada. Selecione o servidor instalado para ser o servidor Web da sua aplicação. Lembre-se de marcar a opção Always use this server when running this project, evitando que essa tela se abra nas próximas vezes. Figura 5 – Indicando o servidor Web a ser utilizado Enfim, após o ServletExemplo ser executado, a janela da Figura 6 será apresentada mostrando que tudo está configurado e funcionando corretamente. Caso isso não ocorra, observe a mensagem de erro que irá aparecer na aba Console, na parte inferior da janela. Figura 6 – Resultado da execução do ServletExemplo Atenção Lembre-se sempre de configurar o arquivo web.xml após criar um novo Servlet, caso isso não seja feito de forma automática pela ferramenta de desenvolvimento utilizada! Nas próximas aulas, iremos considerar a criação dos servlets pelo menu New | Servlet, o que garantirá que o arquivo web.xml seja configurado com o Servlet criado. 1. Defina a classe ServletExemplo como mostrado anteriormente e execute-a no Eclipse, conforme instruções já apresentadas (Run on server). 2. Defina uma classe ServletMeuNome que gera uma página com seu nome e execute-a. 3. Crie uma classe ServletQualMeuNome para gera uma página com a pergunta “Qual o meu nome?”. Esse texto deve possuir um link de forma que, quando clicado, execute o ServletMeuNome. Dica: use o marcador para criar um link para a URL que executa o ServletMeuNome. Ciclo de vida dos Servlets Muito bem, agora que você já sabe criar um Servlet e executá-lo, vamos nos aprofundar sobre os conceitos e recursos dessa tecnologia, que são a base da programação Web. Em primeiro lugar, é importante saber que os Servlets seguem um ciclo de vida. Quando uma requisição Web realizada por um cliente é mapeada para um Servlet, o contêiner Web realiza os seguintes passos: 1. Se o Servlet ainda não tiver sido carregado (ou seja, é a primeira vez que o Servlet é acessado pelo contêiner): a. Carrega a classe do Servlet, isto é, lê o bytecode da classe, o qual é gerado quando ela é compilada. b. Instancia a classe, ou seja, cria um objeto a partir da classe carregada. Para isso, usa-se o construtor vazio do Servlet. c. Chama o método init() do Servlet, responsável por fazer qualquer inicialização ou configuração necessária para o funcionamento do Servlet. 2. Executa o método service() definido na superclasse HttpServlet (e assim herdado pelos Servlets). Esse método será responsável por identificar o tipo da requisição Web e invocar o método de tratamento apropriado (doGet(), doPost() etc.), como descrito na próxima seção dessa aula. Atenção Caso o Servlet não seja mais necessário (ex.: quando o servidor Web está sendo desligado ou reiniciado), o contêiner Web executa o método destroy() de cada Servlet que foi instanciado. Vejamos a seguir o código de um Servlet utilizado para imprimir a data e hora na qual ele foi carregado, executado e finalizado. 01 package aula02; 02 03 import java.io.IOException; 04 import java.io.PrintWriter; 05 import java.text.SimpleDateFormat; 06 import java.util.Date; 07 08 import javax.servlet.ServletException; 09 import javax.servlet.http.HttpServlet; 10 import javax.servlet.http.HttpServletRequest; 11 import javax.servlet.http.HttpServletResponse; 12 13 public class ServletDataHora extends HttpServlet { 14 private SimpleDateFormat format = 15 new SimpleDateFormat("dd/MM/yyyy - hh:mm:ss"); 16 17 @Override 18 public void init() throws ServletException { 19 super.init(); 20 log("ServletDataHora iniciado às " 21 + format.format(new Date())); 22 } 23 24 @Override 25 protected void doGet(HttpServletRequest request, 26 HttpServletResponse response) 27 throws ServletException, IOException { 28 PrintWriter saida = response.getWriter(); 29 saida.write("Olá! A data/hora atual é: "); 30 saida.write(format.format(new Date())); 31 saida.write(""); 32 saida.close(); 33 } 34 35 @Override 36 public void destroy() { 37 log("ServletDataHora finalizado às " 38 + format.format(new Date())); 39 super.destroy(); 40 } 41 } Para funcionar, o ServletDataHora possui um atributo do tipo SimpleDateFormat, utilizado para formatar datas e horas em um String de acordo com o padrão definido pelo programador, ou seja, segundo o formato "dd/MM/yyyy - hh:mm:ss". Esse objeto é primeiramente utilizado na implementação do método init(), o qual faz uso do método log (herdado de HttpServlet) para imprimir na saída padrão a data e hora na qual o Servlet foi inicializado, como pode ser visto na aba Console do Eclipse após iniciarmos o servidor Web e acessarmos o Servlet (veja a Figura 7). Figura 7 – ServletDataHora mostrando a data e hora atual formatadas Código similar é visto no método destroy(). Observe que esses dois métodos devem executar os métodos sobrescritos super.init() e super.destroy(), visando garantir uma correta inicialização e liberação de recursos definidos no código herdado da superclasse! Observe também o uso do @override, anotação no código Java que indica ao compilador que os métodos anotados estão sobrescrevendo métodos das superclasses. É uma boa prática usar esse tipo de anotação, pois dessa forma o compilador garante que o nome e tipos dos parâmetros dos métodos definidos estão de acordo com os da superclasse. Você pode estar pensando agora: por que redefinimos o método doGet e não o método service? A resposta para essa pergunta será dada mais adiante, ainda nesta aula. 1. Defina a classe ServletDataHora como mostrado anteriormente e execute-a. Observe se as mensagens são impressas ao inicializar o Servlet, ao executá-lo e ao finalizar a execução do servidor Web. 2. Altere a classe ServletDataHora para mostrar também o dia da semana atual (segunda-feira, terça-feira etc.). Dica: utilize a classe java.util.Calendar. Métodos de execução dos Servlets Até o momento, todos os Servlets que foram implementados redefiniram o método doGet da classe HttpServlet. Para entendermos o porquê disso, precisamos estar cientes de que o HTTP (HyperText Transfer Protocol) é mais utilizado na comunicação entre clientes e servidores Web e ele pode ser utilizado de acordo com vários métodos (GET, POST, HEAD, PUT etc.). Os métodos GET e POST são os mais utilizados e estão brevemente descritos no Quadro 1. Os detalhes sobre esses dois métodos não serão abordados neste curso. Método Descrição GET Método de acesso ao servidor Web utilizado ao se: digitar uma URL em um navegador Web; clicar em uma URL de uma página; acionar um formulário que contenha o atributo method="get". No caso, utiliza a própria URL para envio desses dados. Por exemplo, a URL é utilizada para acessar o recurso passando-se o parâmetro p1 com valor 10. POST Método de acesso ao servidor Web utilizado ao se acionar um formulário Web que contenha o atributo method="post". No caso, os parâmetros e valores de acesso a um recurso Web não aparecem na própria URL – eles são enviados pelo navegador ao servidor de forma separada. Quadro 1 – Métodos GET e POST do protocolo HTTP A implementação do método service que é dada pela classe HttpServlet reconhece o tipo de método utilizado na requisição Web e delega a execução a métodos específicos de acordo com o método utilizado. O termo “delega” quer dizer que o método executa um outro método, ou seja, deixa para o outro método o trabalho de processamento da requisição. No caso dos métodos GET e POST, o método service delega a tarefa de processar as requisições para os métodos doGet e doPost, respectivamente. Para entendermos melhor esse funcionamento, observe o exemplo da Figura 8, o qual possui uma página HTML com um link e com um botão que levam à execução do mesmo Servlet. No caso, o link usa o método GET e o botão usa o método POST (observe o atributo action do marcador form). Figura 8 – Tela com link para acesso via método GET e botão para acesso via método POST O código da página HTML vista na Figura 8 é mostrado a seguir. 01 02 03 ServletGetPost 04 05 06 GET
07
08 09
10 11 Já a implementação da classe ServletGetPost para atender às requisições acionadas pelo link ou botão dessa página é vista a seguir. Esse Servlet redefine tanto o método doGet como o método doPost de HttpServlet, apresentando essa informação na mensagem de resposta (veja a Figura 9). 01 package aula02; 02 03 import java.io.IOException; 04 import java.io.PrintWriter; 05 06 import javax.servlet.ServletException; 07 import javax.servlet.http.HttpServlet; 08 import javax.servlet.http.HttpServletRequest; 09 import javax.servlet.http.HttpServletResponse; 10 11 public class ServletGetPost extends HttpServlet { 12 13 protected void doGet(HttpServletRequest request, 14 HttpServletResponse response) 15 throws ServletException, IOException { 16 PrintWriter saida = response.getWriter(); 17 saida.write("Olá! "); 18 saida.write("Mesma resposta para o método GET."); 19 saida.write(""); 20 saida.close(); 21 } 22 23 protected void doPost(HttpServletRequest request, 24 HttpServletResponse response) 25 throws ServletException, IOException { 26 PrintWriter saida = response.getWriter(); 27 saida.write("Olá! "); 28 saida.write("Resposta para o método POST."); 29 saida.write(""); 30 saida.close(); 31 } 32 } Figura 9 – Telas geradas pelo ServletGetPost ao usarmos requisição via método POST e GET, respectivamente Se na hora de implementar um Servlet você notar que não faz diferença entre os métodos GET e POST (esse é o caso mais comum!), então você pode implementar apenas um dos métodos (digamos, o GET) e fazer com que o outro chame o primeiro. Isso pode ser observado no código do ServletTantoFaz apresentado a seguir. 01 package aula02; 02 03 import java.io.IOException; 04 import java.io.PrintWriter; 05 06 import javax.servlet.ServletException; 07 import javax.servlet.http.HttpServlet; 08 import javax.servlet.http.HttpServletRequest; 09 import javax.servlet.http.HttpServletResponse; 10 11 public class ServletTantoFaz extends HttpServlet { 12 13 protected void doGet(HttpServletRequest request, 14 HttpServletResponse response) 15 throws ServletException, IOException { 16 doPost(request, response); 17 } 18 19 protected void doPost(HttpServletRequest request, 20 HttpServletResponse response) 21 throws ServletException, IOException { 22 PrintWriter saida = response.getWriter(); 23 saida.write("Olá! "); 24 saida.write("Mesma resposta para o GET ou POST."); 25 saida.write(""); 26 saida.close(); 27 } 28 29 } Caso você não redefina os métodos doGet ou doPost, a sua implementação padrão é mostrar uma mensagem de erro padrão indicando que o método HTTP utilizado não é suportado. 1. Crie a classe ServletGetPost e execute-a através da página HTML apresentada para acessar o Servlet através dos métodos GET ou POST. 2. Altere a classe ServletGetPost para apresentar a data atual se for usado o método GET e a hora atual se for utilizado o método POST. Dica: baseie-se no código do ServletDataHora e altere a String do formato da data/hora para mostrar só a data ou só a hora. Ex: "dd/MM/yyyy" para mostrar só a data. 3. Crie uma página HTML com um formulário e utilize-o para acessar o ServletMeuNome através do método POST. Analise se o Servlet respondeu como você estava esperando. 4. Altere o ServletMeuNome definido por você em atividades anteriores desta aula para que ele funcione tanto com o método GET como com o método POST. Hora da revisão Bem, vamos relembrar alguns tópicos vistos nesta aula? Você começou a estudar os Servlets. Para poder funcionar, os Servlets precisam ser registrados no arquivo web.xml, onde diversas configurações podem ser feitas. No caso da aula de hoje, a principal configuração é a identificação dos Servlets e seu mapeamento para URLs. Os Servlets possuem um ciclo de vida. Primeiro são carregados, depois instanciados (onde o método init() é executado) e então executados (método service) ao serem acessados pelos clientes Web. Quando o Servlet não é mais necessário, seu método destroy() é executado antes do objeto ser liberado da memória. A implementação do método service é dada pela classe HttpServlet, que delega a execução a métodos específicos de acordo com o método utilizado. No caso dos métodos GET e POST, o método service delega a tarefa de processar as requisições para os métodos doGet e doPost, respectivamente. Leitura complementar Veja os métodos existentes nas classes Servlet, GenericServlet e HttpServlet. Servlet API Documentation. • Resumo Chegamos ao fim de nossa primeira aula com programação para Web! Vamos recapitular os principais conceitos aprendidos? Hoje você aprendeu que para criar páginas Web dinâmicas em Java, fazemos uso dos chamados Servlets, classes em Java que em geral herdam direta ou indiretamente de HttpServlet. Com isso você entendeu o ciclo de vida dos Servlets, viu o funcionamento dos métodos de processamento de requisições Web dos Servlets e aprendeu a configuração da aplicação Web necessária para o funcionamento dos Servlets. Faça agora sua Autoavaliação e até a próxima aula! 1. Descreva o ciclo de vida dos Servlets. 2. Identifique quais métodos da classe HttpServlet estão envolvidos no processamento de requisições Web. Descreva como esses métodos são utilizados para processar as requisições feitas pelos clientes através dos navegadores Web. 3. Descreva os passos necessários para configurar uma aplicação Web para que o Servlet aluno.MeuServlet possa ser executado. 4. Crie e execute um Servlet que, dado um número, calcula e mostra o seu fatorial. Desenvolvimento Web Aula 3 – Manipulando Requisições e Respostas HTML com Servlets Olá! Continuamos agora o assunto da aula passada, com os chamados Servlets. Você já aprendeu que os Servlets são criados para processar as requisições Web, gerando as páginas HTML de resposta que serão mostradas no navegador Web do usuário. Tudo bem até aí? Muito bem, continuaremos o assunto de Servlets agora apresentando mais detalhes sobre como processar as requisições dos clientes e sobre como identificá-los ao longo do tempo. Por exemplo, você já deve ter preenchido formulários na Web, correto? Diversas são as situações em que precisamos fazer isso: quando cadastramos uma nova conta de e-mail, informando dados pessoais e e-mail desejado; quando nos autenticamos (logamos) em sistemas Web, acessando nossa conta de e-mail ou conta bancária. Pois bem, esses sistemas Web precisam receber e processar os dados digitados. Nessa aula, vamos ver como fazer isso com Servlets. Além disso, um cadastro pode ser feito em mais de uma tela. Imagine um sistema em que na primeira tela seja necessário entrar com dados pessoais, clicar em um botão de nome “Próximo” e depois, em uma segunda tela, digitar os seus dados profissionais para só então clicar em um botão "Confirmar”. É bem provável que o sistema deva salvar os dados coletados apenas quando ambas as telas forem preenchidas. Dessa forma, como guardar a informação digitada na primeira tela enquanto o usuário digita informações na segunda tela? Também veremos uma possível solução para isso. Depois de ler esta aula, você estará apto a: • Processar parâmetros das requisições Web. • Repassar parâmetros de uma requisição para outra. Manipulando os parâmetros das requisições Web Na aula anterior, você percebeu que os Servlets são acessados através de suas URLs (ex.: ). Esse acesso pode ser via método GET ou via método POST. Ou seja, se você apenas digitar uma URL em um navegador Web ou clicar em um link usual numa página, estará usando o método GET. Já para se usar o método POST, precisamos definir e usar um formulário, ou seja, usar o marcador
na página HTML e acioná-lo, por exemplo, através do clique de um botão. A escolha entre esses dois métodos é feita geralmente baseada na quantidade de informações (parâmetros da requisição) que você quer passar. Por exemplo, para entrar na página da UFRN, você não precisa entrar com nenhuma informação além da URL do sítio . Nesses casos, geralmente você faz uso do método GET, apesar do método POST poder ser utilizado sem problemas. Por outro lado, quando você faz uma consulta em um engenho de busca como o Google, precisa entrar com informações como as palavras que você quer buscar. Para isso, essa informação precisa ser transportada para o servidor Web, o qual é responsável por encontrar as páginas que contenham aquelas palavras que você digitou na busca. Se fizermos uso do método GET, os parâmetros da requisição (palavras de busca etc.) serão transportados na própria URL de acesso ao Servlet. Por exemplo, a URL representa o acesso ao ServletBusca passando o parâmetro de nome palavras, cujo valor é “servlet java”. Note que espaços em branco são substituídos automaticamente pelo código %20. Experimente fazer uma consulta em um engenho de busca e olhar a URL de resposta de uma consulta. Já se você fizer uso do método POST, a URL de acesso ao Servlet será simplesmente , e seus parâmetros serão passados de forma oculta para o usuário do navegador Web. Isso tem algumas consequências importantes. Primeiro, se você estiver transmitindo uma senha, não vai querer que ela fique trafegando na própria URL, pois URLs são armazenadas em históricos de acesso dos navegadores Web. Além disso, existe um limite não muito grande para o tamanho de uma URL, que pode variar de acordo com o navegador e servidor Web utilizados. Dessa forma, o método GET só deve ser utilizado quando a quantidade de informações for pequena e não for secreta. Ok, mas apesar de os parâmetros de uma requisição serem trafegados via métodos HTTP diferentes, GET ou POST, o acesso a estes é praticamente o mesmo no código Java. De fato, a principal diferença no código Java você já aprendeu na aula passada! Requisições GET são processadas pelo método doGet(), e requisições POST são processadas pelo método doPost(). Dentro desses métodos, o acesso aos parâmetros tende a ser o mesmo: uso da classe javax.servlet.http.HttpServletRequest. Ambos os métodos doGet() e doPost() possuem dois parâmetros. O primeiro dele, do tipo HttpServletRequest, irá lhe dar acesso aos parâmetros e a outras informações da requisição. Lembre-se de que o método será chamado de acordo com o evento acionado pelo usuário (clique em um link, digitação da URL no navegador, clique em um botão associado a um formulário etc.). Analise agora o código a seguir, cuja tela de resposta é mostrada na Figura 1. 01 package exemplo.parametros; 02 03 import java.io.IOException; 04 import java.io.PrintWriter; 05 06 import javax.servlet.ServletException; 07 import javax.servlet.http.HttpServlet; 08 import javax.servlet.http.HttpServletRequest; 09 import javax.servlet.http.HttpServletResponse; 10 11 public class ServletOlaPersonalizado extends HttpServlet { 12 13 protected void doGet(HttpServletRequest request, HttpServletResponse response) 14 throws ServletException, IOException { 15 PrintWriter resposta = response.getWriter(); 16 resposta.write(""); 17 resposta.write("Olá, " + request.getParameter("nome") + " !!!"); 18 resposta.write(""); 19 } 20 } Listagem 1 - Acessando parâmetros de requisição em um Servlet Figura 1 – Resposta do ServletOlaPersonalizado Como você pode perceber, a classe ServletOlaPersonalizado é um Servlet que implementa o método doGet(), ou seja, responde a requisições do tipo HTTP via método GET. A resposta desse Servlet irá depender da URL de invocação do Servlet, ou melhor, do valor do parâmetro nome que é passado através da URL. Por exemplo, a página de resposta desse Servlet, quando executado através da URL , é mostrada na Figura 1 que está ao lado. Caso você mude o valor do parâmetro para Lucy, teremos o resultado mostrado na Figura 2. Esse comportamento dinâmico, que muda de acordo com o valor do parâmetro nome, é dado pelo comando request.getParameter("nome") (linha 17 da Listagem 1). Esse comando aciona o método getParameter() da classe HttpServletRequest, o qual recebe como parâmetro uma String que representa o nome do parâmetro da requisição Web cujo valor você deseja ter acesso. No caso, estamos interessados no valor do parâmetro identificado pela String “nome”. Observe a primeira URL de acesso ao Servlet e note que está indicado que o valor do parâmetro nome é igual ao texto Eduardo, justamente o texto que aparece na página HTML da Figura 1. Ao trocar o valor desse parâmetro para Lucy, o valor retornado pelo comando request.getParameter("nome") será o valor “Lucy” e não mais “Eduardo”, fazendo com que a página resultante seja igual à da Figura 2. Fácil de entender, não é? Figura 2 – Resposta do ServletOlaPersonalizado ao mudar valor do parâmetro Ao trabalharmos com parâmetros nas requisições, temos que ter em mente que nem sempre os valores passados estão de acordo com o esperado! Considere, por exemplo, o caso das requisições tratadas pelo ServletOlaPersonalizado. Se o usuário digitar diretamente a URL no browser, ele pode: • esquecer de colocar na URL o valor do parâmetro “nome”. • digitar errado o nome do parâmetro, como no caso da URL . Observe que o nome do parâmetro passado pelo usuário é agora “noome” e não mais “nome”, como esperado pelo Servlet. Nesses casos, o que o comando request.getParameter("nome") irá retornar? Basicamente o valor null. Sempre que o valor de um parâmetro não for encontrado, o valor padrão null será retornado. Veja na Figura 3 o resultado quando esquecemos de passar o valor do parâmetro (note que o valor null, quando convertido para String, é igual ao texto “null”). Figura 3 – Esquecendo de passar o valor do parâmetro nome Além disso, lembre-se de que existem pessoas na Internet com intenções de “invadir e quebrar” os sistemas. Elas podem propositalmente deixar de passar parâmetros para o sistema ou então passar valores inválidos, buscando derrubar o sistema ou fazer com que ele tenha um comportamento inapropriado. Quer ver um exemplo? Uma pessoa mal intencionada pode criar uma página Web com o link “clique aqui para ir para a empresa X”, onde X é a sua empresa e cujo link vai para o ServletOlaPersonalizado que você criou e hospedou no seu site empresarial. Basta que a URL do link criado contenha uma palavra inadequada para o parâmetro nome (ex: https://localhost:8080/civt-web/ServletOlaPersonalizado?nome=@$@$@$) para se ter um possível estrago. Ao clicar no link da página Web da pessoa mal intencionada, um possível cliente verá o resultado impresso na tela e provavelmente ficará ofendido. Sem entender de programação, o cliente em potencial achará que a grosseria veio do seu site e não do site da pessoa mal intencionada, já que a URL que aparecerá no navegador Web é a do ServletOlaPersonalizado (seu site!). Então, fique atento! 1. Implemente e execute o ServletOlaPersonalizado. 2. Crie um novo Servlet que receba dois parâmetros, primeiroNome e ultimoNome, e que faça uma saudação ao usuário imprimindo uma mensagem que contenha o primeiro e o último nome do usuário (parâmetros da requisição). Além do método getParameter, a classe HttpServletRequest possui vários outros métodos que podem ser utilizados para pegar informação sobre parâmetros da requisição Web. Um exemplo é o método getParameterNames(), o qual você pode usar para pegar os nomes de todos os parâmetros da requisição. Esse método é geralmente útil quando você quer, por exemplo, listar para o usuário todos os parâmetros enviados. Vejamos o exemplo de código mostrado na Listagem 2. 01 package exemplo.parametros; 02 03 import java.io.IOException; 04 import java.io.PrintWriter; 05 import java.util.Enumeration; 06 import javax.servlet.ServletException; 07 import javax.servlet.http.HttpServlet; 08 import javax.servlet.http.HttpServletRequest; 09 import javax.servlet.http.HttpServletResponse; 10 11 public class ServletListarNomeParametros extends HttpServlet { 12 13 protected void doGet(HttpServletRequest request, 14 HttpServletResponse response) throws ServletException, IOException { 15 PrintWriter resposta = response.getWriter(); 16 resposta.write(""); 17 resposta.write("Olá, os parâmetros recebidos por essa requisição são:
"); 18 Enumeration nomesParametros = request.getParameterNames(); 19 while (nomesParametros.hasMoreElements()) { 20 resposta.write(nomesParametros.nextElement().toString()); 21 resposta.write(", "); 22 } 23 resposta.write(""); 24 } 25 26 protected void doPost(HttpServletRequest request, 27 HttpServletResponse response) throws ServletException, IOException { 28 doGet(request, response); 29 } 30 } Listagem 2 – Lendo nome de todos os parâmetros da requisição. Ao executar o ServletListarNomeParametros sem passar nenhum parâmetro, será apresentada a página de resposta mostrada na parte superior da Figura 4. Já ao passar parâmetros para a requisição, como mostrado na parte inferior da Figura 4, os nomes desses parâmetros serão apresentados. Se você observou bem, as linhas 18 a 22 da Listagem 2 são as responsáveis por imprimir essa lista de nomes de parâmetros. O tipo de retorno do método getParameterNames é Enumeration, uma classe utilizada para acessar os elementos de uma coleção. No caso, queremos acessar os nomes (elementos) de todos os parâmetros da requisição (coleção). A classe Enumeration possui métodos como hasMoreElements(), que retorna um boleano indicando se ainda existem elementos a serem pegos (ver condição do while na linha 19), e nextElement(), que retorna o próximo elemento da coleção (linha 20). Como Enumeration é uma classe de uso geral, o tipo de retorno do método nextElement() é o Object. Para montarmos uma String com sua representação de um objeto qualquer, basta usar o método toString(), não é? De fato, os nomes dos parâmetros são textos, ou seja, Strings, então uma alternativa ao uso do método toString() é o uso do cast (String). Figura 4 – Páginas de resposta quando não se passa parâmetros (acima) e quando se passa parâmetros (abaixo) para a requisição 1. Sem olhar diretamente o código, implemente e execute o ServletListarNomeParametros. 2. Altere o código do ServletListarNomeParametros para imprimir não só os nomes dos parâmetros, mas também o valor de cada um deles. Por exemplo, ao invés de só dizer que existe um parâmetro de nome idade, mostrar também o valor passado (ver exemplo da Figura 5). Figura 5 – Nomes e valores dos parâmetros Você já sabe como pegar parâmetros de requisições Web, mas os exemplos que mostramos e que você praticou até agora usam basicamente o método GET, ou seja, os parâmetros foram passados na própria URL. Vamos agora praticar um pouco o uso do método POST, através da passagem de parâmetros via formulários Web. Observe a página mostrada na Figura 6. Ela contém um formulário usado para chamar o ServletOlaPersonalizado, passando o nome da pessoa como parâmetro. O código HTML dessa página é mostrado na Listagem 3. Figura 6 – Formulário do olá personalizado 01 02 03 04 Seu Nome 05 06 07 Digite seu nome e clique em confirmar 08 09 10 11 12 13 Listagem 3 – Código HTML para criar formulário do ServletOlaPersonalizado. Ao preencher o seu nome no formulário, ele será apresentado na página de resposta. Veja o exemplo de resposta da Figura 7 para o caso de digitarmos o nome Ednaldo Batista. Figura 7 – Exemplo de resposta do ServletOlaPersonalizado invocado pelo formulário Web 1. Sem olhar diretamente o código, tente criar o código da página HTML contendo um formulário para executar o ServletOlaPersonalizado. Teste e, em caso de dúvida, consulte o código mostrado. 2. Crie uma página HTML com um formulário de cadastro dos dados pessoais de uma pessoa (nome, sobrenome, endereço etc.) e faça-o invocar o ServletListarNomeParametros. 3. Além dos métodos para manipular parâmetros da requisição, a classe HttpServletRequest possui diversos outros métodos com função de retornar outros tipos de informação sobre a requisição Web. Faça uma pesquisa na Internet sobre essa classe e tente descobrir a função de alguns de seus outros métodos. Escreva aqui as funções que mais lhe chamaram a atenção. Mantendo o estado do cliente Web Em aplicações desktop, ou seja, aquelas feitas para rodar na máquina do cliente, todas as informações que vão sendo digitadas pelo usuário vão ficando armazenadas ao longo das várias telas que um sistema pode ter. Por exemplo, em um sistema de cadastro, o usuário pode ter que cadastrar seus dados pessoais em uma tela e seus profissionais (local de trabalho etc.) em outra tela para só então confirmar todos os dados. Isso pode ser trivial em um sistema desktop, mas em um sistema Web temos que seguir certos passos para isso poder funcionar (não quer dizer que também não seja fácil!). Vamos pensar primeiro no funcionamento geral desse sistema de cadastro de pessoas, onde a primeira tela é mostrada na Figura 8. Figura 8 – Primeira tela do sistema de cadastro Após o usuário preencher seus dados pessoais e clicar o botão próxima tela, a página da Figura 9 será apresentada. Figura 9 – Segunda tela de cadastro Ao preencher os dados profissionais e clicar no botão confirmar, a tela de resposta apresentada é a da Figura 10. Figura 10 – Resposta obtida pelo sistema após preenchimento dos dados do usuário Agora que você já tem uma boa ideia do que o sistema deve fazer, vamos ver como podemos implementá-lo. Começando pela primeira tela (Figura 8), precisamos criar um arquivo HTML (cadastro.html) com o formulário. O código dele é mostrado na Listagem 4. Note no código HTML a existência do formulário e de sua submissão para um Servlet de nome ServletTela1Cadastro. 01 02 03 04 Dados Pessoais 05 06 07 Preencha seus dados pessoais: 08
09 Nome: 10 Sobrenome:
11 Endereço residencial:
12 Rua: 13 Complemento:
14 Cidade: 15 CEP: 16 Estado:
17
18
19 20 Listagem 4 – Página HTML contendo formulário de cadastro dos dados pessoais Vamos agora observar na Listagem 5 uma possível implementação para o ServletTela1Cadastro. 01 package exemplo.cadastro; 02 03 import java.io.IOException; 04 import java.io.PrintWriter; 05 import javax.servlet.ServletException; 06 import javax.servlet.http.HttpServlet; 07 import javax.servlet.http.HttpServletRequest; 08 import javax.servlet.http.HttpServletResponse; 09 10 public class ServletTela1Cadastro extends HttpServlet { 11 protected void doGet(HttpServletRequest request, 12 HttpServletResponse response) throws ServletException, IOException { 13 doPost(request, response); 14 } 15 16 protected void doPost(HttpServletRequest request, 17 HttpServletResponse response) throws ServletException, IOException { 18 PrintWriter resposta = response.getWriter(); 19 20 resposta.write(""); 21 resposta.write(""); 22 resposta.write("Dados Profissionais"); 23 resposta.write(""); 24 resposta.write(""); 25 resposta.write("Preencha seus dados profissionais:"); 26 resposta.write("
"); 27 resposta.write("Empresa:
"); 28 resposta.write("Endereço profissional:
"); 29 resposta.write("Rua: "); 30 resposta.write("Complemento:
"); 31 resposta.write("Cidade:"); 32 resposta.write("CEP:"); 33 resposta.write("Estado:
"); 34 resposta.write("
"); 35 resposta.write("
"); 36 resposta.write(""); 37 38 } 39 } Listagem 5 – Implementação inicial do ServletTela1Cadastro Note que a implementação do ServletTela1Cadastro mostrada na Listagem 5 monta uma página HTML de resposta referente à segunda tela de cadastro. Entretanto, os dados digitados na primeira tela não estão sendo armazenados! Temos então que pensar agora em como manter os dados de uma tela para outra. Existem duas abordagens diferentes que podemos usar para isso: repassar parâmetros ou usar sessões. A primeira abordagem será apresentada a seguir, a outra será apresentada na nossa próxima aula. Repassando parâmetros Uma primeira solução é usar os chamados campos ocultos (hidden) dos formulários Web. Basicamente, nessa abordagem, todos os campos da primeira tela irão aparecer na segunda tela, mas agora como campos ocultos: 01 02 03 04 05 06 07 Os campos value devem ser preenchidos, claro, com os valores digitados pelo usuário. Isso é feito inserindo-se o código mostrado na Listagem 6 logo após a linha 26 da Listagem 5. Note que o código da Listagem 6 basicamente escreve os campos ocultos do formulário, definindo o valor do campo de acordo com o parâmetro recebido (request.getParameter()). Por exemplo, a linha 01 é responsável por escrever). Se o nome digitado pelo usuário for João, teremos: "); 03 resposta.write(""); 05 resposta.write(""); 07 resposta.write(""); 09 resposta.write(""); 11 resposta.write(""); 13 resposta.write(""); Listagem 6 – Repassando parâmetros através dos campos ocultos Com a inclusão do código apresentado pela Listagem 6, a segunda tela de cadastro possuirá não só as informações digitadas pelo usuário para a segunda tela (dados profissionais), mas também manterá os dados que foram digitados na primeira tela (dados pessoais). Observe na Listagem 7 como fica o código HTML da segunda tela de cadastro quando o usuário digita na primeira tela os dados apresentados na Figura 10. 01 02 Dados Profissionais 03 04 Preencha seus dados profissionais: 05
06 07 08 09 10 11 12 13 Empresa: 14
15 Endereço profissional:
16 Rua: 17 Complemento:
18 Cidade: 19 20 CEP: 21 Estado:
22
23
24 25 Listagem 7 – Código HTML da segunda tela de cadastro contendo campos ocultos Para receber e processar os dados da segunda tela de cadastro, podemos implementar um ServletTela2Cadastro de acordo com o código mostrado na Listagem 8. Observe que esse Servlet acessa da mesma forma tanto os dados ocultos (dados pessoais, repassados da primeira tela de cadastro) como os dados digitados pela segunda tela (dados profissionais). 01 package aula03; 02 03 import java.io.IOException; 04 import java.io.PrintWriter; 05 06 import javax.servlet.ServletException; 07 import javax.servlet.http.HttpServlet; 08 import javax.servlet.http.HttpServletRequest; 09 import javax.servlet.http.HttpServletResponse; 10 11 public class ServletTela2Cadastro extends HttpServlet { 12 protected void doGet(HttpServletRequest request, 13 HttpServletResponse response) throws ServletException, IOException { 14 doPost(request, response); 15 } 16 17 protected void doPost(HttpServletRequest request, 18 HttpServletResponse response) throws ServletException, IOException { 19 PrintWriter resposta = response.getWriter(); 20 resposta.write(""); 21 resposta.write(""); 22 resposta.write("Confirmação de registro"); 23 resposta.write(""); 24 resposta.write(""); 25 resposta.write("

Registro realizado com sucesso!



"); 26 resposta.write("Seus dados pessoais:
"); 27 resposta.write(request.getParameter("nome") + " " 28 + request.getParameter("sobrenome")); 29 resposta.write("
"); 30 resposta.write(request.getParameter("rua")); 31 resposta.write("
"); 32 resposta.write(request.getParameter("complemento")); 33 resposta.write("
"); 34 resposta.write(request.getParameter("cidade")); 35 resposta.write(", "); 36 resposta.write(request.getParameter("cep")); 37 resposta.write(", "); 38 resposta.write(request.getParameter("estado")); 39 resposta.write("
"); 40 resposta.write("Seus dados profissionais:
"); 41 resposta.write(request.getParameter("empresa")); 42 resposta.write("
"); 43 resposta.write(request.getParameter("ruaEmpresa")); 44 resposta.write("
"); 45 resposta.write(request.getParameter("complementoEmpresa")); 46 resposta.write("
"); 47 resposta.write(request.getParameter("cidadeEmpresa")); 48 resposta.write(", "); 49 resposta.write(request.getParameter("cepEmpresa")); 50 resposta.write(", "); 51 resposta.write(request.getParameter("estadoEmpresa")); 52 resposta.write(""); 53 } 54 } Listagem 8 – Código da classe ServletTela2Cadastro 1. Implemente o ServletTela2Cadastro, conforme mostrado anteriormente, porém tentando não olhar o código apresentado. 2. Execute o sistema mostrado como exemplo, desde a primeira tela (processada pelo ServletTela1Cadastro) até a segunda tela (processada pelo ServletTela2Cadastro). Observe: a. se o código fonte da segunda tela possui os campos ocultos. Para isso, clique com o botão direito na segunda tela e selecione a opção de exibir código fonte. Essa opção pode ter nome diferente de acordo com o navegador Web utilizado. b. se os valores digitados são mostrados corretamente na tela de confirmação de cadastro. 3. Altere o sistema mostrado como exemplo para ter uma terceira tela, responsável pela digitação de dados bancários do usuário (código do banco, agência e conta bancária). Só após a terceira tela é que a tela de confirmação dos dados deve aparecer, agora mostrando também os dados bancários do usuário. Limitações dessa abordagem A abordagem mostrada (repasse de parâmetros de uma requisição para outra) permite que dados digitados em uma tela anterior não sejam perdidos, mas repassados para a próxima tela. Essa abordagem pode ser útil em diversas situações, mas possui, também, várias limitações. Em primeiro lugar, se o usuário digitou sua senha na primeira tela, não se deve repassar esse valor para a segunda tela. Por questões de segurança, senhas e outras informações confidenciais (número de cartão de crédito etc.) não devem ser trafegadas como campos ocultos de páginas Web, pois aumenta o risco de hackers conseguirem roubar essas informações. Outro problema é que um sistema de cadastro pode ter mais telas do que o exemplo mostrado. Transitar os dados digitados entre todas as telas pode aumentar muito o código dos Servlets, além de aumentar a chance de erros de programação. Por fim, se a janela do navegador Web do cliente for fechada, os dados serão perdidos, correto? Imagine um sistema de compras onde um cliente escolhe 5 produtos para comprar e seu computador trava. Não seria interessante que, ao voltar à loja virtual, os 5 produtos escolhidos ainda estejam no seu carrinho de compras? Na próxima aula veremos uma abordagem que auxilia a resolver todas essas limitações. Até lá! Leitura complementar Para complementar seu entendimento sobre a manipulação de parâmetros de uma requisição, bem como o repasse desses parâmetros, analise os métodos disponibilizados pelas classes envolvidas. Isso pode ser feito observando-se a API do Java: JavaTM Platform Enterprise Edition, v 5.0 – API Specifications • Em especial, olhar o pacote javax.servlet e as interfaces HttpServletRequest e HttpServletRespose. Resumo Nesta aula, você aprendeu a receber e processar parâmetros de requisições Web. Para isso, você usou métodos da classe HttpServletRequest. Você viu que o método mais utilizado nos exemplos foi o método getParameter(), responsável por retornar o valor de um dado parâmetro da requisição. Além disso, você aprendeu a repassar os valores de parâmetros de uma tela para outra. Apesar dessa abordagem ser útil em algumas situações, ela possui limitações, como o trabalho extra de codificação e o perigo de transitar entre as requisições dados sensíveis (senhas, dados bancários etc.). Na próxima aula, você verá uma abordagem para evitar esses problemas. Não perca! 1. Descreva a(s) classe(s) e métodos utilizados para processar parâmetros de requisições Web. 2. Descreva o funcionamento geral da abordagem de repassar os valores digitados pelo usuário (parâmetros) de uma requisição para outra. Identifique também as limitações dessa abordagem. Desenvolvimento Web Aula 3 – Manipulando Requisições e Respostas HTML com Servlets Olá! Continuamos agora o assunto da aula passada, com os chamados Servlets. Você já aprendeu que os Servlets são criados para processar as requisições Web, gerando as páginas HTML de resposta que serão mostradas no navegador Web do usuário. Tudo bem até aí? Muito bem, continuaremos o assunto de Servlets agora apresentando mais detalhes sobre como processar as requisições dos clientes e sobre como identificá-los ao longo do tempo. Por exemplo, você já deve ter preenchido formulários na Web, correto? Diversas são as situações em que precisamos fazer isso: quando cadastramos uma nova conta de e-mail, informando dados pessoais e e-mail desejado; quando nos autenticamos (logamos) em sistemas Web, acessando nossa conta de e-mail ou conta bancária. Pois bem, esses sistemas Web precisam receber e processar os dados digitados. Nessa aula, vamos ver como fazer isso com Servlets. Além disso, um cadastro pode ser feito em mais de uma tela. Imagine um sistema em que na primeira tela seja necessário entrar com dados pessoais, clicar em um botão de nome “Próximo” e depois, em uma segunda tela, digitar os seus dados profissionais para só então clicar em um botão "Confirmar”. É bem provável que o sistema deva salvar os dados coletados apenas quando ambas as telas forem preenchidas. Dessa forma, como guardar a informação digitada na primeira tela enquanto o usuário digita informações na segunda tela? Também veremos uma possível solução para isso. Depois de ler esta aula, você estará apto a: • Processar parâmetros das requisições Web. • Repassar parâmetros de uma requisição para outra. Manipulando os parâmetros das requisições Web Na aula anterior, você percebeu que os Servlets são acessados através de suas URLs (ex.: ). Esse acesso pode ser via método GET ou via método POST. Ou seja, se você apenas digitar uma URL em um navegador Web ou clicar em um link usual numa página, estará usando o método GET. Já para se usar o método POST, precisamos definir e usar um formulário, ou seja, usar o marcador
na página HTML e acioná-lo, por exemplo, através do clique de um botão. A escolha entre esses dois métodos é feita geralmente baseada na quantidade de informações (parâmetros da requisição) que você quer passar. Por exemplo, para entrar na página da UFRN, você não precisa entrar com nenhuma informação além da URL do sítio . Nesses casos, geralmente você faz uso do método GET, apesar do método POST poder ser utilizado sem problemas. Por outro lado, quando você faz uma consulta em um engenho de busca como o Google, precisa entrar com informações como as palavras que você quer buscar. Para isso, essa informação precisa ser transportada para o servidor Web, o qual é responsável por encontrar as páginas que contenham aquelas palavras que você digitou na busca. Se fizermos uso do método GET, os parâmetros da requisição (palavras de busca etc.) serão transportados na própria URL de acesso ao Servlet. Por exemplo, a URL representa o acesso ao ServletBusca passando o parâmetro de nome palavras, cujo valor é “servlet java”. Note que espaços em branco são substituídos automaticamente pelo código %20. Experimente fazer uma consulta em um engenho de busca e olhar a URL de resposta de uma consulta. Já se você fizer uso do método POST, a URL de acesso ao Servlet será simplesmente , e seus parâmetros serão passados de forma oculta para o usuário do navegador Web. Isso tem algumas consequências importantes. Primeiro, se você estiver transmitindo uma senha, não vai querer que ela fique trafegando na própria URL, pois URLs são armazenadas em históricos de acesso dos navegadores Web. Além disso, existe um limite não muito grande para o tamanho de uma URL, que pode variar de acordo com o navegador e servidor Web utilizados. Dessa forma, o método GET só deve ser utilizado quando a quantidade de informações for pequena e não for secreta. Ok, mas apesar de os parâmetros de uma requisição serem trafegados via métodos HTTP diferentes, GET ou POST, o acesso a estes é praticamente o mesmo no código Java. De fato, a principal diferença no código Java você já aprendeu na aula passada! Requisições GET são processadas pelo método doGet(), e requisições POST são processadas pelo método doPost(). Dentro desses métodos, o acesso aos parâmetros tende a ser o mesmo: uso da classe javax.servlet.http.HttpServletRequest. Ambos os métodos doGet() e doPost() possuem dois parâmetros. O primeiro dele, do tipo HttpServletRequest, irá lhe dar acesso aos parâmetros e a outras informações da requisição. Lembre-se de que o método será chamado de acordo com o evento acionado pelo usuário (clique em um link, digitação da URL no navegador, clique em um botão associado a um formulário etc.). Analise agora o código a seguir, cuja tela de resposta é mostrada na Figura 1. 01 package exemplo.parametros; 02 03 import java.io.IOException; 04 import java.io.PrintWriter; 05 06 import javax.servlet.ServletException; 07 import javax.servlet.http.HttpServlet; 08 import javax.servlet.http.HttpServletRequest; 09 import javax.servlet.http.HttpServletResponse; 10 11 public class ServletOlaPersonalizado extends HttpServlet { 12 13 protected void doGet(HttpServletRequest request, HttpServletResponse response) 14 throws ServletException, IOException { 15 PrintWriter resposta = response.getWriter(); 16 resposta.write(""); 17 resposta.write("Olá, " + request.getParameter("nome") + " !!!"); 18 resposta.write(""); 19 } 20 } Listagem 1 - Acessando parâmetros de requisição em um Servlet Figura 1 – Resposta do ServletOlaPersonalizado Como você pode perceber, a classe ServletOlaPersonalizado é um Servlet que implementa o método doGet(), ou seja, responde a requisições do tipo HTTP via método GET. A resposta desse Servlet irá depender da URL de invocação do Servlet, ou melhor, do valor do parâmetro nome que é passado através da URL. Por exemplo, a página de resposta desse Servlet, quando executado através da URL , é mostrada na Figura 1 que está ao lado. Caso você mude o valor do parâmetro para Lucy, teremos o resultado mostrado na Figura 2. Esse comportamento dinâmico, que muda de acordo com o valor do parâmetro nome, é dado pelo comando request.getParameter("nome") (linha 17 da Listagem 1). Esse comando aciona o método getParameter() da classe HttpServletRequest, o qual recebe como parâmetro uma String que representa o nome do parâmetro da requisição Web cujo valor você deseja ter acesso. No caso, estamos interessados no valor do parâmetro identificado pela String “nome”. Observe a primeira URL de acesso ao Servlet e note que está indicado que o valor do parâmetro nome é igual ao texto Eduardo, justamente o texto que aparece na página HTML da Figura 1. Ao trocar o valor desse parâmetro para Lucy, o valor retornado pelo comando request.getParameter("nome") será o valor “Lucy” e não mais “Eduardo”, fazendo com que a página resultante seja igual à da Figura 2. Fácil de entender, não é? Figura 2 – Resposta do ServletOlaPersonalizado ao mudar valor do parâmetro Ao trabalharmos com parâmetros nas requisições, temos que ter em mente que nem sempre os valores passados estão de acordo com o esperado! Considere, por exemplo, o caso das requisições tratadas pelo ServletOlaPersonalizado. Se o usuário digitar diretamente a URL no browser, ele pode: • esquecer de colocar na URL o valor do parâmetro “nome”. • digitar errado o nome do parâmetro, como no caso da URL . Observe que o nome do parâmetro passado pelo usuário é agora “noome” e não mais “nome”, como esperado pelo Servlet. Nesses casos, o que o comando request.getParameter("nome") irá retornar? Basicamente o valor null. Sempre que o valor de um parâmetro não for encontrado, o valor padrão null será retornado. Veja na Figura 3 o resultado quando esquecemos de passar o valor do parâmetro (note que o valor null, quando convertido para String, é igual ao texto “null”). Figura 3 – Esquecendo de passar o valor do parâmetro nome Além disso, lembre-se de que existem pessoas na Internet com intenções de “invadir e quebrar” os sistemas. Elas podem propositalmente deixar de passar parâmetros para o sistema ou então passar valores inválidos, buscando derrubar o sistema ou fazer com que ele tenha um comportamento inapropriado. Quer ver um exemplo? Uma pessoa mal intencionada pode criar uma página Web com o link “clique aqui para ir para a empresa X”, onde X é a sua empresa e cujo link vai para o ServletOlaPersonalizado que você criou e hospedou no seu site empresarial. Basta que a URL do link criado contenha uma palavra inadequada para o parâmetro nome (ex: https://localhost:8080/civt-web/ServletOlaPersonalizado?nome=@$@$@$) para se ter um possível estrago. Ao clicar no link da página Web da pessoa mal intencionada, um possível cliente verá o resultado impresso na tela e provavelmente ficará ofendido. Sem entender de programação, o cliente em potencial achará que a grosseria veio do seu site e não do site da pessoa mal intencionada, já que a URL que aparecerá no navegador Web é a do ServletOlaPersonalizado (seu site!). Então, fique atento! 1. Implemente e execute o ServletOlaPersonalizado. 2. Crie um novo Servlet que receba dois parâmetros, primeiroNome e ultimoNome, e que faça uma saudação ao usuário imprimindo uma mensagem que contenha o primeiro e o último nome do usuário (parâmetros da requisição). Além do método getParameter, a classe HttpServletRequest possui vários outros métodos que podem ser utilizados para pegar informação sobre parâmetros da requisição Web. Um exemplo é o método getParameterNames(), o qual você pode usar para pegar os nomes de todos os parâmetros da requisição. Esse método é geralmente útil quando você quer, por exemplo, listar para o usuário todos os parâmetros enviados. Vejamos o exemplo de código mostrado na Listagem 2. 01 package exemplo.parametros; 02 03 import java.io.IOException; 04 import java.io.PrintWriter; 05 import java.util.Enumeration; 06 import javax.servlet.ServletException; 07 import javax.servlet.http.HttpServlet; 08 import javax.servlet.http.HttpServletRequest; 09 import javax.servlet.http.HttpServletResponse; 10 11 public class ServletListarNomeParametros extends HttpServlet { 12 13 protected void doGet(HttpServletRequest request, 14 HttpServletResponse response) throws ServletException, IOException { 15 PrintWriter resposta = response.getWriter(); 16 resposta.write(""); 17 resposta.write("Olá, os parâmetros recebidos por essa requisição são:
"); 18 Enumeration nomesParametros = request.getParameterNames(); 19 while (nomesParametros.hasMoreElements()) { 20 resposta.write(nomesParametros.nextElement().toString()); 21 resposta.write(", "); 22 } 23 resposta.write(""); 24 } 25 26 protected void doPost(HttpServletRequest request, 27 HttpServletResponse response) throws ServletException, IOException { 28 doGet(request, response); 29 } 30 } Listagem 2 – Lendo nome de todos os parâmetros da requisição. Ao executar o ServletListarNomeParametros sem passar nenhum parâmetro, será apresentada a página de resposta mostrada na parte superior da Figura 4. Já ao passar parâmetros para a requisição, como mostrado na parte inferior da Figura 4, os nomes desses parâmetros serão apresentados. Se você observou bem, as linhas 18 a 22 da Listagem 2 são as responsáveis por imprimir essa lista de nomes de parâmetros. O tipo de retorno do método getParameterNames é Enumeration, uma classe utilizada para acessar os elementos de uma coleção. No caso, queremos acessar os nomes (elementos) de todos os parâmetros da requisição (coleção). A classe Enumeration possui métodos como hasMoreElements(), que retorna um boleano indicando se ainda existem elementos a serem pegos (ver condição do while na linha 19), e nextElement(), que retorna o próximo elemento da coleção (linha 20). Como Enumeration é uma classe de uso geral, o tipo de retorno do método nextElement() é o Object. Para montarmos uma String com sua representação de um objeto qualquer, basta usar o método toString(), não é? De fato, os nomes dos parâmetros são textos, ou seja, Strings, então uma alternativa ao uso do método toString() é o uso do cast (String). Figura 4 – Páginas de resposta quando não se passa parâmetros (acima) e quando se passa parâmetros (abaixo) para a requisição 1. Sem olhar diretamente o código, implemente e execute o ServletListarNomeParametros. 2. Altere o código do ServletListarNomeParametros para imprimir não só os nomes dos parâmetros, mas também o valor de cada um deles. Por exemplo, ao invés de só dizer que existe um parâmetro de nome idade, mostrar também o valor passado (ver exemplo da Figura 5). Figura 5 – Nomes e valores dos parâmetros Você já sabe como pegar parâmetros de requisições Web, mas os exemplos que mostramos e que você praticou até agora usam basicamente o método GET, ou seja, os parâmetros foram passados na própria URL. Vamos agora praticar um pouco o uso do método POST, através da passagem de parâmetros via formulários Web. Observe a página mostrada na Figura 6. Ela contém um formulário usado para chamar o ServletOlaPersonalizado, passando o nome da pessoa como parâmetro. O código HTML dessa página é mostrado na Listagem 3. Figura 6 – Formulário do olá personalizado 01 02 03 04 Seu Nome 05 06 07 Digite seu nome e clique em confirmar 08 09 10 11 12 13 Listagem 3 – Código HTML para criar formulário do ServletOlaPersonalizado. Ao preencher o seu nome no formulário, ele será apresentado na página de resposta. Veja o exemplo de resposta da Figura 7 para o caso de digitarmos o nome Ednaldo Batista. Figura 7 – Exemplo de resposta do ServletOlaPersonalizado invocado pelo formulário Web 1. Sem olhar diretamente o código, tente criar o código da página HTML contendo um formulário para executar o ServletOlaPersonalizado. Teste e, em caso de dúvida, consulte o código mostrado. 2. Crie uma página HTML com um formulário de cadastro dos dados pessoais de uma pessoa (nome, sobrenome, endereço etc.) e faça-o invocar o ServletListarNomeParametros. 3. Além dos métodos para manipular parâmetros da requisição, a classe HttpServletRequest possui diversos outros métodos com função de retornar outros tipos de informação sobre a requisição Web. Faça uma pesquisa na Internet sobre essa classe e tente descobrir a função de alguns de seus outros métodos. Escreva aqui as funções que mais lhe chamaram a atenção. Desenvolvimento Web Aula 4 – Trabalhando com sessões Olá, caro aluno(a), você está gostando do assunto? Espero que sim, e ainda estamos apenas nos primeiros passos da programação para Web! Lembre-se, qualquer dúvida, não hesite em reler as aulas anteriores e utilizar outros recursos, como os meios de comunicação com os tutores. Muito bem, vamos agora avançar no assunto, dando continuidade à aula anterior. Nela, você aprendeu a receber e repassar os valores de parâmetros de uma tela para outra, não foi? Apesar dessa abordagem ser útil em algumas situações, você viu que o repasse de parâmetros de requisições Web possui limitações, como o trabalho extra de codificação, o perigo de transitar entre as requisições de dados sensíveis (senhas, dados bancários etc.) e a perca das informações caso a máquina do cliente trave. Além disso, existem situações que queremos identificar o usuário conectado, mesmo que não haja informações a serem trafegadas entre páginas. Lembre-se de que estamos trabalhando com sistemas Web, ou seja, sistemas que podem estar sendo acessados por dezenas ou até milhares de usuários ao mesmo tempo. Imagine quantos usuários estão acessando um dos serviços do Google – correio eletrônico, sistema de busca, orkut, entre outros, neste exato momento, ao mesmo tempo? Nesta aula, você aprenderá um recurso elaborado e muito útil, chamado de sessão, o qual pode evitar as limitações da abordagem de repasse de parâmetros vista na aula anterior. Mas, lembre-se, apesar do recurso que você irá aprender agora ser o mais interessante na maioria das situações, o anterior continua válido para situações simples, em que a passagem de parâmetros de uma tela para outra mostra-se adequada. Ao término desta aula, você será capaz de: • entender o funcionamento das sessões em aplicações Web; • utilizar esse recurso na programação Web utilizando a linguagem Java. Um sistema de autenticação Para apresentar os conceitos da aula de hoje, faremos uso de um sistema de autenticação, além do próprio sistema de cadastro de dados pessoais e profissionais visto na aula passada. Sobre a parte de autenticação, a ideia é que o usuário precise entrar com sua credencial (combinação de nome de usuário e senha válidos). A tela inicial desse sistema pode ser vista na Figura 1. Figura 1 – Tela inicial do sistema de autenticação Após entrar com seu nome de usuário e senha, o sistema de autenticação irá verificar se a combinação de nome de usuário e senha é válida. Caso a combinação seja válida, o usuário visualizará uma página de entrada com uma mensagem de saudação personalizada (ver Figura 2). Se a combinação não for válida, uma mensagem de erro será apresentada (Figura 3). Figura 2 – Autenticação do usuário Jose Figura 3 – Tela do sistema quando nome de usuário e senha não são válidos Sessões de usuário Para implementar o sistema de autenticação mostrado na seção anterior, faremos uso do conceito de sessão do usuário. A ideia de sessão é basicamente a de manter o estado do cliente (ou melhor, das informações sobre o cliente) no próprio servidor. Lembre-se de que na aula anterior o estado do cliente (dados pessoais, profissionais e bancários) ficavam no lado cliente, ou seja, no navegador Web. Usamos os campos ocultos para isso, não foi? Caso o navegador do cliente fosse fechado, perderíamos todos os dados. Pois bem, para manter o estado do cliente no lado do servidor, precisamos de uma classe Java para representar esse estado. O nome dessa classe (na verdade, interface) é javax.servlet.http.HttpSession. Dessa forma, cada usuário poderá ter um e apenas um objeto do tipo HttpSession para representar o seu estado (informações). Para criar um objeto sessão, para representar um cliente, você pode fazer uso do método getSession() da classe HttpServletRequest. Esse método tem o seguinte comportamento: • se não existe nenhum objeto sessão para representar o cliente Web, esse método cria um objeto do tipo HttpSession e relaciona esse objeto ao cliente Web; • caso já exista um objeto sessão relacionado ao cliente Web, esse método simplesmente retorna o objeto. Você pode estar se perguntando agora como é que se relaciona um objeto a um cliente Web. Vamos pensar bem, se tivermos 30 usuários acessando o sistema ao mesmo tempo, quando chega uma requisição Web, como podemos identificar um determinado usuário? Uma primeira solução seria observar o endereço IP da máquina do cliente. Isso pode ser visto no código mostrado na Listagem 1. A classe HttpServletRequest possui vários métodos para acessarmos informações sobre a requisição. Uma dessas informações é o endereço IP ou nome do host de onde vem a requisição (ver métodos getRemoteAddr() e getRemoteHost() da Listagem 1). Porém, esses métodos só serão úteis para identificar os clientes Web em alguns casos, quando seu Servlet estiver rodando em um servidor Web e os clientes Web rodando em máquinas na Internet com IP real. Por exemplo, se várias máquinas estão conectadas a um roteador e esse roteador está conectado à internet, os acessos de todas as máquinas irão aparecer com o IP do roteador, pois ele é que está ligado à internet e apenas o seu IP é que é real. 01 package aula04; 02 03 import java.io.IOException; 04 import java.io.PrintWriter; 05 06 import javax.servlet.ServletException; 07 import javax.servlet.http.HttpServlet; 08 import javax.servlet.http.HttpServletRequest; 09 import javax.servlet.http.HttpServletResponse; 10 11 public class ServletMostrarIp extends HttpServlet { 12 protected void doGet(HttpServletRequest request, 13 HttpServletResponse response) throws ServletException, IOException { 14 PrintWriter resposta = response.getWriter(); 15 resposta.write("IP do cliente: "); 16 resposta.write(request.getRemoteAddr()); 17 resposta.write(" Host do cliente: "); 18 resposta.write(request.getRemoteHost()); 19 } 20 } Listagem 1 – Servlet que mostra informações sobre máquina do cliente A saída encontrada para o problema de identificação do cliente Web é dada pelos chamados cookies. Oscookies HTTP são pedaços de textos armazenados no navegador Web. Esse texto consiste em um ou mais pares de identificador-valor. Cada cookie está associado a um determinado sítio na internet. Sendo assim, o sistema do Banco do Brasil, por exemplo, pode gerar um número aleatório para um usuário após ele ter sido autenticado e mandar esse identificador para ser armazenado no navegador Web. Em todo acesso que for feito dali em diante, o navegador Web irá enviar esse identificador para o servidor. Dessa forma, o servidor pode saber qual usuário está acessando o sistema naquele momento. Ficou claro? Esse número aleatório que é gerado para cada usuário é, basicamente, o identificador único da sessão do usuário. Vamos voltar novamente para a criação dos objetos HttpSession. Ao ser executado, o método getSession() irá, de forma transparente para o programador, enviar na resposta do Servlet uma instrução indicando o cookie que deve ser criado, cujo valor é um identificador único para o cliente e o qual será utilizado nas demais requisições para fazer a identificação desse cliente. Dessa forma, quando usamos sessões, estamos dizendo para o servidor e o navegador Web criarem um vínculo, e isso tudo é feito de forma transparente e automática para nós! Vamos ver agora esse assunto em prática. Vejamos como pode ser implementada a funcionalidade de autenticação (login) do usuário no exemplo a seguir. Exemplo 1 Em primeiro lugar, temos a página HTML de autenticação (login.html) mostrada na Listagem 2. 01 02 03
04 Usuário:
05 Senha:
06
07 08 Listagem 2 – Código HTML da página de autenticação do usuário O formulário de autenticação de usuário está direcionado para um Servet de nome ServletLogin, cujo código fonte é visto na Listagem 3. Observe que os comandos das linhas 22 e 23 são responsáveis por pegar o nome de usuário e senha. Esses dados são então validados pelo método autenticar (linhas 35 a 40). Por estar fora do assunto desta aula, essa validação apenas verifica se o nome de usuário e senha não são vazios nem nulos. Em um sistema real, esse método deveria verificar, por exemplo, se os dados enviados pelo usuário estão de acordo com os dados cadastrados em uma base de dados. Caso o método autenticar() retorne o valor true, isso quer dizer que o nome de usuário e senha são válidos. Nesse caso, a sessão do usuário (linha 28) é criada, caso ainda não exista, e a informação do nome do usuário é armazenada nela, através do método setAttribute() (linha 29). O método setAttribute() recebe dois parâmetros, o nome do atributo e o seu valor. Já havíamos falado que a sessão guarda o estado do cliente, não é? Muito bem, o estado nada mais é do que esse mapeamento de “atributos” do cliente nos seus respectivos valores. No caso, cada sessão vai mapear o atributo de nome “usuario” (note o uso da constante USUARIO) para o nome de usuário utilizado pelo cliente para se autenticar no sistema. Ao autenticar o usuário, uma mensagem de boas vindas será apresentada (linha 30). Caso contrário, uma mensagem de nome de usuário e senha inválidos é mostrada (linha 32). 01 package aula04; 02 03 import java.io.IOException; 04 import java.io.PrintWriter; 05 06 import javax.servlet.ServletException; 07 import javax.servlet.http.HttpServlet; 08 import javax.servlet.http.HttpServletRequest; 09 import javax.servlet.http.HttpServletResponse; 10 import javax.servlet.http.HttpSession; 11 12 public class ServletLogin extends HttpServlet { 13 public static final String USUARIO = "usuario"; 14 15 protected void doGet(HttpServletRequest request, 16 HttpServletResponse response) throws ServletException, IOException { 17 doPost(request, response); 18 } 19 20 protected void doPost(HttpServletRequest request, 21 HttpServletResponse response) throws ServletException, IOException { 22 PrintWriter resposta = response.getWriter(); 23 resposta.write(""); 24 String nomeUsuario = request.getParameter(USUARIO); 25 String senhaUsuario = request.getParameter("senha"); 26 // Login e senha corretos 27 if (autenticar(nomeUsuario, senhaUsuario)) { 28 HttpSession sessao = request.getSession(); 29 sessao.setAttribute(USUARIO, nomeUsuario); 30 resposta.write("Bem vindo, " + nomeUsuario); 31 } else { 32 resposta.write("Usuário e senha inválidos"); 33 } 34 resposta.write(""); 35 } 36 37 private boolean autenticar(String nomeUsuario, String senhaUsuario) { 38 // Aqui entraria o código de autenticação. 39 // Está verificando apenas se é diferente de nulo ou vazio 40 return !("".equals(nomeUsuario) || "".equals(senhaUsuario) 41 || nomeUsuario == null || senhaUsuario == null); 42 } 43 } Listagem 3 – Código do Servlet de autenticação Até agora, tudo bem? Faça as atividades e reveja o conteúdo visto até agora, se necessário. 1. Implemente a página HTML de autenticação e o ServletLogin, de acordo como mostrado no exemplo que resolvemos juntos. Novamente, tente fazer sem olhar as listagens apresentadas, tirando dúvidas somente quando necessário. Execute o sistema de autenticação implementado, testando ambos os casos de usuário e senha inválidos e de usuário autenticado. 2. Altere o ServletLogin para autenticar apenas o usuário de nome “jose” e senha “12345”. Execute novamente o sistema e certifique-se de que apenas esse usuário está tendo acesso ao sistema. Dica: compare os parâmetros recebidos com as Strings “jose” e “12345”. Usando sessão em outras partes do sistema Ok, vamos em frente no assunto, vamos agora ver como a sessão é utilizada pelas demais páginas do sistema. Uma vez autenticado, o cliente pode agora realizar as atividades disponibilizadas pelo sistema. Para isso, vamos fazer uma alteração no ServletLogin. Vamos trocar o código da linha 30 da Listagem 3 (mensagem de boas vindas) por um comando de redirecionamento: request.getRequestDispatcher("ServletMenu").forward(request, response); O comando apresentado faz com que a responsabilidade de resposta para a requisição sendo atualmente processada seja transferida (método forward()) para o ServletMenu (método getRequestDispatcher()). Ou seja, tudo o que for escrito na resposta (PrintWriter de resposta) será descartado e a resposta final para o usuário será aquela apresentada pelo Servlet indicado como parâmetro do método getRequestDispatcher(). E qual a ideia de transferir a responsabilidade de montar a resposta para outro Servlet? Uma vez o usuário autenticado, vamos agora apresentar as funções que o usuário pode executar no sistema. Isso poderia ser feito pelo próprio ServletLogin, mas teríamos uma mistura de funcionalidades (autenticação de usuário e apresentação de menu de funcionalidades). Sendo assim, optei por separar o código em dois Servlets, ok? Vamos então considerar a funcionalidade do sistema vista na aula anterior, aquele cadastro de dados pessoais e financeiros realizados em duas telas de cadastro, tudo bem? Nosso ServletMenu pode ser implementado como mostra a Listagem 4. Observe que na linha 22 nós tentamos recuperar a sessão do usuário. Opa, mas agora o método getSession() de HttpServletRequest está recebendo um boleano como parâmetro! E é isso mesmo! Existem dois métodos getSession() na classe HttpServletRequest. O que não recebe parâmetro você já conhece, já o que recebe um boleano tem o funcionamento descrito a seguir. • Caso o valor passado para o método seja o valor true, o método irá se comportar da mesma forma que o método getSession() que não recebe parâmetros. Por outro lado, se o valor passado for false (como no caso do ServletMenu), o método irá retornar à sessão associada ao cliente, se ela já tiver sido criada, ou o valor null, caso contrário. Em outras palavras, o método getSession(false) nunca irá criar uma sessão, apenas retornar uma criada previamente. Atenção! Se não ficou claro o porquê de usar o método getSession(false), pense no seguinte caso: um hacker descobre o link direto para o ServletMenu; ele pula a etapa de autenticação e executa diretamente o ServletMenu; o ServletMenu é então responsável por verificar se o cliente que o está acessando está realmente autenticado ou não. Se não houver uma sessão criada previamente, é porque ele provavelmente não foi autenticado! Se não for isso, então o servidor foi reiniciado (os objetos sessão são perdidos) ou a sessão expirou (ainda vamos falar sobre isso). Recapitulando, se você precisa que um usuário se autentique em um sistema, você pode fazer com que somente o Servlet responsável pela autenticação esteja autorizado a criar uma sessão para o cliente. Outra opção, caso você queira também manter o estado de clientes ainda não autenticados, é deixar qualquer Servlet criar sessões para usuários, mas apenas o Servlet de autenticação é responsável por criar o atributo “usuario”, indicando que ele já foi autenticado. Note que estamos lhe mostrando agora as duas verificações para identificar se o usuário está realmente autenticado: seu objeto de sessão deve estar criado e o atributo “usuario” deve estar preenchido (linha 23). Ser rigoroso com segurança é sempre bom! 01 package aula04; 02 03 import java.io.IOException; 04 import java.io.PrintWriter; 05 06 import javax.servlet.ServletException; 07 import javax.servlet.http.HttpServlet; 08 import javax.servlet.http.HttpServletRequest; 09 import javax.servlet.http.HttpServletResponse; 10 import javax.servlet.http.HttpSession; 11 12 public class ServletMenu extends HttpServlet { 13 protected void doGet(HttpServletRequest request, 14 HttpServletResponse response) throws ServletException, IOException { 15 doPost(request, response); 16 } 17 18 protected void doPost(HttpServletRequest request, 19 HttpServletResponse response) throws ServletException, IOException { 20 PrintWriter resposta = response.getWriter(); 21 resposta.write(""); 22 HttpSession sessao = request.getSession(false); 23 if (sessao == null || sessao.getAttribute(ServletLogin.USUARIO) == null) { 24 resposta.write( "Faça primeiro o seu login
"); 25 } else { 26 resposta.write("Operações disponíveis:
"); 27 resposta.write( "1. Cadastro
"); 28 // Aqui podem ser adicionadas novas opções de funcionalidades 29 } 30 resposta.write(""); 31 } 32 } Listagem 4 – Código do ServletMenu, o qual mostra as funcionalidades disponíveis para o usuário Pois bem, se o usuário não estiver autenticado, então, uma mensagem é apresentada para o usuário (linha 24) indicando que o mesmo deve se autenticar. Caso contrário, um menu de funcionalidades é apresentado (linhas 26 a 28). Note que novas funcionalidades podem ser adicionadas na linha 28, adicionando-se código similar aos da linha 26 e 27. No caso, a página gerada pelo ServletMenu aponta para a página inicial do sistema de cadastro desenvolvido na última aula (linha 27), então, o cadastro estará funcionando usando a abordagem antiga (passagem de parâmetros). A tela resultante com o menu de opções é vista na Figura 4. O link de cadastro leva a um Servlet ainda não criado, então, não estranhe se encontrar um erro ao clicar nesse link. Figura 4 – Tela de funcionalidades disponíveis para o usuário 1. Altere a classe ServletLogin e implemente a classe ServletMenu conforme apresentamos para você, tentando não olhar para o código mostrado, a não ser em caso de dúvidas. Nesse caso, releia o assunto, não só o código, ok? Execute o sistema e verifique se seu comportamento está funcionando de acordo com o que deveria. 2. Altere a programação visual das páginas do sistema, dando um caráter personalizado. As telas mostradas durante as aulas são bem simples, então, tenho certeza que você conseguirá torná-las mais agradáveis! Explore o seu conhecimento obtido na disciplina de autoria Web, relendo aquele material sempre que necessário. Vamos agora alterar o sistema de cadastro desenvolvido na aula passada, na qual tínhamos usado a abordagem de repasse de parâmetros, para considerar sua implementação através do uso de sessões. Pois bem, para você não apagar o código já criado, vamos criar um novo Servlet e chamá-lo de ServletTela1CadastroSessao. Altere o fomulário Web da página cadastro.html para apontar para esse novo Servlet:
Depois, copie a implementação dos métodos doGet() e doPost() do ServletTela1Cadastro para o ServletTela1CadastroSessao. A página gerada pelo ServletTela1CadastroSessao precisa ter um formulário que aponte para ServletTela2CadastroSessao, um novo Servlet a ser criado. Além disso, precisaremos receber os dados que vêm da primeira tela (dados pessoais) e colocá-los em algum lugar temporário no servidor. E qual é esse lugar? O objeto sessão... Para estruturar melhor os dados a serem recebidos, é melhor criar uma classe para agrupá-los, como visto na Listagem 5. 01 package aula04; 02 03 public class DadosPessoais { 04 05 private String nome; 06 private String sobrenome; 07 private String rua; 08 private String complemento; 09 private String cidade; 10 private String cep; 11 private String estado; 12 public String getNome() { 13 return nome; 14 } 15 public void setNome(String nome) { 16 this.nome = nome; 17 } 18 public String getSobrenome() { 19 return sobrenome; 20 } 21 public void setSobrenome(String sobrenome) { 22 this.sobrenome = sobrenome; 23 } 24 public String getRua() { 25 return rua; 26 } 27 public void setRua(String rua) { 28 this.rua = rua; 29 } 30 public String getComplemento() { 31 return complemento; 32 } 33 public void setComplemento(String complemento) { 34 this.complemento = complemento; 35 } 36 public String getCidade() { 37 return cidade; 38 } 39 public void setCidade(String cidade) { 40 this.cidade = cidade; 41 } 42 public String getCep() { 43 return cep; 44 } 45 public void setCep(String cep) { 46 this.cep = cep; 47 } 48 public String getEstado() { 49 return estado; 50 } 51 public void setEstado(String estado) { 52 this.estado = estado; 53 } 54 55 } Listagem 5 – Código-fonte da classe DadosPessoais Cada parâmetro da requisição será armazenado em um atributo da classe DadosPessoais. Por exemplo, o nome do usuário será armazenado no atributo nome da classe DadosPessoais. A mesma coisa acontece para os demais parâmetros da requisição a serem armazenados na sessão do usuário. Sendo assim, o nosso ServletTela1CadastroSessao será implementado de acordo com o código mostrado na Listagem 6. Note na linha 42 que o formulário agora referencia o ServletTela2CadastroSessao ao invés do antigo ServletTela1Cadastro. Note também a verificação de que o usuário está autenticado no sistema (linhas 27 a 30). Essa verificação será feita por todos os Servlets que exijam que o usuário esteja autenticado. 01 package aula04; 02 03 import java.io.IOException; 04 import java.io.PrintWriter; 05 06 import javax.servlet.ServletException; 07 import javax.servlet.http.HttpServlet; 08 import javax.servlet.http.HttpServletRequest; 09 import javax.servlet.http.HttpServletResponse; 10 import javax.servlet.http.HttpSession; 11 12 public class ServletTela1CadastroSessao extends HttpServlet { 13 14 protected void doGet(HttpServletRequest request, 15 HttpServletResponse response) throws ServletException, IOException { 16 doPost(request, response); 17 } 18 19 protected void doPost(HttpServletRequest request, 20 HttpServletResponse response) throws ServletException, IOException { 21 PrintWriter resposta = response.getWriter(); 22 resposta.write(""); 23 resposta.write(""); 24 resposta.write("Dados Profissionais"); 25 resposta.write(""); 26 resposta.write(""); 27 HttpSession sessao = request.getSession(false); 28 if (sessao == null || sessao.getAttribute(ServletLogin.USUARIO) == null) { 29 resposta.write( "Faça primeiro o seu login
"); 30 } else { 31 DadosPessoais dados = new DadosPessoais(); 32 dados.setNome(request.getParameter("nome")); 33 dados.setSobrenome(request.getParameter("sobrenome")); 34 dados.setRua(request.getParameter("rua")); 35 dados.setComplemento(request.getParameter("complemento")); 36 dados.setCidade(request.getParameter("cidade")); 37 dados.setCep(request.getParameter("cep")); 38 dados.setEstado(request.getParameter("estado")); 39 sessao.setAttribute("dadosPessoais", dados); 40 41 resposta.write("Preencha seus dados profissionais:"); 42 resposta.write( ""); 43 resposta.write( "Empresa:
"); 44 resposta.write("Endereço profissional:
"); 45 resposta.write("Rua: "); 46 resposta.write( "Complemento:
"); 47 resposta.write( "Cidade:"); Listagem 6 – Código-fonte do ServletTela1CadastroSessao Finalmente, o código da linha 31 a 39 é responsável por pegar os dados da requisição, agrupá-los no objeto do tipo DadosPessoais e armazená-lo na sessão do usuário. Basicamente, isso é feito por comandos como o da linha 33, onde o valor do parâmetro da requisição (retorno do método getParameter(“sobrenome”)) é utilizado para setar o atributo do objeto DadosPessoais (método setSobrenome()). No final de tudo, é necessário armazenar o objeto DadosPessoais na sessão (linha 39). Você deve ter notado que o segundo parâmetro do método setAttribute() da sessão é na verdade do tipo Object. Sendo assim, você pode armazenar qualquer tipo de objeto na sessão do usuário, incluindo o do tipo DadosPessoais. Para o ServletTela2CadastroSessao (sucessor do antigo ServletTela2Cadastro), precisaremos criá-lo para receber os dados da segunda tela de cadastro (dados profissionais), recuperar os dados pessoas da sessão do usuário e finalmente montar a tela de confirmação de cadastro. Vamos ver como fica seu código? Ele está mostrado na Listagem 7. Note o uso do bloco de código das linhas 27 a 31, responsáveis por verificar se o usuário está autenticado no sistema, bem como o código da linha 34 a 38, responsável por verificar se o objeto DadosPessoais foi criado e colocado na sessão do usuário. Se o objeto não tiver sido criado e colocado na sessão, o que isso quer dizer? Bom, muito provavelmente o usuário se autenticou no sistema, mas digitou diretamente a URL da segunda tela, pulando o preenchimento da primeira tela. Por fim, note que o bloco de código da linha 39 a 51 pegam os dados do objeto DadosPessoais, enquanto o bloco de código da linha 52 a 63 pegam dados do objeto request, ou seja, digitados e enviados pela tela anterior do sistema. 01 package aula04; 02 03 import java.io.IOException; 04 import java.io.PrintWriter; 05 06 import javax.servlet.ServletException; 07 import javax.servlet.http.HttpServlet; 08 import javax.servlet.http.HttpServletRequest; 09 import javax.servlet.http.HttpServletResponse; 10 import javax.servlet.http.HttpSession; 11 12 public class ServletTela2CadastroSessao extends HttpServlet { 13 14 protected void doGet(HttpServletRequest request, 15 HttpServletResponse response) throws ServletException, IOException { 16 doPost(request, response); 17 } 18 19 protected void doPost(HttpServletRequest request, 20 HttpServletResponse response) throws ServletException, IOException { 21 PrintWriter resposta = response.getWriter(); 22 resposta.write(""); 23 resposta.write(""); 24 resposta.write("Confirmação de registro"); 25 resposta.write(""); 26 resposta.write(""); 27 HttpSession sessao = request.getSession(false); 28 if (sessao == null || sessao.getAttribute(ServletLogin.USUARIO) == null) { 29 resposta.write( 30 "Faça primeiro o seu login"); 31 } else { 32 resposta.write( "

Registro realizado com sucesso!



"); 33 resposta.write("Seus dados pessoais:
"); 34 DadosPessoais dados = (DadosPessoais) sessão 35 .getAttribute("dadosPessoais"); 36 if (dados == null) { 37 resposta.write( "Dados incompletos. Inicie o sistema novamente."); 38 } else { 39 resposta.write(dados.getNome() + " " 40 + dados.getSobrenome()); 41 resposta.write("
"); 42 resposta.write(dados.getRua()); 43 resposta.write("
"); 44 resposta.write(dados.getComplemento()); 45 resposta.write("
"); 46 resposta.write(dados.getCidade()); 47 resposta.write(", "); 48 resposta.write(dados.getCep()); 49 resposta.write(", "); 50 resposta.write(dados.getEstado()); 51 resposta.write("
"); 52 resposta.write("Seus dados profissionais:
"); 53 resposta.write(request.getParameter("empresa")); 54 resposta.write("
"); 55 resposta.write(request.getParameter("ruaEmpresa")); 56 resposta.write("
"); 57 resposta.write(request.getParameter("complementoEmpresa")); 58 resposta.write("
"); 59 resposta.write(request.getParameter("cidadeEmpresa")); 60 resposta.write(", "); 61 resposta.write(request.getParameter("cepEmpresa")); 62 resposta.write(", "); 63 resposta.write(request.getParameter("estadoEmpresa")); 64 } 65 } 66 resposta.write(""); 67 } 68 69 } Listagem 7 – Servlet responsável por montar a segunda tela de cadastro 1. Implemente o novo sistema de cadastro, com tela de login, conforme mostrado, usando sessões ao invés de repasse de parâmetros. Lembre-se de tentar não olhar para o código mostrado, assim você vai poder comprovar pra você mesmo que você entendeu bem os conceitos. Execute o sistema implementado e verifique que tudo está funcionando corretamente. Corrija quaisquer erros de implementação que você pode ter feito. 2. O acesso ao arquivo cadastro.html não tem verificação de autenticação de usuário, ou seja, qualquer usuário, autenticado ou não, pode ver seu conteúdo. Realize o acesso a esse arquivo sem que você esteja autenticado. Se necessário, reinicie o servidor Web para que você não esteja mais autenticado. 3. Crie um ServletCadastro cujo papel é gerar o conteúdo da página cadastro.html. Faça nesse Servlet o controle de autenticação de usuário, conforme foi feito com o ServletTela1CadastroSessao. Execute o Servlet e confirme que apenas usuários autenticados têm acesso à página inicial de cadastro gerada pelo Servlet. Por fim, altere o ServletMenu para que a página de menu gerada referencie o ServletCadastro, e não mais o arquivo cadastro.html. Renomei o arquivo cadastro.html para cadastro_antigo.html e verifique que o sistema, quando acessado pelo ServletLogin, continua funcionando. Finalizando sessões Uma vez que uma sessão é aberta, existem algumas formas de fazer com que a mesma seja finalizada. A primeira forma de se fazer isso é reiniciando o servidor. Caso isso ocorra, todos os objetos sessão serão perdidos. A reinicialização do sistema não é frequente, na prática, visto que reiniciá-los quer dizer deixar os usuários sem poder usar o sistema durante um certo período de tempo (tempo de reinicialização). Uma segunda forma de se finalizar uma sessão é através do método invalidate() da classe HttpSession. Esse método é utilizado para liberar o objeto sessão do servidor (encerrar a sessão) e descartar todo o seu estado (atributos). Essa forma de se liberar a sessão é geralmente utilizada quando se tem sistemas de autenticação, onde o usuário realiza uma ação solicitando essa ação (exemplo: logout do usuário). Em um sistema bancário, por exemplo, ao terminar suas operações, um usuário deve clicar na opção de sair do sistema, encerrando assim sua sessão. Por fim, a terceira forma de encerrar uma sessão é o chamado timeout de sessão. O termo timeout é utilizado para indicar que um determinado tempo de sessão inativa foi alcançado. Por exemplo, umtimeout de 20 minutos quer dizer que, se o usuário relativo a essa seção não acessar o sistema por mais de 20 minutos, a sessão será descartada automaticamente pelo servidor. Esse recurso é importante, já que às vezes acessamos um sistema Web através de computadores compartilhados. Usuários que acessam contas bancárias, por exemplo, devem ter sua sessão automaticamente expirada (liberada) em torno de 7 minutos de inatividade. Dessa forma, se o usuário for embora e esquecer o sistema aberto em um computador compartilhado, uma pessoa má intencionada que chegue 10 minutos depois não vai poder acessar a conta do usuário do banco, pois a sessão já vai ter expirado. O valor do timeout de uma sessão pode ser configurado pelo método setMaxInactiveInterval(), o qual recebe um valor inteiro representando o tempo de timeout em segundos. 1. Implemente um Servlet chamado ServletSair, o qual deve invalidar a sessão atual do usuário e redirecionar a requisição para o Servlet de autenticação. Coloque um link de nome sair dentre as opções geradas pelo ServletMenu, o qual deve apontar para o ServletSair. Execute o sistema e verifique se o ServletSair está funcionando de acordo. Muito bem, é isso, chegamos ao fim da nossa aula! Faça sua autoavaliação e, qualquer dúvida, releia o material. Na próxima aula iremos dar continuidade na programação Web, tratando de uma nova tecnologia, a qual irá lhe ajudar a desenvolver código de forma mais legível e elegante. Até lá! Leitura complementar Para complementar seu entendimento sobre o uso de sessões, analise os métodos disponibilizados pelas classes envolvidas. Isso pode ser feito observando-se a API do Java: JavaTM Platform Enterprise Edition, v 5.0 – API Specifications • Em especial, olhar o pacote javax.servlet.http e a interface HttpSession. Para os mais curiosos, informações extra sobre cookies pode ser vista na classe Cookie nesse mesmo pacote. Resumo Nesta aula, você aprendeu a trabalhar com sessões de usuário. Você viu que uma sessão pode ser aberta para um determinado usuário com o objetivo de manter no servidor o estado (informações) do cliente. Um dos usos mais comuns de sessão é para autenticação de usuários. Você viu que o método getSession() pode receber um boleano como parâmetro, modificando um pouco o seu comportamento. No caso, se o boleano for false, o método não deve criar uma sessão para o usuário, mas retornar o valor null. Você também viu que existem várias formas de uma sessão ser finalizada (timeout, reinicialização do servidor ou uso do método invalidate()). 1. Descreva o funcionamento das sessões dos usuários com relação aos sistemas Web. 2. Liste as principais classes e métodos utilizados na programação Java para Web utilizados para manipular sessões de usuários. Indique quando um Servlet deve ou não criar uma sessão e como eles podem fazer isso. Desenvolvimento Web Aula 5 – Introdução a JavaServer Pages Olá, seja bem vindo(a) a nossa quinta aula da disciplina de Desenvolvimento Web utilizando a linguagem Java. Até agora, você aprendeu a fazer uso dos Servlets para acessar os parâmetros das requisições Web, processá-los e montar as páginas de resposta. Pois bem, os Servlets realmente são a base da programação Web em Java, porém, seu uso direto nos leva a alguns problemas. Um dos principais problemas que podemos perceber com o uso de Servlets puro, ou seja, quando não usamos nenhuma outra tecnologia além dos Servlets, é que o código HTML das páginas de resposta fica embutido dentro do código Java. Essa mistura nos traz diversas dificuldades, como a de entendimento do código e necessidade de reiniciar o servidor Web para que alterações de layout do HTML sejam carregadas. Nesta aula, você aprenderá a usar uma tecnologia chamada de JavaServer Pages (JSP), a qual deve ser utilizada para sanar o problema citado. Ao término desta aula, você estará apto a: • descrever o funcionamento da solução JSP. • entender a sintaxe utilizada na criação de páginas JSP. Mistura do código HTML e do código Java Como comentado no início da nossa aula de hoje, o uso de Servlets sem usar nenhuma outra tecnologia complementar leva a uma mistura entre o código Java da aplicação Web e o código HTML das páginas de resposta. Ambos os códigos Java e HTML ficam armazenados nas classes dos Servlets. Essa mistura nos traz diversas dificuldades, como veremos a seguir. Imagine que você possua uma empresa desenvolvedora de aplicações para internet e que exista um projetista Web, ou seja, uma pessoa responsável por fazer a edição das páginas Web. Normalmente, essa pessoa tem muito conhecimento de HTML, CSS e de outras tecnologias que tornam as páginas Web bonitas e fáceis de serem utilizadas. Entretanto, os projetistas Web geralmente não possuem conhecimento de programação em linguagens de uso geral, como Java, C, Net etc. Essas pessoas normalmente não têm condições de fazer alterações no código Java da aplicação, mesmo que a alteração seja relativa ao código HTML embutido. Dessa forma, qualquer alteração no código HTML, mesmo que simples, como o fato de deixar um texto em negrito ao invés de itálico, requer que o programador Java altere a classe do Servlet baseado na alteração sugerida pelo projetista Web. Essa alteração no Servlet requer que o servidor seja reiniciado, já que uma nova implementação do Servlet precisa ser carregada pelo servidor. Ao reiniciar o servidor, teremos os seguintes inconvenientes: • o sistema ficará indisponível para os usuários durante o tempo de reinicialização; • as sessões dos usuários serão perdidas, ou seja, os objetos sessão que guardam os estados dos clientes no servidor serão descartados durante a reinicialização do servidor. o Dessa forma, os clientes já autenticados no sistema terão que se autenticar novamente. o Clientes podem vir a perder dados já digitados. Imagine que um cliente usando o sistema de cadastro desenvolvido nas aulas anteriores tenha preenchido a primeira tela de cadastro (dados pessoais) e estivesse preenchendo a segunda tela, toda essa informação seria perdida na reinicialização do servidor. Além desses problemas todos, a mistura do código HTML com o código Java atrapalha também a vida do programador. Imagine como o código Java dos Servlets ficaria menor sem todos aqueles comandos write() que escrevem código HTML. Dependendo do caso, o código poderia ficar até 4 ou 5 vezes menor. Por todos esses motivos, foi desenvolvida a tecnologia JavaServer Pages, ou simplesmente JSP, como será visto a seguir. JavaServer Pages JavaServer Pages (JSP) é uma tecnologia utilizada para se criar conteúdo tanto estático como dinâmico. Por conteúdo estático, queremos dizer toda aquela parte do código HTML das páginas de resposta que não mudam de acordo com os parâmetros da requisição ou com o estado do cliente (sessão). São exemplos dessa parte as imagens, textos e outros conteúdos que sempre são mostrados pelo sistema. Já o que chamamos de conteúdo dinâmico é a parte das páginas de resposta cujo valor depende dos parâmetros da requisição ou do estado do cliente (sessão). Por exemplo, em um sistema de compras on-line, o conteúdo do carrinho de compras depende da sessão do cliente. Isto porque o que será mostrado é a lista de produtos selecionados pelo usuário, a qual possivelmente foi sendo montada ao longo de várias telas do sistema, correto? Muito bem, vamos então ao que interessa. Assim como no caso das páginas HTML, o JSP é um arquivo texto que, quando acessado, resultará em uma página Web. Os arquivos JSP tem extensão .jsp e podem conter conteúdo estático, geralmente definidos por marcadores HTML, e conteúdo a ser gerado dinamicamente, definido por marcadores específicos do JSP. Veja o exemplo simples de página JSP, de nome aleatorio.jsp, mostrado na Listagem 1. Nesse exemplo, um número aleatório é gerado toda vez que a página é acessada. Note que a maior parte do conteúdo do arquivo é de código HTML. Vamos ver então o que muda. Primeiro, você pode observar um novo tipo de instrução na linha 1 e 2. Essa instrução <%@ page %> é chamada de diretiva JSP e, no caso do exemplo mostrado, é opcional, indicando informações sobre o conteúdo do JSP. Essa diretiva está indicando que a linguagem de programação utilizada no JSP é Java, que o conteúdo da página a ser gerada é texto HTML (text/html) e que a codificação de caractere utilizada é a ISO-8859-1. Falaremos mais sobre diretivas JSP depois. 01 <%@ page language="java" contentType="text/html; charset=ISO-8859-1" 02 pageEncoding="ISO-8859-1"%> 03 04 05 Número aleatório 06 07 08 Esta página gera um novo número aleatório 09 toda vez que é acessada: <%= Math.random() %> 10 11 Listagem 1 – Página JSP (aleatorio.jsp) para geração de números aleatórios. A outra novidade encontra-se na linha 9. Note o uso do marcador <%= %>. Esse marcador JSP delimita um espaço que deve ser preenchido com uma expressão Java, ou seja, com uma instrução que retorne um valor de qualquer tipo, podendo ser uma String, um número, um boleano, um objeto qualquer etc. No caso do nosso exemplo, a expressão utilizada é Math.random(), uma instrução Java utilizada para retornar um número aleatório diferente toda vez que a mesma é executada. Dessa forma, toda vez que a página JSP é acessada pelo usuário, um novo número aleatório é gerado. A Figura 1 mostra diferentes acessos à pagina JSP, ilustrando o fato de que um número diferente é apresentado toda vez que acessamos novamente a página. Figura 1 – Página JSP de geração de números aleatórios Nesse momento, provavelmente ainda não está claro para você quem executa a instrução Math.random(), se é o navegador ou o servidor Web. Vamos responder essa pergunta a seguir, mas já podemos adiantar que todo o processamento é feito no lado do servidor! 1. Implemente o arquivo aleatorio.jsp conforme mostrado na Listagem 1. Salve o arquivo na pasta do projeto onde você usualmente coloca seus arquivos com extensão .html (exemplo: dentro da pasta WebContents). Acesse a URL relativa à página JSP, como, por exemplo, . Verifique se o conteúdo da página está sendo mostrado corretamente. 2. Faça novos acessos à página aleatório.jsp e confirme que o número gerado é aleatório e diferente dos anteriormente mostrados. 3. Realize uma alteração no conteúdo estático do arquivo aleatório.jsp, mudando, por exemplo, o texto que diz que um número aleatório será gerado a cada novo acesso. Salve o arquivo e realize o acesso através de sua URL, sem realizar, porém, nenhuma reinicialização. Confirme que a nova página de resposta será apresentada. Ciclo de vida do JSP Você já viu que páginas HTML possuem conteúdo estático e que Servlets podem ser responsáveis por gerar tanto conteúdo estático como dinâmico (gerar resposta de acordo com parâmetros da requisição). Uma página JSP, como você pode imaginar, são como os Servlets, podem ter tanto conteúdo estático como dinâmico, só que seu código parece mais com páginas HTML do que com o código dos Servlets. Para entender o funcionamento geral do JSP, é importante saber que essa tecnologia foi desenvolvida em cima da tecnologia dos Servlets. Você vai ver que, em Java, é muito comum que as novas tecnologias sejam desenvolvidas baseadas nas tecnologias prévias maduras e já estabelecidas. No caso, para entender a relação entre Servlets e JSP, vamos estudar o chamado ciclo de vida das páginas JSP. Uma página JSP é normalmente escrita pelo programador. Ela pode também ser editada diretamente pelo projetista Web, já que seu conteúdo é muito parecido com o de páginas HTML (veja novamente a Listagem 1). Uma vez criada a página, ela deve ser disponibiliza em um servidor Web, da mesma forma que ocorre com as páginas HTML. A página aleatorio.jsp, por exemplo, foi colocada na pasta WebContent do projeto de nomeProgramacaoWeb no servidor Web. Seu conteúdo poderá ser então acessado pela URL , considerando que o servidor está rodando na porta 8080. Ao ser acessado pela primeira vez, o arquivo JSP será traduzido para um Servlet. É isso mesmo, o JSP será transformado pelo servidor Web em um Servlet. Isso é feito de forma automática e transparente para o usuário e para o programador. Veja esse processo ilustrado na Figura 2. Figura 2 – Processo de transformação transparente do JSP em Servlet Fonte: Adaptada de Basham, Sierra e Bates (2008). Em verdade, o nome do Servlet gerado não importa para nós, já que esse processo é feito de forma automática e transparente. Para podermos entender melhor, vamos considerar que o arquivo aleatorio.jspseja traduzido para o Servlet mostrado na Listagem 2. Note que esse código, apesar de seu tamanho, está simplificado, sendo ainda maior na realidade. 01 package aula05; 02 03 import javax.servlet.http.HttpServletRequest; 04 import javax.servlet.http.HttpServletResponse; 05 import javax.servlet.jsp.JspWriter; 06 import javax.servlet.jsp.PageContext; 07 // Vários outros imports foram omitidos aqui 08 09 public final class aleatorio_jsp_gerado extends 10 org.apache.jasper.runtime.HttpJspBase implements 11 org.apache.jasper.runtime.JspSourceDependent { 12 13 // Vários atributos foram omitidos aqui 14 15 public void _jspService(HttpServletRequest request, 16 HttpServletResponse response) throws java.io.IOException, 17 ServletException { 18 19 // Várias declarações de variáveis foram omitidos aqui 20 HttpSession session = null; 21 ServletContext application = null; 22 ServletConfig config = null; 23 JspWriter out = null; 24 Object page = this; 25 JspWriter _jspx_out = null; 26 PageContext _jspx_page_context = null; 27 28 try { 29 response.setContentType("text/html"); 30 pageContext = _jspxFactory.getPageContext(this, request, response, 31 null, true, 8192, true); 32 _jspx_page_context = pageContext; 33 application = pageContext.getServletContext(); 34 config = pageContext.getServletConfig(); 35 session = pageContext.getSession(); 36 out = pageContext.getOut(); 37 _jspx_out = out; 38 39 write(" "); 40 out.write(" "); 41 out.write("Número aleatório "); 42 out.write(" "); 43 out.write(" "); 44 out.write("Esta página gera um novo número aleatório "); 45 out.write("toda vez que é acessada: "); 46 out.print(Math.random()); 47 out.write(" "); 48 out.write(" "); 49 out.write(""); 50 } catch (Throwable t) { // Código de tratamento de exceção foi omitido aqui 51 } 52 } 53 } Listagem 2 – Simplificação do código-fonte gerado para o Servlet que representa o arquivo aleatorio.jsp O primeiro ponto que devemos notar é a forma de tradução do JSP para o Servlet. Essa tradução, representada principalmente pelo bloco de linhas de código 39 a 49 da Listagem 2, basicamente segue o raciocínio demonstrado abaixo. • Todo o conteúdo será escrito através de uma variável de nome out. Apesar de seu tipo ser JspWriter, ela se comporta de forma semelhante à classe PrintWriter que estamos acostumados a usar ao escrevermos Servlets. • Conteúdos estáticos do arquivo aleatorio.jsp, como a linha que contém o marcador , serão traduzidos pelo comando out.write() recebendo o conteúdo estático como parâmetro. Esse exemplo é visto na linha 39 do código gerado (out.write(" ");). • Conteúdos dinâmicos, representados por marcadores e diretivas JSP, são traduzidos de forma a gerar o código Servlet equivalente. Veja alguns exemplos: • A diretiva <%@ page diz que o conteúdo da página de resposta é texto HTML (contentType="text/html; charset=ISO-8859-1"). Poderia ser outro tipo, como texto XML. No caso do HTML, essa instrução é traduzida para o comando da linha 29 (response.setContentType("text/html");). • A linha 9 da Listagem 1 (toda vez que é acessada: <%= Math.random() %>) contém tanto código estático (texto) como conteúdo dinâmico (expressão JSP que resultará em um número aleatório. • Nesse caso, a primeira parte, que é estática, será traduzida usando o comandoout.write("toda vez que é acessada: "); (linha 45). Basicamente o texto é transformado em uma String (colocado entre aspas duplas) e escrito pelo comando write. • Já a parte do conteúdo dinâmico é traduzido colocando o resultado da expressão Java como parâmetro do comando write. No caso, a expressão <%= Math.random() %>, é traduzida pro comando out.write( Math.random() ); (linha 46). Muito bem, espero que esteja claro que é possível traduzir as instruções contidas no arquivo em um Servlet. Isso é feito de forma automática e de forma transparente, então, você não precisa se preocupar em entender exatamente como essa tradução é feita. O importante é você saber que essa tradução existe e ter uma noção de como ela é feita! Dito isso, você pode se perguntar, esse processo de tradução não pode impactar o desempenho do sistema? A resposta é sim! O primeiro acesso a um arquivo JSP é sempre mais lento, pois exige a tradução do arquivo em um Servlet e a compilação do código Java gerado. Entretanto, a partir do segundo acesso, não há impacto no desempenho quando comparado à implementação através de Servlets puros. 1. Execute novamente o arquivo aleatorio.jsp e analise o código-fonte da página de resposta gerada. Observe que nenhuma diretriz ou marcador JSP é mostrado, já que essas instruções só são usadas para gerar o código-fonte do Servlet que irá implementar o JSP. 2. ltere o arquivo .jsp para que a o número gerado a ser mostrado seja igual ao número gerado pela instrução Math.random() multiplicado por 1000. Após salvar a alteração, acesse várias vezes o arquivo JSP pela sua URL, observe se o tempo de acesso da primeira página difere do tempo dos acessos subsequentes. Se necessário, faça novas alterações no arquivo para confirmar que o primeiro acesso é mais lento que os demais. JSP e variáveis declaradas Você já sabe que arquivos JSP são traduzidos para Servlets para poderem ser executados. Esse processo de tradução já foi inclusive demonstrado. Pois bem, agora você pode estar se perguntando o porquê de se criar um JSP e não um Servlet, já que no final o JSP vai acabar virando um Servlet. Essa resposta será dada a seguir. Uma coisa que devemos notar é que é muito mais fácil e legível trabalhar com o arquivo JSP do que com o Servlet gerado. É claro que o código gerado não está muito fácil de ser lido porque ele foi gerado automaticamente. Um ser humano teria condições de gerar um código muito mais legível, mas, mesmo assim, ainda seria mais fácil trabalhar direto com o arquivo JSP. Para confirmar isso, é só olharmos o código do arquivo JSP e o trecho de código das linhas 39 a 49 da Listagem 2. O que é mais legível, o código JSP ou esse bloco de linhas de código do Servlet gerado? Espero que você esteja concordando comigo que o arquivo JSP é mais fácil de ser trabalhado pelo programador e por um possível projetista Web. Sobre a tradução dos JSP nos Servlets, é importante você notar a existência do método _jspService()(linha 15) do Servlet gerado. Esse método _jspService() é responsável pela geração da página de resposta. Note que o Servlet gerado não possui métodos doGet() e doPost(), pois o mesmo herda de HttpJspBase(linha 10) ao invés de herdar da classe HttpServlet. Esse método possui os parâmetros request e response, da mesma forma que ocorre com os métodos doGet() e doPost(). Além disso, observe também o trecho de código das linhas 20 a 26 da Listagem 2. Esse bloco de código inclui a declaração de diversas variáveis locais ao método _jspService(), como, por exemplo: • session o Referência ao objeto sessão do usuário, se a mesma tiver sido criada. • aplication o Referência ao objeto do tipo ServletContext, o qual dá acesso ao contexto do Servlet. • config o Referência ao objeto do tipo ServletConfig, o qual dá acesso a informações de configuração do Servlet. • Out o Referência ao objeto de escrita na página de resposta a ser gerada. Ok, mas porque saber disso tudo? Simples, tanto os parâmetros do método como essas variáveis locais podem ser acessadas pelo código JSP. Por exemplo, podemos criar uma página JSP para indicar se o usuário possui uma sessão aberta. Para isso, criaremos o arquivo de nome info-sessao.jsp mostrado na Listagem 3. Esse arquivo, como mostrado na linha 8, usa o valor da variável session para acessar à sessão aberta para o usuário e nos mostrar o identificador único gerado para representar a sessão do usuário. Mas, cuidado, ao usar arquivos JSP, os objetos sessão são criados se não existirem. O comportamento é semelhante ao método getSession() de HttpServletRequest que não possui parâmetros. 01 <%@ page language="java" contentType="text/html; charset=ISO-8859-1" 02 pageEncoding="ISO-8859-1"%> 03 04 05 Informações sobre sessão 06 07 08 Id da sessão do usuário: <%= session.getId() %>
09 10 Listagem 3 – Arquivos JSP com informação sobre o ID da sessão 1. Implemente o exemplo mostrado para imprimir o ID da sessão do usuário. Execute e observe que, ao recarregar a página, o mesmo ID é retornado. Observe também que, ao reiniciar o servidor, um novo ID será retornado, já que os dados da sessão antiga serão perdidos e uma nova será criada. 2. Se seu computador possuir mais de um navegador Web (Internet explorer, Mozilla, Chrome etc.), acesse ao mesmo tempo a página info-sessao.jspusando os vários navegadores instalados. Note que o ID da sessão é diferente para cada navegador. Isso acontece porque o ID da sessão é armazenado no navegador, então, cada um deles receberá um ID diferente. Dessa forma, cada navegador será interpretado como um usuário diferente, apesar de ser a mesma pessoa e o mesmo computador acessando o servidor. Scriptlets Vamos agora a um outro exemplo de página JSP. Vamos implementar uma página que mostra quantas vezes ela já foi acessada. Para isso, vamos fazer uso da classe Contador, como mostrado na Listagem 4. Ela possui um método estático chamado novoAcesso(), o qual incrementa a contagem (atributo contagem) de acessos realizados àquele método. Já o método getQuantidadeAcessos() retorna a quantidade de acessos realizadas até o momento. O uso da palavra-chave static faz com que o atributo e métodos da classe Contador possam ser utilizados sem serem criados objetos, você está lembrado? Qualquer dúvida, consulte novamente o material da disciplina de Programação Orientada a Objetos. 01 package aula05; 02 03 public class Contador { 04 private static int contagem; 05 06 public static void novoAcesso() { 07 contagem = contagem + 1; 08 } 09 10 public static int getQuantidadeAcessos() { 11 return contagem; 12 } 13 } Listagem 4 – Classe Contador usada para armazenar a quantidade de acessos Para usar essa classe em um arquivo JSP, precisamos fazer com que ele execute o método novoAcesso() e, em seguida, execute e apresente o conteúdo retornado pelo método getQuandiadeAcessos(). Isso é feito pelo arquivo acessos.jsp, mostrado na Listagem 5. 01 02 03 <% 04 aula05.Contador.novoAcesso(); 05 %> 06 Quantidade de acessos a essa página: 07 08 <%= aula05.Contador.getQuantidadeAcessos() %> 09 10 11 Listagem 5 – Código-fonte do arquivo acessos.jsp Para executar o método novoAcesso(), fazemos uso de scriptlets. Os scriptlets são blocos de código Java delimitados pelos marcadores <% e %>. No exemplo da Listagem 5, o scriptlet se refere às linhas 3 a 5. Dentre desse bloco de código, pode existir qualquer comando Java válido. Isso porque, como você se lembra, o arquivo JSP será traduzido para um Servlet. O que estiver entre o <% e %> será simplesmente copiado para o método do Servlet responsável pela criação da página de resposta (jsp_service()). Voltando para o exemplo, quando o JSP for executado (na verdade, seu Servlet gerado), o métodonovoAcesso() também será executado, incrementando em uma unidade a quantidade de acessos armazenada pela classe Contador (atributo contagem). Por fim, o valor dessa contagem é apresentada na página de resposta através da linha 8. O resultado de vários acessos a esse arquivo JSP é mostrado na Figura 3. Figura 3 – Acessos sequenciais ao arquivo acessos.jsp 1. Implemente e execute o arquivo acessos.jsp e a classe Contador. Observe se sua implementação tem o comportamento mostrado na Figura 2. 2. Altere o exemplo mostrado para apresentar não só a quantidade de acessos, mas a data e a hora do primeiro e do último acesso. E com isso chegamos ao fim da nossa aula. Na próxima aula, continuaremos com mais recursos que podem ser utilizados nas páginas JSP. Não perca! Leitura complementar Para complementar seu entendimento em arquivos JSP, faça uma consulta ao site: • . Leia pelo menos cinco delas e relacione os links das apresentações que você achou mais interessante, em ordem de preferência. Resumo Nesta aula, você aprendeu o que são arquivos JSP e como funciona seu ciclo de vida. Você viu que essa tecnologia é baseada nos Servlets e que, arquivos JSP, ao serem executados, são traduzidos para Servlets e depois compilados e executados. Para a escrita dos arquivos JSP, utilizamos a sintaxe da linguagem HTML, já conhecida por você a um bom tempo, e marcadores especiais traduzidos pelo JSP. Vimos também exemplos dos marcadores especiais: a diretiva <%@page >, usada para dar informações sobre a página JSP, os scriptlets (<% e %>) e as expressões (<%= e %>). 1. Descreva o funcionamento da solução JSP, em especial, o ciclo de vida dos arquivos JSP. 2. Descreva os recursos que você aprendeu para a criação dos arquivos JSP, como o uso de diretivas, scriptlets e variáveis implícitas (out, session, etc.). Desenvolvimento Web Aula 6 – Recursos de JavaServer Pages Olá, caro aluno, na aula de hoje daremos continuidade ao assunto JavaServer Pages! Na aula passada, você viu a motivação para utilizarmos essa tecnologia e seus conceitos básicos. Nesta aula, você aprenderá a utilizar mais recursos que podem ser utilizados nos arquivos JSP. Bons estudos! Ao final desta aula, você deverá: • entender como variáveis e métodos podem ser declarados em um arquivo JSP; • entender como são feitos os comentários em arquivos JSP; • entender como arquivos JSP podem reusar código de outros arquivos JSP. Declaração de variáveis Na aula passada, você viu a implementação de uma página JSP (acessos.jsp) para contagem da quantidade de acessos realizados. Para implementar essa página, foi utilizada a classe Contador, bem como seus métodos estáticos novoAcesso() e getQuantidadeAcessos(). Reveja esse código, caso não se lembre dele. Vamos tentar agora implementar essa mesma funcionalidade, sem fazer uso da classe auxiliar Contador. Você já aprendeu que os marcadores <% e %> são utilizados para delimitar scriptlets, ou seja, código Java embutido nos arquivos JSP. Sendo assim, será que podemos utilizar scriptlets para declarar uma variável no próprio arquivo JSP e utilizá-la como contador? Veja o código JSP a seguir. 01 02 03 04 05 06 07 08 09 10 11 <% int cont = 1; %> Este é o acesso de número: <%= cont %> <% cont = cont + 1; %> Nesse código, temos a declaração de uma variável de nome cont, sua inicialização com o valor 1. Seu valor é impresso indicando a quantidade de acessos já realizado (inicialmente igual a 1) e depois o seu valor é incrementado em uma unidade, indicando que o próximo valor a ser impresso deve ser 2 (segundo acesso). Ao rodar esse código, a tela da Figura 1 é apresentada, indicando que o código está correto. Entretanto, os acessos posteriores também apresentarão o número 1, como se fosse o primeiro acesso! Você consegue perceber qual o problema do código mostrado? Figura 1 – Página JSP mostrando primeiro acesso Vamos observar um possível código de Servlet gerado para esse JSP: 01 02 03 04 05 06 07 08 09 10 11 12 13 14 public class contadorAcessos_jsp extends HttpJspBase { public void _jspService(HttpServletRequest request, HttpServletResponse response) throws java.io.IOException, ServletException { PrintWriter out = response.getWriter(); response.setContentType("text/html"); out.write(""); int cont = 1; out.write("Este é o acesso de número: "); out.print(cont); cont = cont + 1; out.write(""); } } Quando o arquivo JSP foi compilado para um Servlet, a variável cont virou uma variável local ao método _jspService(). Sendo uma variável local, seu valor final é perdido no final da execução do método! Então, o que nós precisamos para resolver nosso problema é fazer com que essa variável cont seja uma variável de instância (atributo do Servlet) e não simplesmente uma variável local. Isso pode ser feito através de um elemento JSP chamado de declaração. Uma declaração é delimitada pelos marcadores <%! e %> e contém a declaração de uma variável. Na tradução do JSP para o Servlet, essa variável será traduzida para um atributo da classe gerada. Vejamos agora como deve ficar a declaração da variável cont: <%! int cont = 1; %> E vejamos também como fica o Servlet gerado para esse JSP: 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 public class contadorAcessos_jsp extends HttpJspBase { int cont = 1; public void _jspService(HttpServletRequest request, HttpServletResponse response) throws java.io.IOException, ServletException { PrintWriter out = response.getWriter(); response.setContentType("text/html"); out.write(""); out.write("Este é o acesso de número: "); out.print(cont); cont = cont + 1; out.write(""); } } Observe que agora a variável cont virou um atributo da classe, ou seja, uma variável de instância! Se acessarmos a primeira vez o JSP, o conteúdo da Figura 1 será mostrado. Entretanto, ao acessarmos a página pela segunda vez, a tela da Figura 2 será apresentada. Figura 2 – Página JSP mostrando segundo acesso 1. Implemente a página JSP de contagem de acesso das duas formas mostradas na aula: declarando uma variável em um scriptlet e declarando a variável contadora com a instrução <%! %>. 2. Altere a página JSP para mostrar a data e a hora do primeiro e do último acesso. Vamos agora pensar em uma contagem que leva em consideração a hora de acesso. Por exemplo, em caso de acessos em horários tarde da noite ou de madrugada, são considerados como 2 acessos. Os acessos realizados durante o restante do horário são considerados como apenas 1 acesso. Como você imagina que poderíamos implementar isso? Bom, além de declarar variáveis, você pode também declarar operações (métodos) em um arquivo JSP. A seguir, podemos ver o arquivo JSP com a declaração do método calculaContagem(), responsável por calcular a contagem de acesso baseada na hora atual. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 <%@ page import="java.util.Calendar" %> <%! int cont = 0; int calculaContagem() { int valor = 1; int hora = Calendar.getInstance().get(Calendar.HOUR_OF_DAY); if (hora >= 22 || hora <= 6) { valor = 2; } return valor; } %> <% cont = cont + calculaContagem(); %> Este é o acesso de número: <%= cont %>*
* Considerando que acessos noturnos valem por 2. Observe que, além da declaração da variável cont, temos também o método calculaContagem(), que retorna 2 apenas quando o acesso é feito entre às 22h e às 6h da manhã. O código JSP começa pela declaração de import da classe java.util.Calendar, utilizada para retornar a hora atual (Calendar.getInstance().get(Calendar.HOUR_OF_DAY)). O método getInstance() retorna uma referência para o objeto Calendar que representa a data/hora atual. Já o método get() retorna uma propriedade da data/hora, como a hora do dia (hora atual, entre 0h e 24h). 1. Implemente a página JSP de contagem de acesso que depende da hora de acesso, conforme mostrado na aula. Ajuste o período de contagem dupla (entre 22h e 6h da manhã), se necessário, para não precisar entrar pela madrugada para poder ver a execução da contagem dupla! 2. Altere o JSP para que um acesso em final de semana valha por 3 acessos durante o dia em dias normais. Observe a API da classe Calendar para fazer isso. Em HTML, você sabe que comentários podem ser escritos, desde que delimitados entre . Em JSP, comentários podem ser feitos tanto usando os marcadores HTML de comentários como também usando marcadores específicos: <%-- e --%>. Em ambos os casos, o código escrito não irá afetar a tela de resposta do arquivo JSP, mas existe uma diferença sutil. Comentário usando os marcadores JSP não são colocados na página HTML de resposta! Para entender isso, veja o seguinte exemplo: 01 02 03 04 05 06 07 Olá! <%-- já esse comentário não vai --%> Esse código JSP, gera a página mostrada na Figura 3. Se você observar o código-fonte da página recebida pelo navegador Web, vai visualizar apenas o seguinte: 01 02 03 04 05 06 Olá! Note que, no lugar do comentário JSP (<%-- -->), apenas uma linha em branco é mostrada. O comentário foi suprimido. Esse comportamento é interessante, caso queiramos colocar comentários no código do JSP, como, por exemplo, “esta funcionalidade não está robusta, ocorre um erro se informarmos determinado valor como parâmetro” etc. Imagine um cliente acessar uma página, se for curioso, ver o código-fonte das páginas e observar esse tipo de mensagens! Ou então um hacker, que pode explorar esse tipo de informação para derrubar o sistema! Figura 3 – Página de Olá com comentários HTML e JSP 1. Altere a página JSP de contagem de acesso que depende da hora de acesso, adicionando comentários em HTML e em JSP. Observe quais comentários aparecem no código-fonte da página HTML enviada para o navegador Web. Inclusão de arquivos Uma outra característica interessante de JSP é a capacidade de modularizar, ou seja, dividir o conteúdo de seus arquivos em partes menores que depois serão compostas resultando nas páginas do sistema. Vejamos o seguinte exemplo de página JSP: <%@ page import="java.util.*" %> <%= (new java.util.Date()).toLocaleString() %> Esse código é responsável por criar um objeto Date que representa a data/hora atual, transformá-lo em um a String com o formato local e apresentá-lo no HTML gerado. A página resultante da execução desse código é mostrada na Figura 4. Figura 4 – Arquivo dataAtual.jsp que mostra a data e hora atual Essa informação pode ser útil em várias páginas do sistema. Dessa forma, esse código passa a ser repetido em vários arquivos JSP. Entretanto, para você não ter que duplicar esse código em todos os outros arquivos, você pode importar o conteúdo de dataAtual.jsp para esses outros arquivos. Vamos imaginar um arquivo de boas-vindas, no qual é mostrada uma mensagem de boas-vindas e a data e hora atual. O código desse arquivo JSP é mostrado a seguir, e sua tela resultante é mostrada na Figura 5. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 Bem vindo!

Seja muito bem-vindo!



Data/hora atual: <%@ include file="dataAtual.jsp"%> Figura 5 – Tela de boas-vindas, a qual importa o conteúdo do arquivo dataAtual.jsp Veja que a parte responsável por mostrar a data e a hora do sistema é do código escrito no arquivo dataAtual.jsp. Esse link é feito pela instrução <%@ include file="dataAtual.jsp"%>. Basicamente, ela diz que o conteúdo do arquivo dataAtual.jsp deve ser copiado para o Servlet gerado para o arquivo bemVindo.jsp. Note que é como se o compilador editasse o arquivo do Servlet gerado para incluir as instruções contidas no arquivo dataAtual.jsp. 1. Implementar e executar os arquivos bemVindo.jsp e dataAtual.jsp, conforme apresentado. 2. Criar um arquivo 8ndereço.jsp contendo o endereço de uma empresa. Alterar o arquivo bemVindo.jsp para mostrar também o conteúdo do arquivo 8ndereço.jsp, o qual também poderá ser incluído em diversos outros arquivos. Uma outra forma de montar o conteúdo de uma tela baseado no conteúdo de outras é através da diretiva . Essa diretiva faz a inclusão tanto estática como dinâmica de outros arquivos. Inclusão estática é aquela feita também pela instrução <%@ include >, ou seja, o código do arquivo importado é colocado no Servlet gerado para o arquivo que está fazendo a inclusão. Já a inclusão dinâmica é diferente. Na hora em que o arquivo que faz a inclusão é executado, ele executa o arquivo incluído e seu resultado (código HTML) é colocado dentro da resposta final a ser montada. Imagine o seguinte código para um arquivo JSP chamado de dadosUsuario.jsp. Ele apresenta uma mensagem de identificação e saudação baseada no parâmetro “usuario”. Se executarmos esse arquivo, teremos o resultado mostrado na Figura 6. Como nenhum valor é passado para o parâmetro “usuario”, o valor null aparece na tela de resposta. Sr. <%= request.getParameter("usuario") %>,
É um prazer tê-lo conosco! Figura 6 – Acesso ao arquivo dadosUsuario.jsp sem passagem de parâmetros Você pode, no entanto, criar outros arquivos JSP que queiram mostrar esse conteúdo de identificação e boas-vindas, como, por exemplo, no código a seguir (telaInicialUsuario.jsp). O resultado da execução desse arquivo é mostrado na Figura 7. A página de resposta gerada inclui o conteúdo gerado pelo arquivo dadosUsuario.jsp quando o parâmetro “usuario” é passado com valor igual a Jones. Esse nome, em um programa real, poderia ter sido recuperado de uma base de dados. 01 02 03 04 05 06 07 08 09 10 Tela inicial Figura 7 – Inclusão do arquivo dadosUsuario.jsp com passagem de parâmetro. 1. Implemente o arquivo telaInicialUsuario.jsp conforme mostrado na aula. Execute-o e veja o seu comportamento. 2. Altere o arquivo dadosUsuario.jsp para receber e imprimir na tela um parâmetro relativo ao sobrenome do usuário. Altere o arquivo telaInicialUsuario.jsp para passar o sobrenome do usuário. Execute o código e observe o novo comportamento. Chegamos ao fim da nossa aula! Daremos continuidade ao assunto, na próxima aula, momento no qual você aprenderá a utilizar os Servlets em conjunto com os arquivos JSP. Até lá! Leitura complementar Para complementar a fixação do conhecimento, faça uma breve leitura das aulas anteriores sobre JSP, sua linguagem de expressões e JSTL. Veja também artigos na Internet sobre esse assunto, como por exemplo: JAVASERVER pages: syntax reference. Disponível em: Resumo Nesta aula, você aprendeu que variáveis podem ser declaradas dentro de scriptlets, tornando-se variáveis locais, ou dentro de declarações entre <%! e %>, tornando-se variáveis de instância (atributos). Essa segunda forma de declaração pode ser utilizada também para declarar métodos, os quais farão parte do Servlet gerado pela compilação do arquivo JSP. Além disso, você aprendeu a utilizar comentários dentro de arquivos JSP, os quais podem ser iguais aos comentários HTML ou podem utilizar marcadores específicos, evitando que os comentários sejam enviados para o navegador Web. Por fim, você viu o uso do <%@ include > e do , marcadores especiais utilizados para incluir em um arquivo JSP o conteúdo de outros arquivos, promovendo assim o reuso de código entre esses arquivos. Descreva como você: 1. declara variáveis e métodos em um arquivo JSP; 2. pode comentar código em arquivos JSP; 3. pode reusar código de outros arquivos JSP. Desenvolvimento Web Aula 7 – MVC, Servlets e JavaServer Pages No início desta disciplina você utilizou Servlets no desenvolvimento de pequenos programas Web. Em seguida, você viu que o código HTML embutido dentro dos Servlets dificultava o entendimento e manutenção desses arquivos. Para minimizar esse problema, você estudou o uso de JSP. Entretanto, agora corremos o risco de ter o problema inverso, o de ter arquivos JSP sobrecarregados com comandos Java! Por isso, a aula de hoje apresenta uma forma de minimizar o problema citado. Tenha uma boa leitura! Após essa aula, você será capaz de: • Aplicar o conceito de arquitetura MVC (Model/View/Controler) utilizando Servlets e JSP. Separação em camadas Assim como os Servlets não devem ter código HTML embutido, os arquivos JSP não devem ter código Java – pelo menos não em excesso. Então, como fazer com que estruturemos nosso código de forma a não sobrecarregar nem os Servlets com código HTML, nem os arquivos JSP com código Java? Existem várias formas de se fazer isso e você irá estudar aqui uma das mais simples, porém bastante utilizada, chamada de arquitetura MVC. A arquitetura MVC (Model – View – Controller) é uma arquitetura que realiza a separação do código da aplicação em: • Controlador • Componente de software responsável por receber os comandos do usuário e acionar as ações corretas a serem realizadas no sistema, incluindo a seleção de qual componente de apresentação da resposta deverá utilizado. • Modelo • Representa as regras de negócio. Fazem parte dessa camada de software as classes responsáveis por realizar transações de negócio (venda de produtos, cadastro de informações etc.). Essa parte do código é responsável também por se comunicar com o banco de dados ou com outros sistemas existentes. • Visão • Componente de software responsável pela apresentação, ou seja, pela montagem das telas de resposta do sistema. Ele é ativado pelo controlador e recebe informações da camada de modelo. Essa separação do sistema em componentes é ilustrada pela Figura 1. Figura 1 – Arquitetura MVC Fonte: . Acesso em: 10 out. 2010. Com relação à programação Web, o usuário, ao acessar uma URL, aciona o controlador (Servlet), o qual é responsável por realizar a mudança no modelo de negócio (classes Java de negócio) e direcionar para o componente de apresentação correto (arquivo JSP). Vamos entender isso através de um exemplo simples. Considere que você é uma pessoa interessada em registrar todos os encontros com seus colegas. Vamos então desenvolver um sistema para registrar esses encontros. Esse sistema é bem simples e será mostrado nas seções a seguintes, de acordo com o padrão MVC. Camada de modelo A modelagem do sistema começa a modelagem de uma classe com as informações de cada um de seus encontros. Segue uma possível implementação para essa classe: 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 40 41 42 43 package aula07; import java.util.Date; public class Encontro { private String nomePessoa; private String local; private Date data; private String motivo; public String getNomePessoa() { return nomePessoa; } public void setNomePessoa(String nomePessoa) { this.nomePessoa = nomePessoa; } public String getLocal() { return local; } public void setLocal(String local) { this.local = local; } public Date getData() { return data; } public void setData(Date data) { this.data = data; } public String getMotivo() { return motivo; } public void setMotivo(String motivo) { this.motivo = motivo; } } Note os atributos dessa classe: nome da pessoa com que você se encontrou; local do encontro; data e motivo do encontro. Além disso, precisamos de uma classe para realizar o registro e a consulta dos encontros registrados. Isso será feito pela classe a seguir. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 package aula07; import java.util.ArrayList; import java.util.List; public class SistemaEncontros { private List encontros = new ArrayList(); public void adicionar(Encontro e) { encontros.add(e); } public List listarEncontros() { return encontros; } } Note que a implementação mostrada para o sistema de encontros utiliza uma lista para armazenar em memória os registros de encontros realizados. 1. Implemente as classes mostradas para a camada de modelo (negócio) do sistema de encontro. 2. Adicione a função de remover ao sistema. Camada de controle Para a camada de controle, temos em essência a tecnologia de Servlets. Em primeiro lugar, vamos utilizar uma classe para instanciar e guardar a referência para o sistema de encontros. Isso pode ser feito através da classe mostrada a seguir: 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 package aula07; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class ContextListener implements ServletContextListener { public void contextInitialized(ServletContextEvent event) { ServletContext context = event.getServletContext(); context.setAttribute("sistemaEncontros", new SistemaEncontros()); } public void contextDestroyed(ServletContextEvent event) { ServletContext context = event.getServletContext(); context.removeAttribute("sistemaEncontros"); } } Essa classe herda de uma classe especial chamada ServletContextListener e implementa métodos que vão ser invocados basicamente quando o contêiner dos Servlets for criado e for destruído. Nesses dois momentos, temos a oportunidade de respectivamente adicionar e remover uma referência para o sistema dentro do chamado contexto dos servlets. Para isso, o seguinte trecho de código precisa ser escrito no arquivo web.xml da aplicação: aula07.ContextListener O contexto dos servlets possui atributos, que são objetos mapeados por um nome. No caso, a instância do sistema (objeto criado) será mapeado ao nome “sistemaEncontros”. O próximo passo a ser seguido é a implementação de um servlet controlador, como mostrado no código que é comentado a seguir. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package aula07; import java.io.IOException; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ServletControlador extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String acao = request.getParameter("acao"); SistemaEncontros sistema = (SistemaEncontros) getServletContext() .getAttribute("sistemaEncontros"); String resposta = "index.jsp"; if ("cadastrar".equals(acao)) { resposta = "cadastrar.jsp"; } else if ("confirmarCadastro".equals(acao)) { Encontro e = new Encontro(); e.setNomePessoa(request.getParameter("nomePessoa")); e.setMotivo(request.getParameter("motivo")); e.setLocal(request.getParameter("local")); e.setData(new Date()); sistema.adicionar(e); request.setAttribute("lista", sistema.listarEncontros()); resposta = "listar.jsp"; } else if ("listar".equals(acao)) { request.setAttribute("lista", sistema.listarEncontros()); resposta = "listar.jsp"; } request.getRequestDispatcher(resposta).forward(request, response); } } A classe ServletControlador atende tanto requisições através do método GET como através do método POST. Na implementação do método doPost(), temos o acesso ao parâmetro que representa a ação a ser executada (request.getParameter("acao")) e o acesso ao sistema através do contexto do Servlet (getServletContext().getAttribute("sistemaEncontros")). Em seguida, é trabalho do controlador atuar de acordo com a ação requisitada. No caso, temos quatro casos, cada um representando uma ação diferente: • Valor do parâmetro “acao” é igual a “cadastrar” • Deve-se montar a tela de cadastro de encontros. Para isso, basta redirecionar a requisição para o arquivo cadastro.jsp. Esse arquivo será apresentado na próxima seção. O comando request.getRequestDispatcher(resposta).forward(request, response) é responsável por fazer com que a página de resposta seja montada pela página JSP indicada na variável de nome resposta. • Valor do parâmetro “acao” é igual a “confirmarCadastro” • Uma vez preenchido o formulário de cadastro, o mesmo será submetido para o servlet controlador, o qual pegará as informações do encontro através das chamadas ao método request.getParameter(), construirá o objeto encontro e o adicionará no sistema. • Em seguida, a lista de encontros registrados é colocada como atributo da requisição request.setAttribute(). Essa informação, como veremos, será acessada pelo arquivo JSP a ser executado (listar.jsp). • Valor do parâmetro “acao” é igual a “listar” • Nesse caso, o arquivo JSP a ser executado é o listar.jsp e a lista contendo os encontros registrados é passada como atributo da requisição. • Valor do parâmetro “acao” é diferente dos casos citados • A página index.jsp será apresentada (valor inicial da variável resposta). Lembrando que todo Servlet tem que estar referenciado no arquivo web.xml. Então, teremos o seguinte código nesse arquivo: ServletControlador ServletControlador aula07.ServletControlador ServletControlador /ServletControlador 1. Implemente e configure a aplicação de acordo com o mostrado para a camada de controle. 2. Altere o Servlet controlador para dar suporte à operação de remover encontro. Camada de apresentação Para a camada de apresentação da arquitetura MVC, temos os arquivos JSP. O primeiro deles é o index.jsp, cujo código é mostrado abaixo e cuja tela é vista na Figura 2. Note os links do menu utilizando o ServletControlador e o parâmetro ação para indicar a ação a ser realizada. 01 02 03 04 05 06 07 08 09 10 11 Olá Seja bem vindo a esse sistema! Selecione a operação desejada:
1. Registrar um encontro.
2. Listar meus encontros.
Figura 2 – Tela gerada pelo arquivo index.jsp A tela de registro de encontros é gerada pelo arquivo cadastrar.jsp, cujo código-fonte é mostrado a seguir. Note que ele é, na verdade, um arquivo puramente HTML. A tela gerada para esse arquivo é mostrada na Figura 3. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 Cadastrar novo encontro

Cadastrar novo encontro:

Nome:
Local:
Motivo:



Voltar ao menu Figura 3 – Tela gerada pelo arquivo cadastrar.jsp Por fim, temos a seguir o código-fonte do arquivo listar.jsp, responsável por mostrar a lista de encontros registrados. Note o uso das diretivas de import logo no início do arquivo. Também temos o uso de um scriptlet que envolve o comando for para percorrer a lista de encontros armazenada como atributo da requisição com o nome “lista” (ver também esse nome no código do Servlet controlador). Nesse caso, o comando for vai imprimir na tela da Web o conteúdo HTML que está entre suas chaves { e }. Esse conteúdo é, basicamente, a informação de cada encontro cadastrado. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <%@ page import="java.util.List" %> <%@ page import="aula07.Encontro" %> Lista de encontros

Lista de encontros:

<% List lista = (List) request.getAttribute("lista"); for (Encontro encontro : lista) { %> <% } %>
Nome Local Motivo Data
<%= encontro.getNomePessoa() %> <%= encontro.getLocal() %> <%= encontro.getMotivo() %> <%= encontro.getData().toLocaleString() %>


Voltar ao menu 1. Implemente a aplicação de acordo com o mostrado para a camada de apresentação. Finalmente, execute o sistema e verifique se o seu comportamento está correto. 2. Adicione o arquivo JSP necessário para dar suporte à operação de remover encontro. Chegamos ao final da nossa Aula 7, mas nas próximas aulas, você voltará a estudar recursos de JSP que ajudam no desenvolvimento para Web. Serão apresentados recursos que, inclusive, aumentam a produtividade de desenvolvimento. Até lá. Leitura complementar Realize uma busca na Internet pela arquitetura MVC e faça uma leitura das primeiras páginas encontradas sobre o assunto. Faça também a leitura sobre os marcadores JSP aprendidos hoje através da seguinte URL: • Resumo Nessa aula, você estudou o padrão arquitetural chamado MVC, responsável por separar a lógica de negócio do controle do fluxo de execução e do modelo de negócio (regras de negócio). Essa arquitetura é muito importante para que o código de sua aplicação fiquem bem organizados. Na verdade, existem diversas bibliotecas de desenvolvimento para Web que seguem essa arquitetura. Você viu que essa arquitetura se divide em três camadas: de controle, modelo e visão. Por fim, você estudou as camadas de modelo, controle e de apresentação mais detalhadamente, observando que a primeira representa a lógica do negócio, a segunda representa o fluxo de controle da aplicação e a última é responsável pelo código de apresentação do sistema (telas de entrada e saída). 1. Descreva como é possível aplicar o conceito de arquitetura MVC (Model/View/Controler) utilizando Servlets e JSP. Desenvolvimento Web Aula 8 – Introdução ao JSTL Olá, caro aluno, você está gostando do assunto? Espero que sim. Lembre-se: qualquer dúvida, não hesite em reler as aulas anteriores e utilizar outros recursos, como os meios de comunicação com os tutores. Até agora você já aprendeu a usar scriptlets e diretivas dentro dos arquivos JSP. Esses recursos são muito úteis, mas podem “poluir” o código fonte dos arquivos JSP. Nesta aula veremos como o uso de tag libraries padrão podem nos ajudar a escrever um código mais limpo e de fácil entendimento. Além disso, o uso de tag libraries permite que a mesma lógica seja reusada em diversas páginas com a vantagem de um único ponto de manutenção e extensão. Nesta aula e nas próximas veremos as principais tag libraries que são padrão JSP. Estas bibliotecas são: core, XML, internacionalização, SQL e funções. Nesta disciplina, focaremos nas bibliotecas core, internacionalização e funções, bem como teremos uma aula de EL (Expression Language), que nos ajudará a entender como obter os objetos dentro de uma páginas JSP em diferentes escopos (requisição, sessão, aplicação etc.) de forma elegante e padronizada. Ao ler esta aula, você estará apto a: • Entender uma nova abordagem de construção de páginas sem o uso de scriptlets e diretivas JSP. • Produzir páginas JSP mais fáceis de ler e manter usando JSTL (JSPStandard Tag Library). O que é uma tag Tag é uma tecnologia para o ambiente JSP. Um desenvolvedor pode usar código Java para criar um pequeno componente reutilizável que pode ser colocado na página JSP para executar alguma tarefa no lado do servidor. Por exemplo, muitas páginas de um site que precisem exibir o nome do usuário que está “logado” no sistema. Um desenvolvedor Java pode criar uma tag que identifica o nome do usuário na sessão da aplicação e o exibe. Essa tag pode ser colocada em cada JSP que dela necessite. Se além do nome do usuário “logado” nós quisermos exibir alguma outra informação, não será necessário alterar código em todos os JSPs para atingir este objetivo, basta alterar o código da tag para que esta nova informação automaticamente esteja presente em todas as páginas JSPs que usam essa tag. Reutilizar código é possível de várias maneiras diferentes, mas o benefício de tags é que a sintaxe de uso é semelhante ao uso de tags HTML. Uma vez que a sintaxe é natural para os desenvolvedores HTML, as tarefas podem ser divididas de uma forma que permite aos desenvolvedores concentrar seus objetivos em regras de negócio e deixar a apresentação para os designers. O que é uma tag library Uma biblioteca de tags é um conjunto de tags úteis empacotadas e distribuídas em conjunto, que ajudam os desenvolvedores de aplicativos web a realizar tarefas afins. O Java Standard Tag Library, ou JSTL, é uma Tag Library útil que muitos desenvolvedores usam para implementar a funcionalidade sem recorrer ascriptlets dentro do arquivo JSP. JSTL inclui tarefas comuns como a execução condicional, loops e internacionalização, exibir valores de variáveis armazenadas em diferentes escopos da aplicação. Embora essas tarefas possam ser feitas usando scriptlets (ver Listagem 1), JSP com tags é considerado mais fácil de manter do que os arquivos JSP com o código scriptlet (ver Listagem 2). 01 02 03 04 05 06 07 08 09 10 11 12 13 <% String[] filmes = (String[]) request.getAttribute("filmes"); String filme=null; for (int i = 0; i < filmes.length; i++) { filme = filmes[i]; %> <% } %>
<%= filme %>
Listagem 1 – Código JSP que usa scriptlets 01 02 03 04 05 06 07 08 09 10 11 12 13 <%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %> Lista de filmes:

${filme}
Listagem 2 – Código JSP equivalente que usa JSTL A biblioteca JSTL JSTL encapsula funcionalidades comuns a muitas aplicações JSP. Ao invés de misturar tags de vários fornecedores em suas aplicações JSP, JSTL lhe permite empregar um conjunto padrão de tags. Essa padronização permite que você use seus aplicativos em qualquer contêiner JSP que suporte JSTL. As funcionalidades da biblioteca padrão JSTL são: Área Subfunções Prefixo Core Suporte a variáveis c Fluxo de controle Gerenciamento de URLs Miscelâneas XML Core x Fluxo de controle Transformação I18N Localidade fmt Formatação de mensagens Formatação de números e datas Database SQL sql Funções Tamanho de coleções fn Manipulação de strings Quadro 1 – Funcionalidades da biblioteca padrão JSTL Para este curso abordaremos apenas as áreas core, I18N e funções. Deixando a teoria um pouco de lado, vamos agora mostrar quais são essas bibliotecas padrão do JSTL e quais funcionalidades elas fornecem. A biblioteca core A biblioteca core implementa funcionalidades de suporte a variáveis, controle de fluxo, gerenciamento de URLs e miscelâneas. Você verá agora com mais detalhes cada uma dessas funcionalidades. Área Função Tags Prefixo Core Suporte a variáveis remove set c Controle de fluxo choose when otherwise forEach if Gerenciamento de URLs import param redirect param url param Miscelâneas catch out Quadro 2 – Funcionalidades detalhadas da biblioteca core Começaremos mostrando as duas funcionalidades gerais da biblioteca core, que são e . Cada uma delas possui atributos que descrevem como será o seu comportamento. A tag c:out exibe os valores armazenados nas variáveis de escopo na página. O escopo pode ser a própria página, o request, a sessão e a aplicação. Assim, o código irá procurar por um objeto chamado var no escopo da página. Caso não encontre, a procura será feita no request,e caso não encontre, será a vez da sessão até chegar no escopo da aplicação. Caso a variável não esteja em nenhum desses escopos, nada será exibido. Um detalhe sobre esta tag é que se o valor da variável for null uma string vazia será exibida, ou seja, não precisamos nos preocupar com NullPointerException. Uma vez que explicamos a funcionalidade desta tag, vamos agora mostrar e explicar cada um dos seus atributos. Atributo Descrição Obrigatório Valor default value Valor a ser avaliado e exibido Sim default Valor que será exibido caso o value seja vazio ou nulo Não Body escapeXml Escapa alguns caracteres que são marcadores HTML Não true Quadro 3 – Atributos da tag c:out O atributo value é uma expressão (veremos com mais detalhes em uma próxima aula que expressões são essas e quais variáveis estão implícitas para serem usadas nessas expressões) que é avaliada no servidor na hora que a página JSP está sendo processada. Você viu também que existe uma ordem de avaliação nessa expressão com relação a onde essas variáveis serão procuradas primeiro (página,request, sessão e aplicação). Viu também que caso o resultado desta avaliação seja null,uma string vazia será retornada. Uma vez que podemos ter como resultado da avaliação do atributo value uma string vazia, esta tagdefine um atributo da tag, no caso default, cujo valor pode ser usado neste caso. Existe duas formas de se usar um valor default. A primeira e mais óbvia é usar o atributo default da tag (ver Listagem 3), e a outra é não especificar este atributo e deixar este valor no corpo da tag (ver Listagem 4). 01 Hello . Listagem 3 – Código JSP que usa o atributo default para exibir “guest” se ${user} for vazio 01 Hello Guest. Listagem 4 – Código JSP que usa o body da tag c:out para exibir “guest” se ${user} for vazio O terceiro atributo, o escapeXml, é usado para escapar caracteres que são marcadores HTML. Quando uma expressão possui um destes caracteres, o valor retornado pela tag não pode ser o próprio caractere, e sim a sua sequência de escape. Por exemplo, o caractere aspas duplas é escapado para ". Isto evita que se a tag for usada dentro de uma marcação HTML, o resultado da avaliação da expressão não quebre o HTML gerado. Veja o exemplo a seguir: Figura 1 – exemplo de código (e tela) usando escapeXml Perceba que a palavra você não apareceu na caixa de texto, porque de acordo com o código acima esta palavra está entre aspas duplas. Perceba também que no código JSP do exemplo a tag c:out não está escapando o caractere que representa as aspas duplas. 1. Um dos caracteres que deve ser escapado quando renderizado pela tag c:out já foi visto. Quais outros caracteres devem ser escapados? Dica: são mais quatro. Tente buscar essa informação na Internet. 2. Altere o exemplo acima para que a tag c:out escape as aspas duplas. A próxima tag de uso geral é c:catch. Esta tag permite que caso ocorra uma exceção dentro do bloco encapsulado por ela, esta possa ser tratada. No exemplo abaixo podemos ver que caso ocorra alguma exceção dentro do corpo da tag c:catch esta fará um teste na variável definida no atributo var, e caso este atributo não seja vazio ou nulo a mensagem de erro será impressa. Figura 2 – Código-fonte de arquivo JSP com tratamento de exceção usando o tag c:catch Uma vez que explicamos a funcionalidade desta tag, vamos agora mostrar e explicar cada um dos seus atributos. Atributo Descrição Obrigatório Valor default var Nome da variável que armazenará a exceção lançada, se ocorrer. Esta variável será do tipo Java.lang.Throwable Não Quadro 4 – Atributos da tag c:catch Podemos observar quão simples e útil é esta tag, cujo único atributo é o nome da variável que será armazenada no escopo da página como o nome definido pelo atributo var. 1. Altere o exemplo acima para imprimir em caso de erro a mensagem da exceção ocorrida ao invés de “Não foi possível exibir a lista de filmes”. O próximo conjunto de funcionalidades da taglib c será o suporte a variáveis. Com este conjunto de tagspodemos definir e remover variáveis dentro do escopo da requisição JSP que está sendo processada. Existem duas tags que são responsáveis por esse suporte, que são a tag e . Começaremos explicando a tag c:set. A tag c:set permite que sejam definidas variáveis que não existiam no escopo da requisição JSP à medida que esta página é processada. Esta tag tem o comportamento similar à diretiva , entretanto ela pode definir variáveis em JavaBeans quanto em qualquer escopo da página JSP (página, requisição, sessão e aplicação). Existem duas formas de informar à tag qual o valor que ela deve armazenar. A primeira é usando o atributo value. A segunda é definindo o corpo desta tag. Ambas as formas são mostradas nas Listagens 5 e 6. 01 Listagem 5 – Código JSP que usa o atributo value para definir o valor da variável userLevel 01 admin Listagem 6 – Código JSP que usa o body da tag como valor para a variável userLevel Adicionalmente, o atributo value pode ser uma expressão assim como na tag c:out. Entretanto, se a avaliação dessa expressão resultar num valor nulo a variável será removida do escopo. Uma vez que explicamos a funcionalidade desta tag, vamos agora mostrar e explicar cada um dos seus atributos. Atributo Descrição Obrigatório Valor default value Valor ou expressão a ser armazenado na variável definida pelo atributo var Não Body target Nome da variável que será modificada Não property Nome da propriedade do objeto definido pelo atributo target que será modificado Não var Nome da variável onde será armazenado o valor definido pelo atributo value Não scope Escopo da variável (Página, Requisição, Sessão ou Aplicação) Não Quadro 5 – Atributos da tag c:set Aqui precisamos diferenciar dois atributos, que são var e target. O atributo var é para definir variáveis no escopo da página, requisição, sessão e aplicação, enquanto que o atributo target define valores para propriedades em beans que estejam definidos em algum escopo. Quando usarmos var não podemos usar target, e vice-versa. Para ficar mais claro, veja os exemplos a seguir. Definimos uma classe Pessoa que possui os atributos id e nome. Figura 3 – Código da classe Pessoa Alteramos o servlet de exemplo para listar pessoas ao invés de filmes. Figura 4 – Método doGet() do Servlet listando pessoas Neste JSP definimos uma variável com o nome de id cujo valor será o id + 1 e, logo em seguida, este id será exibido na página juntamente com o nome da pessoa. Como estamos dentro de um loop, a cada iteração este id será incrementado e o novo valor atribuído à variável id. Figura 5 – Código JSP responsável por gerar o HTML com a lista de pessoas Por fim, vamos alterar diretamente o atributo id da classe pessoa dentro de cada iteração. Para isso precisamos definir qual o objeto terá a sua propriedade modificada. Neste exemplo eu usei um recurso da taglib de loop, que veremos mais adiante, que guarda em uma variável o contador da iteração, porém a lógica é a mesma. Perceba que para cada instância de pessoa que está sendo iterada o valor do seu atributo id está sendo modificado com o contador da iteração + 1. Aqui vale ressaltar uma coisa. O atributo target recebe uma expressão que deve ser avaliada para um objeto; caso eu usasse apenas “pessoa” ao invés de “${pessoa}” a tag entenderia “pessoa” como uma string e tentaria definir o valor de status + 1 numa propriedade id que não existe na classe String. Logo, isto causaria um erro em tempo de execução, ou seja, para o atributo target devemos usar uma expressão que avalie para um objeto, e não apenas o identificador do objeto no escopo. Figura 6 – Código JSP alterado, responsável por gerar o HTML com a lista de pessoas Por último, o atributo scope define em qual escopo a variável será armazenada, ou seja, no escopo da página, requisição, sessão ou aplicação. Quando este atributo não é definido, o escopo utilizado pela tagé o da página. 1. Experimente agora alterar dentro do JSP o atributo nome da classe Pessoa usando var e target. Para encerrarmos a aula de hoje, veremos a tag c:remove. Esta tag permite remover do escopo as variáveis que não mais serão utilizadas. Para isso, basta informar o nome da variável e o escopo onde ela se encontra. Aqui, ao contrário da tag c:set, que se o escopo não for definido o default é a página, se o escopo não for definido a tag procura pela variável em todos os escopos. Uma vez que explicamos a funcionalidade desta tag vamos agora mostrar e explicar cada um dos seus atributos. Atributo Descrição Obrigatório Valor default var Nome da variável a ser removida Sim scope Escopo da variável (Página, Requisição, Sessão ou Aplicação) Não Todos os escopos Quadro 6 – Atributos da tag c:remove Então, pessoal, mais simples impossível, não é?! Leitura complementar Para complementar seu aprendizado, olhe o assunto visto nas seguintes referências: • Resumo Hoje você viu alguns conceitos sobre taglibs e por que elas permitem um código JSP mais fácil de entender e manter. Viu também que existe um conjunto de taglibs padrão e quais são as suas funcionalidades. Viu um pedaço de uma dessas tags, que é a tag core, e as suas funcionalidades. Na próxima aula, você verá a tag core no tocante a gerenciamento de URLs e controles de fluxo. 1. Descreva sucintamente as vantagens de se usar as taglibspara implementar páginas JSP. 2. Descreva, se for o caso, situações que você tenha se deparado e que você percebeu que poderia ter resolvido de forma mais elegante usando algumas das tags que vimos hoje. Desenvolvimento Web Aula 9 – Detalhando a biblioteca JSTL core Olá caro aluno! Hoje continuaremos com as nossas aulas falando mais um pouco sobre JSTL, mais especificamente sobre a taglib core com as tags de gerenciamento de URLs e controle de fluxo. Essas duas taglibs são bastante úteis, não só simplificando o código do arquivo JSP, como também evitando erros de programação. Uma boa leitura! Ao ler esta aula, você estará apto a: • utilizar os recursos da taglib core para gerenciamento de URLs; • utilizar os recursos da taglib core para controle de fluxo. A taglib core Você viu na aula passada as tags de suporte a variáveis e miscelâneas. Hoje, continuaremos os estudos mostrando as tags da taglib core que tratam de gerenciamento de URLs e controle de fluxo. Começaremos então pelo gerenciamento de URLs. Existem três tags básicas para o gerenciamento de URLs. Essas tags são c:url, c:import e c:redirect. Essas três tags podem fazer uso de uma outra tag que permite a passagem de parâmetros para as funcionalidades de gerenciamento. Essa tag é c:param. A tag c:url constrói uma URL aplicando as regras de re-escrita de URLs apropriadas. E o que seria isso? Re-escrever uma URL significa escrever a URL de uma forma mais elegante, curta e amigável para os engenhos de busca. Assim, a listagem 1 representa o uso da tag c:url da taglib core. 01 02 03 04 05 06 Edit Profile Listagem 1 – Código JSP que usa funcionalidade de re-escrita de URL Perceba que na linha 01 estamos indicando que o valor definido no atributo value será transformado em uma URL a partir da URL base. Como assim? Digamos que você está acessando o JSP . Então, a URL gerada será algo como . Perceba também que na linha 02, dentro da tag c:url, utilizei a tag c:param para passar os parâmetros. Aqui, a informação mais importante com relação aos parâmetros é que eles são passados via query string pela URL, porém, alguns caracteres não são permitidos em uma query string. Dessa forma, é necessário que esses caracteres sejam codificados, o que NÃO é feito pela tag c:url. Espaços em branco, &, ?, etc. devem ser codificados antes de serem utilizados como parâmetros nas URLs. E como isso é feito? A tag c:param já cuida disso para você automaticamente, ou seja, é bastante aconselhável que se use a tag c:param quando se deseja passar parâmetros tanto para a tag c:url quanto para as demais tags de gerenciamento de URLs. Uma vez que explicamos a funcionalidade dessa tag, vamos agora mostrar e explicar cada um dos seus atributos. Atributo Descrição Obrigatório Valor default value URL que será processada. Pode ser uma expressão ou a própria URL. Sim var Nome da variável que guardará o valor processado da URL Não scope Escopo onde a variável definida no atributo var será armazenada. Não Página Quadro 1 – atributos da tag c:url O exemplo anterior já mostrou uma forma de uso para funcionalidade c:url, porém, vamos explicar cada um dos seus atributos. O atributo value define qual a URL que será processada. O valor definido neste atributo pode ser uma string que representa uma URL diretamente ou uma expressão que será avaliada antes de ser processada pela tag. O atributo var define qual o nome da variável onde será armazenado o resultado do processamento da URL, assim poderemos usar esse resultado em vários lugares dentro da página usando a funcionalidade c:out, por exemplo. O atributo scope define em qual escopo a variável definida pelo atributo var será armazenada. Se ele não for especificado, o default é a página. Continuando com as tags de gerenciamento de URLs, temos a tag c:import. Essa tag permite que você inclua dentro da sua páginas JSP outras páginas, que podem estar no mesmo servidor ou em outros. Além disso, essas páginas devem ser snippets de código e não páginas inteiras com marcações .... Uma vez que explicamos a funcionalidade desta tag, vamos agora mostrar e explicar cada um dos seus atributos. Atributo Descrição Obrigatório Valor default url URL do recurso (HTML, JSP, REST, etc) a ser importado Sim var Nome da variável que guardará o conteúdo importado Não scope Escopo onde a variável definida no atributo var será armazenada Não Página Quadro 2 – Atributos da tag c:import O recurso a ser importado é definido pelo atributo url. Esse recurso pode ser um HTML, um JSP, um serviço etc. Assim como a tag c:url, também podemos passar parâmetros para a URL utilizado como recurso da mesma forma que fizemos com a tag c:url. Assim como a tag c:url, os demais atributos têm o mesmo comportamento. A última tag de gerenciamento de URLs é a tag c:redirect. Essa tag permite que o fluxo da requisição seja desviado para outra URL, ou seja, ao processar uma página JSP que usa a tag c:redirect, ao encontrá-la, o fluxo é desviado para outra página dentro do servidor. O exemplo abaixo mostra a páginas índex.jsp que redireciona o fluxo para o servlet de exemplo. Uma vez que explicamos a funcionalidade desta tag, vamos agora mostrar e explicar cada um dos seus atributos. Atributo Descrição Obrigatório Valor default url URL do recurso para o qual o fluxo será redirecionado Sim Quadro 3 – Atributos da tag c:redirect Dessa forma, concluímos as tags de gerenciamento de URLs da taglib core. Continuaremos agora as tags de controle de fluxo. 1. Use a tag c:import para criar uma estrutura de páginas que possuam um header, um footer e um menu lateral e use essa estrutura em duas páginas JSPs. 2. Modifique o fragmento de página que representa o header para receber um parâmetro que é o título da página. Dica: Para usar o parâmetro na página do header, use a seguinte expressão ${param.nomeDoParametro}. As funcionalidades de controle de fluxo da taglib core compreendem as tags de loop e condicionais. Começaremos essa parte falando da tag de loop e concluiremos a taglib core mostrando as tags condicionais. Para controlar loops dentro do JSP, a taglib core usa a tag c:forEach. Ela é responsável por fornecer uma maneira simples de iterar em coleções(Arrays, Listas, Mapas etc.) ou um número definido de vezes. Tudo que estiver dentro do corpo dessa tag será repetido enquanto houver elementos na coleção ou o limite de vezes ainda não foi atingido. Já vimos nos exemplos da aula passada o uso dessa tag quando iteramos por uma coleção de filmes e pessoas exibindo seus nomes. Agora, vamos entender com mais detalhes o funcionamento da tag usando como exemplo a listagem 2. 01 02 03 04 05 06 07 08 09 10 11 12 13 <%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %> Lista de filmes:

${filme}
Listagem 2 – Uso da tag c:forEach No exemplo acima, a lista de filmes é um array de strings. Na linha 06, estamos dizendo que o todo body dentro da tag c:forEach será repetido enquanto houver elementos no array de filmes. O atributo itens pode receber tanto uma expressão, como no caso acima, como uma string separada por vírgulas, por exemplo . No exemplo completo da aula passada, vimos que ${filmes} é uma array que foi definido como um atributo da requisição dentro do nosso servlet de exemplo. O atributo var define o nome da variável onde cada elemento da iteração será armazenado. Uma vez que explicamos a funcionalidade dessa tag, vamos agora mostrar e explicar cada um dos seus atributos. Atributo Descrição Obrigatório Valor default begin Índice do primeiro item a ser processado. Inicia em zero Não 0 end Índice do último item a ser processado Não Último item step Processa cada enésimo elemento. Por exemplo, se step for dois processa o segundo, quarto, sexto e assim por diante Não 1 var Nome da variável onde o elemento corrente da iteração será armazenado. Só existe durante a execução do loop Não items Coleção, Iterador, Mapa ou Array para ser iterador Não varStatus Nome da propriedade que armazenará o status do loop. Esse objeto que representa o status possui quatro atributos: • índex – posição do item corrente; • count – número de vezes que o loop foi executado (começa em 1); • first – booleano que indica se é a primeira iteração; • last – booleano que indica se é a última iteração Não Quadro 4 – Atributos da tag c:forEach O funcionamento básico da tag c:forEach já foi explicado. Agora, mostraremos o que cada atributo representa para tag. O atributo begin informa à tag qual deve ser o índice que o loop iniciará a iteração. Esse atributo tem uma semântica bastante similar à variável de controle de uma loop for Java. Em um loop for Java, temos a seguinte construção for (int i = 0; i < 10; i++)... Se definirmos o atributo begin como 5, por exemplo, é como tivéssemos definindo a variável i do loop Java como 5. De forma análoga funciona o atributo end. Nesse caso, a sua analogia é com a constante 10 do loop Java. O atributo step, por analogia, seria a condição de incremento do loop Java, ou seja, o i++. Para finalizarmos, da mesma forma que a variável i só existe dentro do contexto do loop, a variável que representa o valor corrente da iteração (indicada pelo atributo var) só pode ser usada dentro da tag c:forEach. Na listagem 3, temos um exemplo correto na linha 02 da variável filme e um exemplo incorreto de uso na linha 05. 01 02 03 04 05 ${filme} ${filme} Listagem 3 – Uso indevido da variável filme fora da tag c:forEach O atributo itens pode ser uma expressão que avalia para uma coleção, mapa, iterador, array ou uma string separada por vírgulas, ou até mesmo uma string separada por vírgulas, como dito anteriormente. Por último, e não menos importante, temos o atributo varStatus. Esse atributo, quando definido, coloca no escopo da página durante a execução do loop um objeto que possui atributos que auxiliam no controle do loop. No último exemplo da aula passada, fiz uso desse atributo para definir o atributo id do objeto pessoa, como mostrado no exemplo abaixo. 1. Altere a tag c:forEach para imprimir apenas o primeiro e o terceiro elemento do array usando um dos atributos da tag. 2. Altere a tag para imprimir a listagem invertida. Chegamos agora ao último conjunto de funcionalidades da taglib core que é o controle de fluxo condicional. Essas tags avaliam condições lógicas e executam o corpo da tag caso essa condição seja verdadeira. Existem duas tags para controlar o fluxo baseado em condições que são c:if e c:choose. Começaremos explicando a tag c:if. Um exemplo clássico de controle de fluxo condicional é quando nós temos uma única página JSP que serve para todos os usuários de uma determinada aplicação, porém, de acordo com o perfil do usuário logado, alguns itens são exibidos e outros não. Um exemplo de uso é mostrado na listagem 4 01 02 03 Listagem 4 – Uso da tag c:if para importar JSP para os usuários que possuem o perfil ADMIN Perceba que na linha 01 nós testamos a condição em que o perfil do usuário logado é igual a ADMIN. Se essa condição for verdade, o menuAdmin.jsp será importado. Uma vez que explicamos a funcionalidade dessa tag, vamos agora mostrar e explicar cada um dos seus atributos. Atributo Descrição Obrigatório Valor default test Expressão condicional que será avaliada Sim var Nome da variável onde será armazenado o resultado do teste da expressão condicional Não scope Escopo da variável Não Página Quadro 5 – Atributos da tag c:if Como acabamos de ver, o atributo test é a expressão condicional que será avaliada para saber se o corpo da tag será processado ou não. O comportamento dos demais atributos nós já vimos repetidas vezes durante o curso. Qualquer dúvida que vocês venham a ter, por favor perguntem ou vejam a aula anterior. 1. Modifique o exemplo da listagem de filmes para só listar os filmes quando a variável varStatus.index for maior que 1. Para encerrarmos as funcionalidades da taglib core, vamos à última tag de controle de fluxo condicional que é a tag c:choose. Essa tag sozinha não funciona, ela precisa de duas tags auxiliares aninhadas para fazer sentido. Essas tags que devem ser aninhadas são c:when e c:otherwise. Tomando como base o exemplo de um usuário logado em uma aplicação e que um determinado menu será importado de acordo com o perfil desse usuário, vamos “complicar” um pouco mais, como mostra a listagem 5. 01 02 03 04 05 06 07 08 09 10 11 Listagem 5 – Uso da tag c:if para importar JSP para os usuários que possuem o perfil ADMIN Percebam que essa estrutura de tag é bastante similar a um comando switch/case/default Java e da mesma forma que no comando Java nós não precisamos usar default ao fim do switch, nós também não precisamos usar a tag c:otherwise. Percebam também que a construção da tag c:when é similar a da tag c:if, porém,, ela não possui os atributos var e scope, apenas o atributo obrigatório test. 1. Modifique o exemplo da listagem de filmes para alterar o background das linhas pares da tabela caso a linha seja par ou ímpar. Dica: usar no test a expressão ${algumacoisa % 2 eq 0}. Então, pessoal, muito fácil né?! Bom, chegamos ao fim da taglib core. Na próxima aula, veremos a taglib de internacionalização I18N. Até lá! Leitura complementar Para complementar seu aprendizado, veja o assunto abordado nas seguintes referências: • Resumo Hoje você terminou os estudos sobre a taglib core do JSTL. Viu a parte mais interessante dessa taglib, que é o controle de fluxo com loops e condicionais. Com o conhecimento dessa taglib, já é possível fazer bastante coisa de forma organizada e de fácil manutenção. Na próxima aula, você verá um pouco mais de JSTL com a taglib I18N. Descreva o funcionamento das tags (incluindo seus atributos) que você aprendeu nesta aula para: 1. gerenciamento de URLs; 2. controle de fluxo. esenvolvimento Web Aula 10 – JSTL – Internacionalização Olá, caro aluno. Hoje, continuaremos com a nossa aula falando mais um pouco sobre JSTL e sua taglib de internacionalização, I18N. Essas tags definem a localização de uma página, criando mensagens sensíveis à localidade, formatação e parsing de objetos, como números, moedas, datas e horários de uma maneira sensível à localidade ou personalizado. Ao ler esta aula, você estará apto a: • construir páginas cujo conteúdo é sensível à localização do usuário. A taglib I18N A biblioteca I18N implementa funcionalidades de definição de localização, mensagens localizadas e formatação de números, datas, moedas, hora e data, conforme o quadro a seguir. Essas funcionalidades são importantes quando se deseja construir uma aplicação que será usada em diferentes idiomas, como veremos nos exemplos mais adiante. Área Funcionalidade Tags Prefixo I18N Definição de Localidade setLocale fmt Mensagens bundle message param setBundle Formatação formatNumber formatDate Começaremos nossos estudos pela tag de definição de localidade c:setLocale. Essa tag permite definir qual Locale (localidade geográfica) será usado para exibir os itens sensíveis à localidade. O Locale será definido dentro de um escopo que pode ser a página, comportamento padrão, a requisição, a sessão ou a aplicação. A tag fmt:setLocale sobre-escreve o Locale definido pelo browser do usuário, ou seja, mesmo que o browser do usuário esteja configurado com o Locale en_US, por exemplo, nós podemos usar a tag fmt:setLocale para definir a localidade para pt_BR e a configuração do browser será ignorada. Assim, a listagem 1 representa o uso da tag fmt:setLocale da taglib fmt. 01 Listagem 1 – Código JSP que define o idioma da página JSP para inglês americano Um detalhe que vale a pena mencionar é que a tag fmt:setLocale é uma tag de configuração, logo o seu corpo deve ser deixado vazio e seu escopo pode ser mais abrangente que apenas uma página. Mais a frente veremos outras tags que funcionam apenas para configuração e com escopos também mais amplos, assim como veremos tags cujo escopo é apenas o seu corpo. Uma vez que explicamos a funcionalidade desta tag, vamos agora mostrar e explicar cada um dos seus atributos. Atributo Descrição Obrigatório Valor default value Expressão que avalia para um java.lang.String ou java.util.Locale. Pode ser também apenas uma string. Sim scope Escopo onde a variável que representa o Locale será armazenada. Não Página Quadro 1 – Atributos da tag fmt:setLocale Na Listagem 1, vimos o uso do atributo value que pode ser uma string, como, por exemplo, pt_BR, ou uma expressão que será avaliada para Java.lang.String ou Java.util.Locale. O atributo scope já é um velho conhecido nosso e tem a mesma característica que vimos antes. Continuando com as tags de internacionalização, vamos agora ver as tags de mensagem. A tag fmt:bundle define o contexto de localização, baseado em um resource bundle, que será usado apenas dentro do corpo da tag, enquanto a tag fmt:setBundle define um resource bundle não apenas para o seu corpo, que por sinal deve ser vazio, e sim para um escopo. Se você deseja usar, por exemplo, um resource bundle para a toda a sua aplicação, você pode usar a tag fmt:setBundle como mostrado na Listagem 2. Porém, se em determinada página quisermos outro resource bundle, podemos usar a tag fmt:bundle e aninhar dentro dela o conteúdo que desejamos usar em outro resource bundle, como mostra a Listagem 3. 01 Listagem 2 – Código JSP que define o resource bundle para ser usado pela aplicação 01 02 03 Listagem 3 – Código JSP que define um resource bundle específico para ser usado dentro do corpo da tag Aqui, é importante algumas explicações. Na linha 01 da Listagem 2, a tag define qual o resource bundle da aplicação, ou seja, qual o nome do arquivo .properties que será usado, no caso messages. Na linha 01 da Listagem 3, estamos dizendo que usaremos um outro arquivo chamado messages2 como resource bundle. Na linha 02, estamos dizendo que existe uma propriedade neste arquivo cujo nome(ou chave) é message.helloWorld e que o valor será usado dentro do corpo da tag fmt:bundle. Para ficar mais claro, vamos a um exemplo. Aqui, escrevemos um servlet que por enquanto só redireciona a requisição para o nosso JSP, como mostrado a seguir. O nosso JSP define o resource bundle como messages e solicita que o valor associado à chave message.helloWorld seja exibido. O nosso resource bundle possui uma chave que é message.helloWorld cujo valor é Alô Mundo!!Note no JSP acima que essa é a mesma chave que nós estamos usando na tag fmt:message(falaremos mais dessa tag mais a frente). Porém, para uma determinada parte do meu JSP, eu poderia não querer usar o bundle definido pela tag fmt:setBundle, mas sim um outro bundle chamado messages2. Dessa forma, a mensagem final não seria mais Alô Mundo!!! e sim Alô Mundo 2!!! Como pode ser visto no JSP modificado. Perceba que agora o resultado será diferente, pois não estamos usando o bundle messages e sim messages2. Uma vez que explicamos a funcionalidade da tag fmt:bundle e fmt:setBundle, vamos agora mostrar e explicar cada um dos seus atributos. Atributo Descrição Obrigatório Valor default basename Nome do resource bundle(sem o .properties) Sim prefix String que será pré concatenada ao valor da chave da mensagem(com o . no final) Não Quadro 2 – atributos da tag fmt:bundle Atributo Descrição Obrigatório Valor default basename Nome do resource bundle(sem o .properties) Sim var Nome da variável que armazenará o contexto de localização Não scope Escopo da variável armazenada em var. Não Página Quadro 3 – Atributos da tag fmt:setBundle Em ambas as tags, basename é o atributo que define o nome do resource bundle. Para a tag fmt:bundle, o atributo prefix define uma string que será concatenada no início de cada string usada como chave pela tag fmt:message. No exemplo acima, nós usamos como chave message.helloWorld, porém, se tivéssemos definido o atributo prefix na tag fmt:bundle como “message.”, poderíamos ter usado como chave apenas a string “helloWorld”. Os demais atributos mais uma vez são nossos velhos conhecidos. A próxima tag é a tag fmt:message, que possui uma tag auxiliar que a tag fmt:param. Nós já vimos brevemente a tag fmt:message em uso quando falamos de fmt:bundle e fmt:setBundle. Agora veremos a tag fmt:messagem usando a tag fmt:param. A tag fmt:message procura por mensagens localizadas dentro de um resource bundle. Essa tag, como dito, pode conter a tag fmt:param no seu corpo que especificam valores para os parâmetros definidos na mensagem. Na Listagem 4, podemos ver um exemplo de uso da tag fmt:message como a tag fmt:param. 01 02 03 Listagem 3 – Código JSP que procura por uma chave chamada message.goodMorning no resource bundle cujo parâmetro será substituído pelo valor definido por fmt:param Aqui, vale explicar o que seria substituir o parâmetro da mensagem da chave message.goodMorning, linha 01, pelo valor do parâmetro especificado pela tag fmt:param, linha 02. Para que isso seja possível, o valor da chave message.goodMorning no resource bundle deve ser algo como message.goodMorning=Bom dia {0}!!!. Isso permite que no lugar de {0} eu terei o valor que foi definido na tag fmt:param. Um exemplo mais prático seria usar como parâmetro o nome do usuário logado no sistema. Uma vez que explicamos a funcionalidade da tag fmt:bundle e fmt:setBundle, vamos agora mostrar e explicar cada um dos seus atributos. Atributo Descrição Obrigatório Valor default key Nome da chave a ser procurada no resource bundle Não Corpo var Nome da variável que armazenará a mensagem Não scope Escopo da variável armazenada em var Não Página Quadro 4 – atributos da tag fmt:message Atributo Descrição Obrigatório Valor default value Valor que será substituído em uma mensagem parametrizada Sim Corpo Quadro 5 – atributos da tag fmt:param O atributo key é o nome da chave no resource bundle que corresponde à mensagem a ser exibida. A key pode ser uma string ou uma expressão. O atributo value da tag fmt:param define o valor que será usado para substituir os parâmetros da mensagem. Aqui, vale um comentário. Uma mensagem pode ter n parâmetros ( {0}, {1}, {2}, ..., {n} ), logo a ordem da tag fmt:param deve obedecer à ordem definida no resource bundle. O atributo value também pode ser uma string ou uma expressão. 1. Crie um arquivo messages_en_US.properties e defina a chave message.helloWorld com o valor Hello World!!!. Em seguida, use a tag fmt:setLocale para mudar o Locale para em_US e veja o que acontece. 2. Retire a tag fmt:setLocale e mude o idioma diretamente no seu browser para em_US e veja o que acontece. 3. Use novamente a tag fmt:setLocale, sem alterar a língua do seu browser que deve estar como em_US, com o locale T_BR e veja o que acontece. Formatação de datas e números Agora, veremos as tags de formatação de datas e números que são sensíveis à localização. Por exemplo, nos EUA o formato padrão de data é mês/dia/ano, enquanto no Brasil esse formato é dia/mês/ano. Da mesma forma valores numéricos, pois uns usam o separador de casas decimais o caractere ‘.’ e outros usam ‘,’. Assim, essas tags de formatação nos fornecem os mecanismos necessários para exibir essas informações sensíveis de localidade de forma correta. A tag de formatação de data, fmt:formatDate, usa as informações de localização do browser do usuário ou a definida pela tag fmt:setLocale. Você também pode customizar a formatação dessa data usando alguns atributos da tag, como veremos mais adiante. Adicionalmente, essa tag formata informações de data e de hora. Vamos começar alterando o exemplo da aula passada que lista um conjunto de pessoas. Vamos alterar a classe Pessoa para ter mais um atributo, que é a data de nascimento da pessoa, como mostrado na figura a seguir. Agora, vamos alterar o servlet de exemplo que lista as pessoas do sistema para incluir o valor do atributo dataDeNascimento, conforme a figura a seguir. Agora, vamos alterar a o JSP que exibe os dados das pessoas para exibir além do nome a data de nascimento, como mostra a figura a seguir. Veja o resultado Perceba que a data ficou num formato legível sem muitos problemas. Agora, vamos trocar a tag fmt:formatDate por c:out, ou seja, onde temos vamos mudar para . Percebeu a diferença? O que acontece quando usamos a tag c:out é que quando a expressão ${pessoa.dataDeNascimento} é avaliada o resultado é um objeto do tipo java.util.Date só que essa tag trabalha com objetos do tipo java.lang.String, então, a tag chama o método toString da classe java.util.Date cujo resultado é o que acabamos de ver. Já a tag fmt:formatDate espera um objeto do tipo java.util.Date e usa seus valores default para formatar a data de uma forma mais fácil de ser entendida. Vamos agora examinar todos os atributos da tag fmt:formatDate para entendermos seu comportamento padrão e o que podemos fazer para customizar a formatação de uma data e de uma hora. Atributo Descrição Obrigatório Valor default type Define o tipo do valor especificado pelo atributo value. Os tipos válidos são: time, date, both Não Date dateStyle Estilos de formatos pre-definidos para uma data. É usado se o type for date. Os estilos válidos são: default, short, medium, long e full Não Default timeStyle Estilos de formatos pre-definidos para uma hora. É usado se o type definido for time. Os estilos válidos são:default, short, medium, long e full. Não Default pattern Expressão customizada para formatação de data e hora. Se for definido sobre-escreve os atributos type, dateStyle e timeStyle. Não value O valor da data a ser formatada. Não var Nome da variável que armazenará a data/hora formatada. Não scope Escopo da variável armazenada em var Não Página Quadro 6 - Atributos da tag fmt:formatDate Começaremos explicando o atributo type. Como vimos no exemplo anterior, se usarmos a tag c:out para exibir uma data, são exibidas informações de data e hora. Entretanto, na maioria dos casos nós estamos apenas interessados na data ou na hora. Com o uso do atributo type, nós podemos definir qual informação será exibida, se a data, a hora ou ambos. Os atributos dateStyle e timeStyle fornecem algumas formatações básicas para data e hora. Como vimos na tabela anterior, existem cinco tipos pré-definidos de estilos para data e hora. Quando usamos o tipo default o resultado para uma data é dia/mês/ano com quatro dígitos. Já para hora, o resultado é hora:minuto:segundo. Para o estilo short temos os seguintes resultados dia/mês/ano com dois dígitos e a hora igual ao estilo default. Como exercício, você pode verificar como fica os demais estilos. O atributo pattern prevê uma forma alternativa de formatação de data e hora quando nenhum dos estilos pré-definidos pode atender às nossas necessidades. Para entender quais são os possíveis valores desse campo, é necessário entender a classe java.text.SimpleDateFormat. Só a título de exemplo, imaginemos que seja necessário exibir apenas a data com seu dia e mês. Nenhum dos tipos pré-definidos suporta essa formatação, mas podemos usar o atributo pattern, que como vimos na tabela sobrescreve os atributos type, dateStyle e timeStyle, o valor dd/MM . Os demais atributos já são nossos velhos conhecidos. Se você tiver alguma dificuldade com padrões de formatação de data e hora veja este link . 1. Brinque com atributos type, timeStyle e dateStyle para entender melhor o comportamento deles. 2. Use o atributo format para formatar uma data dia/mês hora:minuto. Veremos agora a tag fmt:formatNumber, que tem um comportamento e atributos bastante similar a tag fmt:formatData, porém, sua finalidade, como o próprio nome já diz, é formatar números. Vamos começar alterando o nosso exemplo de forma que a classe Pessoa tenha mais um atributo, que é o saldo da conta corrente da pessoa, como mostrado na figura a seguir. Agora, vamos alterar o servlet de exemplo que lista as pessoas do sistema para incluir o valor do atributo saldo conforme a figura a seguir. Devemos alterar também o JSP que exibe os dados das pessoas para exibir, além do nome, a data de nascimento e agora o saldo usando a tag fmt:formatNumber. Veja o resultado: Perceba que o saldo ficou formatado, inclusive com vírgula separando as casas decimais, porém, para o último valor, a última casa decimal foi omitida. Agora, vamos trocar a tag fmt:formatNumber por c:out, ou seja, onde temos vamos mudar para . Percebeu a diferença? O que acontece quando usamos a tag c:out é que perdemos o separador de decimais, porém a última casa decimal do saldo de Beltrano foi exibida. Isso acontece pelo mesmo motivo que a data fica com a formatação verbose quando usamos a tag c:out. O objeto que a tag c:out recebe é um java.lang.Double e então chama o método toString. Já a tag fmt:formatNumber espera receber um objeto do tipo java.lang.Number (Double é uma subclasse de Number) para então aplicar as formatações adequadas. Vamos agora examinar todos os atributos da tag fmt:formatNumber para entendermos seu comportamento padrão e o que podemos fazer para customizar a formatação de um número. Atributo Descrição Obrigatório Valor default value O valor numérico a ser formatada. Não Corpo type Define o tipo do valor especificado pelo atributo value. Os tipos válidos são: number, currency, percent Não number pattern Expressão customizada para formatação. Se for definido sobrescreve os demais atributos de formatação. Não currencyCode Código da moeda usado para formatar valores monetários, tais como USD, EUR. Não De acordo com o Locale currencySymbol Símbolo da moeda ($, F) Não De acordo com o Locale maxIntegerDigits Número máximo de dígitos a ser exibido da parte inteira do número Não minIntegerDigits Número mínimo de dígitos a ser exibido da parte inteira do número Não maxFractionDigits Número máximo de dígitos a ser exibido da parte fracionário do número Não minFractionDigits Número mínimo de dígitos a ser exibido da parte fracionário do número Não var Nome da variável que armazenará o número formatado Não scope Escopo da variável armazenada em var Página Quadro 7 - Atributos da tag fmt:formatNumber Os atributos value, var e scope dispensam apresentação. Os atributos currencyCode e currencySymbol definem o que para nós brasileiros seria o R$, onde o R seria o currencyCode e o $ seria o currencySymbol. Aqui, vale a seguinte explicação: se o atributo type for definido como currency e o Locale estiver definido como pt_BR, automaticamente o valor formatado será exibido com R$. Da mesma forma, se o atributo type for definido como percent o símbolo de % será exibido automaticamente para você. Os demais atributos definem a quantidade de dígitos que serão exibidos na parte inteira e fracionária do número. Isso ajuda a, por exemplo, limitar o número de casas decimais a ser exibido. Se minFractionDigits for definido para 2 e o número a ser formatado for, por exemplo 23,2, o valor exibido será 23,20. Por outro lado, se maxFractionDigits for igual a 2 e o número a ser exibido for 23,285, o resultado será 23,29. Perceba que houve um arredondamento, e não um truncamento do número, para o número mais próximo. Os atributos minIntegerDigits e maxIntegerDigits funcionam de forma similar. Então, mais uma vez, muito fácil né?! Nada que um pouco de prática e uso não resolva. Bom, chegamos ao fim da taglib fmt. Na próxima, aula veremos a taglib functions(fn), que trata de manipulação de strings e tamanho de coleções, e EL, que mostra como podemos acessar às variáveis de escopo de uma forma elegante e padronizada. Até lá! Leitura complementar Para complementar seu aprendizado, leia sobre os comandos aprendidos nesta aula através da referência abaixo: • Resumo Hoje, concluímos a taglib de internacionalização I18N do JSTL. Você viu que podemos configurar nossa aplicação com as tags fmt:setLocale e fmt:setBundle. Adicionalmente, viu que podemos sobrescrever o bundle definido pela tag fmt:setBundle usando a tag fmt:bundle. Aprendeu como usar as mensagens que estão definidas dentro do resource bundle e como passar parâmetros para as mensagens parametrizadas. Com o conhecimento dessa taglib, já é possível fazer uma aplicação que suporte várias localizações sendo que o esforço para isso é bastante reduzido. Na próxima aula, veremos um pouco mais de JSTL com a taglib fn e EL. 1. Descreva como aplicações podem ser internacionalizadas, ou seja, criadas de forma que as mensagens apresentadas estejam de acordo com a localidade (língua, formato de números etc.). Desenvolvimento Web Aula 11 – JSTL – Taglib functions e expression language Olá, caro aluno. Hoje finalizaremos nossas aulas de JSTL estudando a taglib functions e EL (expression language). Tenho certeza que você conseguirá visualizar a utilidade dessas instruções, principalmente com relação à linguagem de expressão (EL). Tenha uma boa leitura! Ao ler esta aula, você estará apto a: • Saber utilizar a taglib functions. • Aprender a usar EL ao invés de scriptlets. A taglib functions Você viu na aula passada as tags usadas para construir uma aplicação que pode ser internacionalizada. Hoje continuaremos os estudos mostrando as funções de JSTL, que manipulam strings e descobrem o tamanho das coleções e strings, bem como EL, que é uma alternativa mais elegante ao uso de scriptlets. Vamos lá, então, começando pela funções JSTL. As funções JSTL possuem basicamente dois conjuntos de funcionalidades. A primeira trata das funções básicas de string, como trim, toUpperCase, substring etc. A segunda possui a única finalidade de, dada uma coleção ou uma string, descobrir qual o seu tamanho. O quadro a seguir mostra o conjunto de funcionalidades existentes. Área Funcionalidade Tag Prefixo Funcionalidade Tamanho de coleções Length fn Manipulação de strings toUpperCase, toLowerCase substring, substringAfter, substringBefore trim replace indexOf, startsWith, endsWith, contains, containsIgnoreCase split, join escapeXml Cada uma dessas funcionalidades tem a mesma semântica dos métodos da classe java.lang.String, exceto substringAfter, substringBefore, containsIgnoreCase e join. Nesta aula o nosso foco será nessas tags, visto que não existe método correspondente para estas funcionalidades na classe String. Veremos também a funcionalidade length. Antes de começarmos a explicar a taglib functions é necessário explicar uma pequena diferença entre esta tag e as demais. As taglibs que vimos antes, core e I18N, definem um conjunto de atributos que são usados para que a tag execute adequadamente. Entretanto, a taglib functions não funciona assim. Na verdade, ela define uma assinatura de método, como seus parâmetros e tipo de retorno, que devem ser invocados de forma similar à invocação de método numa classe Java. Vamos ao exemplo. Ao invés de chamar a funcionalidade contains assim: , eu devo chamar assim: ${fn:cotains(variavel, algumValor)}. Percebeu a diferença? Entretanto, assim como as demais tags, os parâmetros utilizados nestas funções que são nulos, quando string, são tratados como string vazia. Preparamos um exemplo a seguir que mostra o uso das taglibs que não possuem função correspondente na classe String. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 <%@ taglib uri="https://java.sun.com/jsp/jstl/functions" prefix="fn" %> <%@ taglib uri="https://java.sun.com/jsp/jstl/core" prefix="c" %> O tamanho da String tempStr é: ${fn:length(tempStr)}
A substring depois da palavra "Tags" é: ${fn:substringAfter(tempStr,"Tags")} A substring antes da palavra "tem" é: ${fn:substringAfter(tempStr,"tem")} Esta string contem a palavra "tags"? ${fn:containsIgnoreCase(tempStr,"tags")}
Neste exemplo vemos o uso das tags substringAfter, substringBefore e contaisnIgnoreCase. Daqui a pouco veremos um exemplo de join. Na linha 05 definimos uma variável usando a tag c:set cujo valor possui aspas. Na linha 06 pedimos que seja exibido o tamanho dessa variável. Nesse caso a função length contará o número de caracteres dessa variável. Na linha 07 será exibido uma string que é uma substring da variável tempStr a partir da palavra “Tags”. O resultado disso será “tem sintaxe diferente”. Já na linha 08 temos a mesma coisa. A diferença é que estamos interessados em exibir apenas a substring que vem antes da palavra “tem”, no caso “Functions e Tags”. Na linha 09 vemos o uso da funcionalidade containsIgnoreCase. Esta funcionalidade verifica se existe a string “tags”, independente se ela está escrita como “TAGS”, “TaGs” etc. dentro da String definida por tempStr. Nesse o caso, o resultado será true. A tag join tem o comportamento de exibir os elementos de um array como string onde cada elemento do array é separado por uma determinada string. Veja o exemplo abaixo. O nosso servlet de exemplo define um array de strings como atributo da requisição No nosso JSP e de exemplo nós usamos o array definido no servlet de exemplo (linha 15) como parâmetro para a função join, bem como a string “,” (linha 4). O resultado é que os elementos do array são exibidos separados por ,. Com este último exemplo vimos como usar as funções de JSTL. Agora veremos EL, que eu considero uma das melhores funcionalidades na construção de páginas JSP. EL é uma forma elegante de não usar scriptlets, porém isso não significa que não existam situações onde seu uso não se justifique, mas deve ser evitado, e EL deve ser usado no seu lugar. A principal vantagem em usar EL no lugar de scriptlets é que o código JSP fica mais legível, fácil de entender e manter. As expressões em EL possuem a seguinte sintaxe ${expressao}. Nós já vimos um pouco de EL enquanto estudamos JSTL, porém sem entrar no detalhe de contexto e funcionamento. Com ele podemos acessar objetos que estão no escopo da página, da requisição, da sessão e da aplicação sem precisar indicar onde estes objetos estão, pois automaticamente EL procura o objeto em todos estes escopos na ordem apresentada. Por exemplo, digamos que existe um atributo chamado “usuarioLogado” na sessão da minha aplicação. Suponha que queremos exibir o nome deste usuário na página. Para isso temos que usar o seguinte código no JSP: ${usuarioLogado.nome}. Agora, como faríamos isso usando scriptlets? Seria algo como <%= request.getSession().getAttribute(“usuarioLogado”).getNome() %>. Perceba aqui que eu tive que dizer explicitamente em qual contexto estava o objeto referente a usuarioLogado. Além disso, este código não é tão robusto, pois se o usuário logado não estiver na sessão fatalmente este código levantará NullPointerException. Quando usamos EL o comportamento é procurar por usuarioLogado em cada um dos escopo, e se o objeto não for encontrado em nenhum deles, a expressão para a avaliação, evitando a tentativa de avaliar a propriedade nome em um objeto nulo. Perceba que a sintaxe do EL é muito menos verbosa que a do scriptlet, o que melhora o entendimento do código. Perceba também que seria possível escrever um código mais robusto usando scriptlets, porém seria muito mais trabalhoso e menos produtivo, como mostra o exemplo abaixo. 01 02 03 04 05 06 07 08 09 10 <%@ page import="aulaJSTL.Usuario" %> <% String nome = ""; if (session != null && session.getAttribute("usuarioLogado") != null) { Usuario usuario = (Usuario) session.getAttribute("usuarioLogado"); nome = usuario.getNome(); } %> <%= nome %> O exemplo anterior mostra porque é preferível usar EL ao invés de scriptlets. Entretanto, existem algumas situações em que não é possível usar EL. Isto se deve ao fato de EL trabalhar apenas com objetos que são JavaBeans, ou seja, objetos que possuem métodos set e get. Quando usamos a expressão ${usuárioLogado.nome} estamos na verdade invocando o método getNome do objeto Usuario. Agora, imagine a situação em que queremos invocar algum método que não seja setters ou getters em algum objeto. Usando EL isso não é possível. Nestas situações é admitido o uso de scriptlets. Vimos então que não podemos chamar qualquer método nos objetos usando EL. Só podemos invocar métodos que obedeçam ao padrão JavaBean. Porém, existem formas de acessar as propriedades e atributos desses objetos usando EL. Vimos que para acessar a propriedade nome do objeto usuarioLogado precisamos apenas usar ${usuarioLogado.nome}, mas esta não é a única maneira de acessar a propriedade nome. Podemos também usar a notação ${usuarioLogado[“nome”]}. Esta forma é possível, mas só é usada, na maioria das vezes, se o valor do atributo usuarioLogado for um java.util.Map. Vamos supor que na sessão o objeto associado a usuarioLogado fosse um Map ao invés de um Usuario. A forma de acessar os valores desse mapa seria algo como ${nomeDoMapa[“nomeDaChave”]}. Da mesma forma, imaginemos agora que o atributo na sessão fosse um java.util.List ou um array. A forma de acessar os valores nessa lista seria ${nomeDaLista[indice]}. Legal, não é mesmo? Uma vez que vimos as formas como podemos acessar objetos, mapas, listas, arrays e suas propriedades, vamos agora entender mais um detalhe sobre EL. Da mesma forma que dentro dos códigos scriptlets nós temos algumas variáveis que estão implícitas, em EL isso também é verdade. Por padrão ${algumaCoisa} procura por uma variável com o nome algumaCoisa em algum dos escopos, porém eu posso deixar explícito qual o escopo usando um nome de variável que representa cada um dos escopos. Por exemplo, ${sessionScope.usuarioLogado} ou ${pageScope.pessoas}. sessionScope e pageScope são variáveis implícitas do contexto EL. O quadro a seguir exibe os contextos implícitos disponíveis. Objeto Descrição pageScope Define as variáveis que estão no escopo da página requestScope Define as variáveis que estão no escopo da requisição sessionScope Define as variáveis que estão no escopo da sessão applicationScope Define as variáveis que estão no escopo da aplicação param Mapa que contém o valor parâmetros da requisição paramValues Mapa que contém o valor parâmetros da requisição como array de strings header Mapa que contém o nome e valor dos headers da requisição headerValues Mapa que contém o nome e o array de valores dos headers da requisição cookie Mapa do nome dos cookies e o cookie initParam Mapa com o nome dos parâmetros iniciais e seus valores Cada uma dessas variáveis representa um objeto Java já visto por nós. Por exemplo, quando usamos ${param.code} estamos na verdade fazendo request.getParameter(“code”). Bem mais fácil que usar scriptlets, não é?! Outra coisa muito legal de EL é o uso de operadores de aritmética, lógica e relacionais. A seguir mostraremos quais são estes operadores e como usá-los. Operador Descrição + Adição - Subtração * Multiplicação / (div) Divisão % (mod) Módulo da divisão (resto) == (eq) Igualdade != (ne) Desigualdade < (lt) Menor que > (gt) Maior que <= (le) Menor ou igual >= (ge) Maior ou igual && (and) Verdadeiro se ambos operandos são verdadeiros; Falso caso contrário || (or) Verdadeiro se um ou ambos operandos forem verdadeiros; Falso caso contrário ! (not) Verdadeiro se operando for verdadeiro; Falso caso contrário empty Verdadeiro se o operando for nulo, uma string vazia, um mapa vazio ou uma lista vazia; Falso caso contrário. Bom, operações é que não faltam J. Basicamente são todas as operações de lógica e aritmética da linguagem Java. Lembram da atividade que listava as pessoas do sistema? Pois bem, e se quiséssemos pintar as linhas ímpares da tabela de uma cor e as pares de outra, como poderíamos fazer? Abaixo mostramos uma possível maneira fazer isso. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 <%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %> Lista de pessoas:

">
Observe que na linha 07 eu usei o atributo varStatus para ter acesso ao contexto de loop da tag c:forEach. Dentro o loop eu uso a tag c:choose na linha 08 para escolher se a linha será cinza ou branca de acordo com o índice da minha iteração. Se o índice % 2 for zero a linha será cinza; caso contrário, será branca. Aqui vale comentar uma coisa: no lugar do operado % poderíamos usar mod, e no lugar do operado == poderíamos usar eq. Então, viu como é fácil, poderoso e claro usar EL no lugar de scriptlets? Espero que daqui pra frente scriptlets sejam coisa do passado e você comece a usar JSTL e EL nas suas páginas JSP. Você perceberá como o seu código ficará mais legível e mais fácil de manter. Bom, pessoal, terminamos o que de mais importante existe no uso de JSTL e EL. Espero que você tenha aproveitado e entendido. 1. Quais são as duas maneiras de testar se uma coleção é vazia? 2. Veja o que acontece ao tentar usar uma expressão da seguinte forma: ${usuarioLogado.nome + 1} . Leitura complementar Para complementar seu aprendizado, leia sobre os comandos aprendidos nessa aula através das referências abaixo: • Resumo Hoje concluímos nossos estudos de JSTL e EL. Vimos que essas taglibs podem nos ajudar a manter nosso código JSP bem mais simples. No caso da aula de hoje, temos a taglib functions para manipulação de Strings e a linguagem de expressão para acesso a valores e expressões de uma forma mais simples. Com este conhecimento já é possível fazer bastante coisa de forma organizada e de fácil manutenção. E o melhor: usando o que o mercado espera de um profissional que programa para WEB, usando Java/J2EE. 1. Descreva com as suas palavras: a. O que pode ser feito com a taglib functions. b. Como a linguagem de expressões (EL) ajuda na escrita do código JSP. Desenvolvimento Web Aula 12 – Desenvolvendo uma livraria virtual – Parte 1 Até agora, você viu diversos conceitos e tecnologias para programação Web em Java. Nesta aula, você verá o desenvolvimento de uma aplicação utilizando o conhecimento já aprendido. Essa aplicação é um sistema para ser utilizado por uma livraria que venda seus livros pela Internet. Como atividade, você irá desenvolver uma aplicação similar àquela mostrada durante esta aula. Será uma aplicação de Locadora virtual de filmes, em que seus filmes podem ser locados pela internet. Apesar de serem sistemas simplificados em relação ao que acontece no mercado, estes são bastante úteis para desenvolver e fixar o raciocínio de programação Web usando as tecnologias apresentadas. Ao final desta aula você será capaz de: • Entender os primeiros passos que devem ser realizados antes de se começar a realizar a programação das interfaces Web: modelagem do negócio da aplicação. Modelagem do negócio O sistema a ser desenvolvido nesta aula é uma livraria on-line simplificada, porém, com recursos suficientes para demonstrar a capacidade da tecnologia de Servlets e de JSP para o desenvolvimento de programas Web. Esse exemplo de sistema foi baseado no Duke’s Bookstore Example, encontrado no The Java EE 5 Tutorial, o qual é fornecido gratuitamente pela empresa responsável pela linguagem Java. Para iniciarmos o desenvolvimento desse sistema, podemos começar com as classes que representam as regras de negócio da livraria. A primeira classe a ser definida é a Livro, a qual define as informações que descrevem um livro no sistema da livraria on-line. O código-fonte dessa classe é mostrado na Listagem 1. Note que a classe Livro possui atributos para representar um código identificador do livro (String idLivro, linha 4), título, nome dos autores, ano de publicação, preço, quantidade em estoque e uma crítica da obra (descrição, comentário sobre o livro). 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package livraria.negocio; public class Livro { private String idLivro; private String titulo; private String autores; private int ano; private double preco; private int quantidade; private String descricao; public Livro() { } public String getIdLivro() { return idLivro; } public void setIdLivro(String idLivro) { this.idLivro = idLivro; } public String getTitulo() { return titulo; } public void setTitulo(String titulo) { this.titulo = titulo; } // Demais métodos get e set foram omitidos } Listagem 1 - Classe que representa um Livro 1. Crie um projeto para o sistema de Livraria on-line e implemente a classe Livro, conforme apresentado na Listagem 1. 2. Crie um outro projeto para o sistema de Locadora virtual de filmes e modele uma classe para representar um filme. Os livros serão escolhidos pelo usuário e acondicionados em um carrinho de compras virtual. Dessa forma, precisamos modelar também uma classe para representar esse carrinho virtual e seu conteúdo. Como um carrinho de compras possui itens a serem comprados, com informação sobre o tipo do produto e a quantidade a ser comprada, primeiro modelamos a classe ItemCompra, como mostrado na Listagem 2. O atributo item armazena o livro escolhido e o atributo quantidade indica a quantidade desse livro a ser comprada. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package livraria.negocio; public class ItemCompra { private Livro item; private int quantidade; public ItemCompra(Livro prod) { item = prod; quantidade = 1; } public void incrementaQuantidade() { quantidade++; } public void decrementaQuantidade() { quantidade--; } public Livro getItem() { return item; } public int getQuantidade() { return quantidade; } public void setQuantidade(int quantity) { this.quantidade = quantity; } } Listagem 2 - Classe responsável por representar um item (produto, quantidade) do carrinho de compras. Com relação ao carrinho de compras, podemos modelá-lo através da classe CarrinhoCompras mostrada na Listagem 3. Você pode estar pensando que um carrinho de compras possui uma lista ou um conjunto de itens (ItemCompra) a serem comprados. Esse pensamento está correto. Entretanto, o conteúdo do carrinho de compras foi modelado como um mapeamento (interface Map, linha 6) de String em objetos do tipo ItemCompra. O uso de (Java Generics) é opcional, mas fortemente recomendado, já que garante que o tipo da chave do mapeamento seja String e que o valor associado a cada chave seja do tipo ItemCompra. Para maior esclarecimento sobre esse assunto, leia a referência sobre Java Generics indicada na leitura complementar. Essa decisão foi tomada porque as operações de adicionar ou de remover um livro do carrinho requer a busca no carrinho pelo objeto ItemCompra relacionado àquele livro. Isso pode ser visto nos métodos adicionar() e remover(), definidos respectivamente nas linhas 13 e 23. Sobre o primeiro método, ele recebe o livro escolhido pelo usuário (representado pelo parâmetro book) e realiza a busca para verificar se já existe um objeto ItemCompra para o livro recebido como parâmetro. Observe na linha 14 o uso do método containsKey() existente na classe Map (variável items). Esse método retorna true se existir no mapeamento uma entrada que mapeie o código do livro (book.getIdLivro()) no objeto ItemCompra associado a esse produto. Se esse for o caso, isso quer dizer que o cliente já tinha selecionado esse livro antes, então, simplesmente recuperamos o objeto ItemCompra (método items.get(book.getIdLivro()) associado ao código do livro sendo comprado e incrementamos sua quantidade no carrinho de compras. Caso contrário, criamos um novo objeto ItemCompra (linha 18) e o adicionamos no mapeamento (linha 19). Note que mapeamos o objeto utilizando o método put() do mapeamento (linha 19), em que mapeamos o código do livro (book.getIdLivro()) no objeto criado (novoItem). O uso do mapeamento na implementação do carrinho de compras facilita e torna mais eficiente a implementação das operações do carrinho, as quais poderiam ser feitas também através de listas ou conjuntos ao invés de mapeamentos. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package livraria.negocio; import java.util.*; public class CarrinhoCompras { Map itens; public CarrinhoCompras() { itens = new HashMap(); } public synchronized void adicionar(Livro book) { if (itens.containsKey(book.getIdLivro())) { ItemCompra item = itens.get(book.getIdLivro()); item.incrementaQuantidade(); } else { ItemCompra novoItem = new ItemCompra(book); itens.put(book.getIdLivro(), novoItem); } } public synchronized void remover(String idLivro) { if (itens.containsKey(idLivro)) { ItemCompra item = itens.get(idLivro); item.decrementaQuantidade(); if (item.getQuantidade() <= 0) { itens.remove(idLivro); } } } public synchronized List getItens() { List resultado = new ArrayList(); resultado.addAll(this.itens.values()); return resultado; } protected void finalize() throws Throwable { itens.clear(); } public synchronized int getNumeroItens() { int numeroItens = 0; for (ItemCompra item : getItens()) { numeroItens += item.getQuantidade(); } return numeroItens; } public synchronized double getTotal() { double total = 0.0; for (ItemCompra item : getItens()) { Livro book = item.getItem(); total = total + (item.getQuantidade() * book.getPreco()); } return total; } public void limpar() { itens.clear(); } } Listagem 3 - Classe que representa um carrinho de compras virtual Para o método remover(), na linha 23, ele só vai fazer algo caso exista uma entrada no mapeamento para o código do livro passado como parâmetro. Se esse for o caso, basta recuperar o objeto ItemCompra mapeado (linha 25) e decrementar em uma unidade a quantidade de livros a ser comprada (método decrementaQuantidade()). Por fim, caso a quantidade de livros restantes a ser comprada seja menor ou igual a zero, isso quer dizer que nenhum livro daquele tipo será comprado. Sendo assim, removemos a entrada do mapeamento relativa a esse livro (linha 28), fazendo com que esse item saia do carrinho de compras. É interessante destacar alguns outros métodos, como o getItens() da linha 34, o qual retorna o conteúdo do carrinho de compras, ou seja, todos os itens a serem comprados. Para isso, criamos uma nova lista de objetos ItemCompra e adicionamos nessa lista todo os itens existentes no carrinho de compras. Isso é feito através do método addAll(), o qual recebe uma coleção de objetos (conteúdo do carrinho) e adiciona todos esses elementos na lista criada. Outro método do carrinho de compras é o getTotal(), linha 52, o qual percorre todos os elementos do carrinho de compras e contabiliza o valor total a ser gasto com a compra desses produtos. Para isso, o método leva em conta a quantidade de livros a serem comprados e o preço unitário. A variável total é utilizada como acumulador (linha 56) durante o cálculo do valor total a ser gasto na compra dos livros selecionado. Por fim, vale a pena também ressaltar que o método limpar(), definido na linha 61, tem como objetivo remover todo os itens selecionados do carrinho de compra. Isso é feito acionando-se o método clear() da interface Map, que vai eliminar todas as entradas existentes no mapeamento. 1. Implemente o carrinho de compras da livraria virtual conforme mostrado. Crie um programa para testar as funcionalidades do carrinho de compras. Execute o programa e verifique que o comportamento da implementação está correto. 2. Implemente um carrinho de compras para a locadora virtual. Crie também um programa para testar as funcionalidades do carrinho de compras. Execute o programa e verifique que o comportamento da implementação está correto. Modelagem do armazenamento dos livros e do sistema Para modelar o armazenamento dos livros disponíveis na livraria virtual, iremos utilizar uma abordagem em memória. Isso porque o foco do curso é ensinar as tecnologias Web. Entretanto, é fortemente recomendado para os alunos que concluíram o módulo Banco de Dados, como atividade opcional, que usem banco de dados ao invés da abordagem em memória. Entretanto, essa implementação em banco de dados não pode interferir seu rendimento no curso, se possível sendo realizada apenas ao final deste módulo. Utilizaremos uma classe chamada Livraria para representar tanto as operações na livraria como o meio de armazenamento. A primeira parte da implementação dessa classe é mostrada na Listagem 4.1 e é uma simplificação do que um sistema desenvolvido para o mercado pode possuir. A nossa implementação possui uma lista de objetos do tipo Livro (linha 18), representando o estoque existente na livraria virtual. Como a nossa implementação é em memória, toda vez que o servidor é reiniciado, o estoque deve ser carregado na memória. Isso é feito pelo método popularLivros(), definido na linha 25. Esse método, executado logo quando o objeto Livraria é criado (linha 22), tem como objetivo criar alguns livros e inserir no estoque. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package livraria.negocio; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import livraria.negocio.excecoes.CompraException; import livraria.negocio.excecoes.LivroNaoEncontradoException; public class Livraria { private List estoqueLivros; public Livraria() { estoqueLivros = new ArrayList(); popularLivros(); } private void popularLivros() { Livro livro = new Livro(); livro.setId("0596005407"); livro.setAno(2008); livro.setTitulo("Head First Servlets and JSP"); livro.setDescricao("Livro sobre Servlets e JSP."); livro.setAutores("Bryan Basham, Kathy Sierra, Bert Bates"); livro.setQuantidade(10); livro.setPreco(200.5); estoqueLivros.add(livro); livro = new Livro(); livro.setId("9788573935721"); livro.setAno(2007); livro.setTitulo("Desenvolvendo Aplicações Web com JSP, Servlets, “ + “JavaServer Faces, Hibernate, EJB 3 Persistence e Ajax"); livro.setDescricao( "Livro sobre tecnologias usadas na programação Java para Web."); livro.setAutores("Edson Gonçalves"); livro.setQuantidade(10); livro.setPreco(110.9); estoqueLivros.add(livro); } } Listagem 4.1 – Implementação da classe Livraria – Parte 1 Além disso, a classe Livraria deve possuir métodos de consulta aos livros disponíveis. Esses métodos são vistos na Listagem 4.2. O método getLivros() retorna todos os livros disponíveis. Note que a implementação desse método faz uso da operação Collections.unmodifiableList(). Essa operação pega uma lista como parâmetro e retorna uma lista com os mesmos elementos, porém, seus métodos de modificação da lista (adicionar e remover) irão levantar uma exceção se executados. Isso é opcional, mas garante que apenas a lista original, guardada pela classe Livraria, poderá ser modificada. Essa garantia nos permite saber que apenas métodos da classe Livraria podem adicionar ou remover livros da lista, evitando que essas operações sejam executadas “sem querer” por outras partes do código que tiveram acesso à mesma pelo método getLivros(). Outro método de busca disponível é o getLivro(), o qual recebe um código e retorna o livro relacionado àquele código (linhas 48 a 50) ou levanta a exceção LivroNaoEncontradoException (linhas 53 a 56), caso não exista no estoque nenhum livro com o código passado. Essa exceção foi definida por nós e seu código é visto mais adiante, na Listagem 5. 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 public List getLivros() { return Collections.unmodifiableList(estoqueLivros); } public Livro getLivro(String idLivro) throws LivroNaoEncontradoException { Livro livroProcurado = null; for (Livro book : estoqueLivros) { if (book.getIdLivro().equals(idLivro)) { livroProcurado = book; } } if (livroProcurado == null) { throw new LivroNaoEncontradoException( "Não foi possível encontrar o livro: " + idLivro); } return livroProcurado; } } Listagem 4.2 - Implementação da classe Livraria – Parte 2 Por fim, a classe Livraria possui métodos relacionados à compra de livros. O primeiro deles é o método comprarLivros(), que recebe como parâmetro um carrinho de compras contendo os livros selecionados pelo usuário. Esse método é apresentado na Listagem 4.3, linha 61. Basicamente, os produtos contidos no carrinho são analisados (linhas 65 a 71) um por um, e, para cada livro, o método comprarLivro() é executado. Esse método é mostrado na linha 74 e, dado o código de um livro a quantidade a ser comprada, é responsável por: • verificar se o livro existe no estoque (linhas 77 a 81); • pegar a quantidade de livros em estoque (linha 83); • verificar se o estoque é suficiente para atender a quantidade de livros a ser comprada (linha 85), lançando uma exceção caso o estoque seja insuficiente (linhas 89 e 90). Esse caso pode ocorrer quando vários usuários tentam comprar o mesmo livro ao mesmo tempo, numa quantidade de estoque satisfatória para cada cliente individualmente, mas superior ao estoque no total dos vários usuários; • debitar do estoque a quantidade vendida do livro (linha 86 e 87). 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 public void comprarLivros(CarrinhoCompras carrinho) throws CompraException { Collection items = carrinho.getItens(); Iterator i = items.iterator(); while (i.hasNext()) { ItemCompra item = (ItemCompra) i.next(); Livro livro = (Livro) item.getItem(); String id = livro.getIdLivro(); int quantity = item.getQuantidade(); comprarLivro(id, quantity); } } public void comprarLivro(String idLivro, int qtdComprada) throws CompraException { Livro livroSelecionado; try { livroSelecionado = getLivro(idLivro); } catch (LivroNaoEncontradoException e) { throw new CompraException(e.getMessage()); } int qtdEstoque = livroSelecionado.getQuantidade(); if ((qtdEstoque - qtdComprada) >= 0) { int novaQtd = qtdEstoque - qtdComprada; livroSelecionado.setQuantidade(novaQtd); } else { throw new CompraException("Livro " + idLivro + " sem estoque suficiente."); } } public void fechar() { // liberaria conexões de banco de dados, se usasse } } Listagem 4.3 - Implementação da classe Livraria – Parte 3 As listagens 5 e 6 mostram o código das exceções criadas para representar possíveis erros durante a execução do sistema. O uso das mesmas é feito nos métodos já mostrados da classe Livraria. 01 02 03 04 05 06 07 08 09 10 package livraria.negocio.excecoes; public class LivroNaoEncontradoException extends Exception { public LivroNaoEncontradoException() { } public LivroNaoEncontradoException(String msg) { super(msg); } } Listagem 5 – Código da exceção criada para o erro de livro não encontrado. 01 02 03 04 05 06 07 08 09 10 package livraria.negocio.excecoes; public class CompraException extends Exception { public CompraException() { } public CompraException(String msg) { super(msg); } } Listagem 6 - Código da exceção criada para representar um erro na compra do livro 1. Implemente a classe Livraria conforme apresentado, bem como as exceções LivroNaoEncontradoException e CompraException. Crie um programa para testar as funcionalidades da classe Livraria. Execute o programa e verifique que o comportamento da implementação está correto. 2. Adicione mais 5 livros distintos sobre programação no método popularLivros() da classe Livraria. 3. Implemente uma classe chamada Locadora para implementar as operações de locar filmes e para armazenar os filmes disponíveis, de forma semelhante à classe Livraria. Crie um programa para testar as funcionalidades da classe criada. Execute o programa e verifique que o comportamento da implementação está correto. Por esta aula, paramos por aqui, mas na próxima aula continuamos com a implementação da Livraria virtual, focando na camada da Web. Até lá! Leitura complementar Para complementar seu entendimento sobre a programação em Java, analise os métodos disponibilizados pelas classes envolvidas no desenvolvimento da Livraria virtual. Isso pode ser feito observando-se a API do Java: JavaTM Platform Standard Edition, v 6.0 – API Specifications. Disponível em: . Acesso em: 27 out. 2010. Em especial, observe as classes Map, HashMap e Collections, encontradas no pacote java.util. Além disso, leia sobre programação genérica em Java (Java Generics): UM PASSEIO pelo Java 5: generics: parte 1. Disponível em: . Acesso em: 14 set. 2010. UM PASSEIO pelo Java 5: generics: parte 2. Disponível em: . Acesso em: 14 set. 2010. JavaTM Platform Standard Edition, v 6.0: generics. Disponível em: . Acesso em: 27 out. 2010. Resumo Nesta aula, você praticou a modelagem das classes de negócio de uma aplicação Web: a Livravia Vitual. Você criou observou e praticou a criação de classes que representam entidades (coisas) do negócio, ou seja, de uma livraria virtual: livros, carrinho de compras, a livraria em si e os erros que podem acontecer nas operações do negócio (exceções). Na próxima aula, continuaremos com o desenvolvimento das camadas necessárias para que a aplicação rode no ambiente da Web. Até lá! Descreva os passos que você seguiu para realizar a modelagem de negócio das aplicações Livraria vitual e Locadora vitual. Desenvolvimento Web Aula 13 – Desenvolvendo uma livraria virtual – Parte 2 Na aula anterior, você estudou a modelagem das classes de negócio do sistema de livraria online. Nesta aula, você continuará a estudar a modelagem desse sistema, agora focando na camada de código voltada para os detalhes de implementação Web. Você estudará o desenvolvimento de um Servlet controlador e das páginas de apresentação JSP da livraria vitual. Continuaremos com as atividades relacionadas à locadora virtual de filmes, então, você será o responsável por colocar a locadora funcionando na Web! Boa leitura! Ao final desta aula, você será capaz de: • entender o desenvolvimento da camada Web de sistemas como o da livraria virtual, o que inclui o desenvolvimento de Servlets controladores e de arquivos JSP de apresentação do conteúdo. Contexto dos Servlets Na aula anterior, você estudou a modelagem de diversas classes, como a classe Livraria, responsável por implementar as funcionalidades da livraria: manutenção do estoque e venda dos livros. Para que essa classe e suas operações fiquem disponíveis para a camada de apresentação Web (Servlets e arquivos JSP), é necessário disponibilizar o sistema de alguma forma. Como você já viu antes, vamos disponibilizar o sistema através do contexto dos Servlets. Isso é feito pela classe LivrariaContextListener, mostrada na Listagem 1. 01 package livraria.servlet; 02 03 import javax.servlet.ServletContext; 04 import javax.servlet.ServletContextEvent; 05 import javax.servlet.ServletContextListener; 06 07 import livraria.negocio.Livraria; 08 09 public final class LivrariaContextListener implements ServletContextListener { 10 11 public static final String SISTEMA_LIVRARIA = "sistemaLivraria"; 12 13 public void contextInitialized(ServletContextEvent event) { 14 ServletContext context = event.getServletContext(); 15 16 try { 17 Livraria livraria = new Livraria(); 18 context.setAttribute(SISTEMA_LIVRARIA, livraria); 19 } catch (Exception ex) { 20 System.out.println( 21 "O sistema de livraria não pode ser publicado no contexto: " 22 + ex.getMessage()); 23 } 24 } 25 26 public void contextDestroyed(ServletContextEvent event) { 27 ServletContext context = event.getServletContext(); 28 29 Livraria livraria = (Livraria) context.getAttribute(SISTEMA_LIVRARIA); 30 31 if (livraria != null) { 32 livraria.fechar(); 33 } 34 35 context.removeAttribute(SISTEMA_LIVRARIA); 36 } 37 } Listagem 1 - Classe que publica o sistema da livraria online através do contexto dos Servlets Como você pode notar, temos o método contextInitialized() (linha 13), que será executado no evento de inicialização do contexto dos Servlets. Esse método é executado apenas uma vez no servidor e ele instancia a classe Livraria, criando através da linha 17 o núcleo da nossa Livraria virtual, o qual é publicado através de um atributo de contexto de nome Sistema_Livraria, conforme instruções mostradas pelas linhas 11 e 18. O uso da constante SISTEMA_LIVRARIA evita que o texto Sistema_Livraria seja escrito em vários lugares (sujeito a erros). Além do método contextInitialized(), foi definido também o método contextDestroyed(), executado no momento em que o contexto dos Servlets está sendo destruído, sendo responsável por liberar recursos alocados. No caso, o método acessa o sistema (linha 29) e, se o mesmo tiver sido inicializado corretamente (não for nulo), o seu método fechar() é invocado. Esse método avisa ao sistema da livraria que qualquer recurso como, por exemplo, conexões de banco de dados, deve ser liberado, pois o sistema está sendo desligado. Por fim, a instrução da linha 35 remove o atributo SISTEMA_LIVRARIA do contexto. Para que a classe LivrariaContextListener seja executada de acordo com os eventos de criação e destruição do contexto dos Servlets, é necessário incluir a instrução ... no arquivo web.xml: livraria ... livraria.servlet.LivrariaContextListener ... onde “…” corresponde às demais instruções de configuração que devem ser colocadas no arquivo web.xml. 1. Implemente o LivrariaContextListener conforme apresentado. Crie um servlet de exemplo que ao ser executado acessa o contexto e escreve uma página de resposta indicando se o atributo do contexto de nome Sistema_Livraria existe (não é nulo). 2. Implemente um Listener de contexto para o sistema de locadora virtual desenvolvido como atividade na aula anterior. Faça um teste de acordo com o teste realizado para o LivrariaContextListener. Servlet controlador da livraria vitual Seguindo o padrão MVC (Model-View-Controler), utilizaremos um Servlet como componente controlador e arquivos JSP como os componentes responsáveis por montar as páginas de resposta ao usuário. O Servlet controlador da livraria virtual, de nome ServletControladorLivraria, é mostrado na Listagem 2. Esse Servlet implementa tanto o método doGet() (linha 16) como o método doPost() (linha 43), sendo que o doPost() apenas redireciona a execução para o método doGet(). Na execução do Servlet controlador, é feito o acesso ao sistema da livraria virtual (linha 20) através do atributo de contexto, conforme discutido anteriormente. Também é feito um acesso à sessão do usuário (linha 22). Lembre-se de que o método getSession() cria uma sessão, caso ela não exista. Em seguida, nas linhas 24 a 28, o carrinho de compras é acessado ou criado e armazenado como atributo da sessão de nome “cart”, caso ele ainda não exista. Por fim, as linhas 30 a 40 são responsáveis por processar e montar a resposta para as requisições dos clientes. Para facilitar esse trabalho, utilizaremos as seguintes estratégias. • As URL de acesso ao sistema não possuirão extensão. Isso pode ser visto no seguinte exemplo: https://localhost:8080/Livraria/livros/catalogo. • O caminho da URL será utilizado para descobrir qual o serviço a ser executado. Isso é feito na linha 30, onde o método getServletPath() da variável request é utilizado para recuperar parte da URL de acesso, no caso, a parte relativa ao caminho do Servlet: /livros/catalogo. Essa informação é armazenada na variável acaoSelecionada. • Dependendo da ação a ser executada (conteúdo da variável acaoSelecionada), uma determinada operação do sistema pode ser executada. Essas operações, no caso, serão apresentadas aos poucos e seu código será inserido logo após a linha 32. • O conteúdo da variável acaoSelecionada é utilizado também para definir o componente (arquivo JSP) responsável por montar a página de resposta. Isso é feito pela linha 34, onde usamos a convenção do nome e pasta onde se encontra o arquivo JSP de resposta igual ao conteúdo da variável acaoSelecionada (URL do Servlet) concatenada com a extensão .jsp. Dessa forma, o acesso à URL https://localhost:8080/Livraria/livros/catalogo poderá resultar na execução de uma operação do sistema identificada pelo final da URL (/livros/catalogo) e sua resposta será montada de acordo com o arquivo catalogo.jsp encontrado na pasta de nome livros. 01 package livraria.servlet; 02 03 import javax.servlet.http.HttpServlet; 04 import javax.servlet.http.HttpServletRequest; 05 import javax.servlet.http.HttpServletResponse; 06 import javax.servlet.http.HttpSession; 07 08 import livraria.negocio.CarrinhoCompras; 09 import livraria.negocio.Livraria; 10 import livraria.negocio.Livro; 11 import livraria.negocio.excecoes.CompraException; 12 import livraria.negocio.excecoes.LivroNaoEncontradoException; 13 14 public class ServletControladorLivraria extends HttpServlet { 15 16 public void doGet(HttpServletRequest request, HttpServletResponse response) { 17 String idLivro = null; 18 String limpar = null; 19 Livro livro = null; 20 Livraria livraria = (Livraria) getServletContext().getAttribute( 21 LivrariaContextListener.SISTEMA_LIVRARIA); 22 HttpSession session = request.getSession(); 23 24 CarrinhoCompras carrinho = (CarrinhoCompras) session.getAttribute("cart"); 25 if (carrinho == null) { 26 carrinho = new CarrinhoCompras(); 27 session.setAttribute("cart", carrinho); 28 } 29 30 String acaoSelecionada = request.getServletPath(); 31 32 // Aqui entram as operações do sistema 33 34 String tela = acaoSelecionada + ".jsp"; 35 36 try { 37 request.getRequestDispatcher(tela).forward(request, response); 38 } catch (Exception ex) { 39 ex.printStackTrace(); 40 } 41 } 42 43 public void doPost(HttpServletRequest request, HttpServletResponse response) { 44 doGet(request, response); 45 } 46 } Listagem 2 - Código-fonte do Servlet controlador da livraria virtual Para que esse Servlet possa funcionar, precisamos configurá-lo no arquivo web.xml. Esse código é visto a seguir. ... Controlador Controlador livraria.servlet.ServletControladorLivraria Controlador /livros/livraria ... A primeira parte dessa declaração faz com que o ServletControladorLivraria seja associado ao nome Controlador. Na segunda parte da declaração, temos a associação do caminho /livros/livraria ao Servlet de nome Controlador, ou seja, ao ServletControladorLivraria. Com isso, caso um acesso seja realizado ao sistema na forma de https://localhost:8080/Livraria/livros/livraria, então, o ServletControladorLivraria será executado e, de acordo com o código das linhas 34 a 40, o processamento da requisição será repassado para o arquivo /livros/livraria.jsp. Esse arquivo livraria.jsp será o arquivo responsável por montar a tela inicial do sistema, a qual é mostrada na Figura 1. O código JSP desse arquivo é mostrado em seguida, na Listagem 3. Ele começa com a inclusão da taglib c, na linha 1. Seu código inclui também o uso de uma imagem chamada livro.gif, a qual é impressa antes e depois do texto Minha Livraria Virtual. Para implementar essa página, você deve escolher uma imagem que achar adequada. Faça essa busca na internet, usando o engenho de busca que você achar melhor. Uma sugestão é usar o serviço de busca de imagens do google (https://images.google.com.br). Figura 1 - Tela inicial do sistema Livraria Virtual 01 <%@ taglib uri="https://java.sun.com/jsp/jstl/core" prefix="c" %> 02 03 04 05 06 07 08 09 Minha Livraria Virtual 10 11 12 13 14 15
16
17
  18

19 20 Minha Livraria Virtual 21 22

23
24 25
  26
27 28

Livraria Online

29 30 31 32

Iniciar compras 33 34
  35


36 37
Copyright © 2010 Livraria Virtual.
38 39 40 41 Listagem 3 - Código JSP do arquivo responsável por montar a tela inicial mostrada na Figura 1 Já na linha 30, temos o uso do marcador , o qual é responsável por montar a URL correta para o caminho /livros/catalogo. Por exemplo, se seu contexto Web tiver nome Livraria, o acesso a essa página deve ser https://localhost:8080/Livraria/livros/catalogo. Sendo assim, o valor atribuído a variável url criada pelo marcador terá valor igual a /Livraria/livros/catalogo. Isso torna o sistema independente do nome a ser utilizado no contexto Web. A URL armazenada na variável url é então utilizada na linha 32 com vistas a criar a referência para a página de compra de livros. Note o uso de "${url}?Add=". Isso fará com que a URL gerada seja, na verdade, igual a /Livraria/livros/catalogo?Add=. O uso desse parâmetro Add passado com valor vazio será visto mais adiante, quando você estudar o código do arquivo catalogo.jsp. Para que expressões como ${url} possam funcionar, é necessário configurar o arquivo web.xml colocando o seguinte código: ... livraria /livros/* false false false Essa instrução indica que os arquivos JSP contidos na pasta livros estão com a linguagem de expressões habilitadas (false). 1. Implemente o Servlet controlador da Livraria virtual, bem como sua página de entrada, conforme apresentado. Execute e verifique que o comportamento do código está correto. 2. Escreva um Servlet controlador e a página de entrada do sistema para o seu projeto de Locadora virtual. Execute o sistema e verifique se o seu Servlet e a página inicial da Locadora virtual estão funcionando corretamente. Catálogo de livros Agora que você já viu o código do Servlet controlador, vamos passar para o desenvolvimento da próxima tela do sistema, a do catálogo de livros. Essa tela é mostrada na Figura 2 e ela apresenta a lista de livros disponíveis para venda, além de informações sobre o carrinho de compras do usuário. Figura 2 - Tela de catálogo de livros quando o carrinho de compras está vazio Para implementar essa tela, criamos na pasta livros o arquivo catalogo.jsp. Note que a URL de acesso à pagina mostrada na Figura 1 termina com /livros/catalogo?Add=, por isso, o nome do nosso arquivo tem que ser catalogo.jsp e o mesmo tem que estar na pasta livros. Lembrando que toda nova URL criada para o sistema tem que ser adicionada no arquivo web.xml, como mostraremos a seguir, para que ela seja processada pelo Servlet controlador. ... Controlador /livros/catalogo ... Vamos agora estudar o conteúdo desse arquivo, o qual será apresentado aos poucos, iniciando-se pela Listagem 4.1. Nas primeiras duas linhas do arquivo, vemos a declaração das taglibs c e fmt. Em seguida, temos a montagem da parte superior da tela, a qual é a mesma da tela inicial do sistema e de todas as outras. Isso quer dizer que estamos duplicando código, não é mesmo? Duplicar código significa aumentar desnecessariamente o tamanho do código-fonte, além de dificultar a manutenção e entendimento do sistema. Mas, não se preocupe, na próxima aula, depois de você entender bem o código da tela de catálogo, veremos como tratar esse problema! 01 <%@ taglib uri="https://java.sun.com/jsp/jstl/core" prefix="c" %> 02 <%@ taglib uri="https://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> 03 04 05 06 07 Minha Livraria Virtual 08 09 10 11 12
13
14
  15

16 17 Minha Livraria Virtual 18 19

20
21 22
  23
24 Listagem 4.1 - Código-fonte do arquivo catalogo.jsp – Parte 1 O restante do conteúdo do arquivo catalogo.jsp irá acessar o sistema da livraria virtual para fazer consulta a livros disponíveis etc. Essas consultas exigirão o uso da classe LivrariaBean, mostrada na Listagem 5. Essa classe representa um bean que irá implementar as operações necessárias para a execução do arquivo JSP. Podemos ver que essa classe tem como atributo o próprio sistema da livraria (atributo sistema, do tipo Livraria) e um atributo idLivro do tipo String. O primeiro atributo receberá, como veremos adiante, o objeto Livraria armazenado no contexto dos Servlets. Já o segundo atributo representa o identificador do livro escolhido pelos usuários em operações como adicionar um livro ao carrinho. Observe nessa classe as operações dos métodos getLivro() e getLivros(). O primeiro retorna o objeto do tipo Livro que possui identificador igual ao do atributo idLivro. Já o segundo método retorna todos os livros em estoque na livraria. Por fim, temos o método comprarLivros(), que realiza a compra dos livros existentes no carrinho de compras recebido como parâmetro. Esses métodos fazem uma “ponte”, ou seja, uma interligação entre o código dos arquivos JSP e o sistema (classe Livraria). Veremos seu uso à medida que apresentamos os códigos JSP. 01 package livraria.negocio; 02 03 import java.util.List; 04 05 import livraria.negocio.excecoes.CompraException; 06 import livraria.negocio.excecoes.LivroNaoEncontradoException; 07 08 public class LivrariaBean { 09 private Livraria sistema = null; 10 private String idLivro = "0"; 11 12 public LivrariaBean() { 13 } 14 15 public void setIdLivro(String id) { 16 this.idLivro = id; 17 } 18 19 public void setSistema(Livraria livraria) { 20 this.sistema = livraria; 21 } 22 23 public Livro getLivro() throws LivroNaoEncontradoException { 24 return (Livro) sistema.getLivro(idLivro); 25 } 26 27 public List getLivros() { 28 return sistema.getLivros(); 29 } 30 31 public void comprarLivros(CarrinhoCompras cart) throws CompraException { 32 sistema.comprarLivros(cart); 33 } 34 } Listagem 5 - Código da classe LivrariaBean Voltando para o arquivo catalogo.jsp, a segunda parte de seu código é mostrada na Listagem 4.2. Observe a declaração de uso do LivrariaBean nas linhas 25 a 27. O marcador indica a classe do bean a ser instanciado (já que o escopo é page, o bean será instanciado em cada acesso). O identificador do bean (atributo id) é livrariaBean, o qual é utilizado no restante do arquivo para referenciar o bean. Sua propriedade de nome sistema é configurada com o valor da expressão ${sistemaLivraria}, o qual irá retornar o atributo do contexto dos Servlets de nome “sistemaLivraria”. Relembrando, esse atributo é criado pela classe LivrariaContextListener mostrada na aula anterior e referencia a instância criada para o sistema (objeto Livraria). 25 26 27 28 Listagem 4.2 - Código-fonte do arquivo catalogo.jsp – Parte 2 Por questões didáticas, as linhas 29 a 36 desse arquivo serão apresentadas no final, tendo respeito ao parâmetro da requisição de nome Add. Com relação ao código mostrado na Listagem 4.3, ele é responsável por mostrar o conteúdo do carrinho de compras. Usamos um marcador para simular uma instrução if-then-else. Na linha 38, é testado se o atributo numeroItens do objeto carrinho de compras (propriedade cart da sessão) possui valor maior que zero, ou seja, testa se existe pelo menos um item no carrinho de compras. Caso esse teste seja satisfeito (verdadeiro), o conteúdo das linhas 39 a 45 serão executados. Um link de nome url será criado com valor /livros/mostrarCarrinho, passando os parâmetros limpar e remover com valor igual a 0. Esse link levará a página que apresenta o conteúdo do carrinho de compras (linha 43), a qual não será vista nesta aula. Já as linhas 44 e 45 criam um link para a página de compra dos itens encontrados no carrinho. Essa página também será vista em outra aula. Caso não existam itens no carrinho de compras, ou seja, o atributo numeroItens do carrinho de compras seja igual a 0, a mensagem “Seu carrinho de compras está vazio.” será apresentada, conforme mostrado nas linhas 47 a 49. 37 38 39 40 41 42 43

Ver carrinho de compras    44 45 Finalizar compras

46
47 48 Seu carrinho de compras está vazio. 49 50
51 Listagem 4.3 - Código-fonte do arquivo catalogo.jsp – Parte 3 A próxima parte do código, mostrado na Listagem 4.4, é responsável por mostrar a lista de livros disponíveis para compra. Para cada livro, serão mostradas informações e links para se visualizar os detalhes do livro e para se adicionar o livro no carrinho de compras. Para isso, usamos o marcador visto na linha 58. Observe que a coleção de objetos utilizada no comando for é determinada pela expressão ${livrariaBean.livros}, ou seja, pelo retorno do método getLivros() do objeto livrariaBean. Cada objeto dessa coleção será armazenado na variável de nome livro e utilizado em comandos como o da linha 60, que pega o atributo idLivro e armazena seu valor em uma variável local também chamada de idLivro. A criação de um link para a página de apresentação dos detalhes do livro é mostrada nas linhas 62 a 64, adicionando como parâmetro a variável idLivro. A linha 65 é responsável por apresentar o título do livro sendo apresentado pelo marcador . Já na linha 68 temos o uso do marcador para apresentar o preço do livro como moeda. Isso fará a inclusão do símbolo $ antes do número e o uso de duas casas decimais para apresentação do valor. 52
  53
  54

Livros disponíveis para compra:

55
56 57 58 59 60 61 67 71 79 80 81 84 85 86 87
62 63 64 65 ${livro.titulo}  66 68 69   70 72 73 74 75

76  Adicionar livro ao carrinho  77 78

82   ${livro.autores}  83
88
89 Listagem 4.4 - Código-fonte do arquivo catalogo.jsp – Parte 4 Na linha 72, temos a criação de uma URL para a própria página de catálogo, passando o parâmetro Add com valor igual ao identificador do livro (idLivro) que o usuário queira adicionar ao carrinho. Muito bem, finalmente chegamos ao uso do parâmetro Add. Ele é utilizado para indicar o livro que o usuário quer adicionar ao carrinho de compras. Essa operação é realizada adicionando-se o seguinte trecho de código ao Servlet controlador, mais especificamente logo após a linha 32 da classe ServletControladorLivraria mostrada na Listagem 2. if (acaoSelecionada.equals("/livros/catalogo")) { idLivro = request.getParameter("Add"); if (!idLivro.equals("")) { try { livro = livraria.getLivro(idLivro); carrinho.adicionar(livro); } catch (LivroNaoEncontradoException ex) { // isso não deve acontecer } } } Esse código verifica se a ação selecionada (representada pela parte final da URL acessada) é igual a “/livros/catalogo” (parâmetros da requisição não entram aqui). Sendo o caso, pega-se o valor do parâmetros Add e, se ele não for vazio, busca-se o livro através do objeto livraria e adiciona-se o mesmo no carrinho de compras. A busca do livro pode gerar uma exceção, o que não deve ocorrer normalmente, pois o código do livro é escrito no link de forma automática pelo sistema. Em caso de acontecer a exceção devido a um acesso indevido por um hacker, por exemplo, a exceção fará com que a operação de adição do livro no carrinho não ocorra, porém, sem comprometer a geração da página de resposta. Caso a operação de adição do livro ao carrinho de compras seja executada com sucesso, o arquivo catalogo.jsp será novamente carregado e, em especial, teremos também a execução do código mostrado na Listagem 4.5. Esse código representa as linhas do arquivo catalogo.jsp que não foram mostradas antes. Como você pode observar, testa-se os parâmetros Add para ver se ele está vazio. Se não estiver, é porque a operação de adicionar um livro ao carrinho foi executada, compreende? Se existe um código de livro no parâmetro Add, então, o ServletControladorLivraria, em sua execução, adicionou esse livro ao carrinho de compras. Esse trecho de código é então utilizado para apresentar uma mensagem confirmando a execução dessa operação, como mostrado pela Figura 3 (ver mensagem em vermelho). 29 30 31 32 33

34 Você adicionou o livro ${livroAdicionado.titulo} ao seu carrinho de compras.

35
36 Listagem 4.5 - Código-fonte do arquivo catalogo.jsp responsável por mostrar o livro que acabou de ser adicionado ao carrinho de compras. Figura 3 - Tela de catálogo recarregada após usuário clicar no link de adicionar livro ao carrinho. Por fim, temos o final do código do arquivo catalogo.jsp mostrado na Listagem 4.6. Esse trecho de código é responsável por escrever um rodapé com uma mensagem de direitos autorais, a mesma que é mostrada na página inicial do sistema e que também será mostrada nas demais páginas do sistema. Na próxima aula, veremos como evitar que esse código seja duplicado em todas as páginas. Imagine ter que alterar o copyright para 2011 em todas as páginas do sistema! Seria bem melhor ter que fazer isso em um só lugar, não acha? 90
  91
92
Copyright © 2010 Livraria Virtual.
93 94 Listagem 4.6 - Código-fonte do arquivo catalogo.jsp – Parte 5. 1. Implemente as classes e o arquivo JSP apresentado para o sistema da Livraria virtual. Execute e verifique se o seu comportamento está de acordo com o esperado. Lembre-se de que os links de ver detalhes de um livro, ver carrinho de compras e finalizar compras ainda não estarão funcionando! 2. Realize uma implementação para o seu projeto de Locadora virtual para apresentação de um catálogo de livros, de forma similar ao realizado para a Livraria virtual. 3. Veja o código-fonte das páginas HTML geradas e identifique que trechos de código são gerados dinamicamente e por quais comandos JSP. Leitura complementar Para complementar a fixação do conhecimento, faça uma breve leitura das aulas anteriores sobre JSP, sua linguagem de expressões e JSTL. Verifique também artigos na internet sobre esse assunto, como, por exemplo: JAVASERVER Pages - Syntax Reference. Disponível em: . Acesso em: 27 out. 2010. Resumo Nesta aula, você estudou o papel do Servlet controlador no desenvolvimento do sistema da Livraria virtual. Uma estratégia foi montada para que o nome das URLs do sistema tenha relação com o nome dos arquivos JSP, facilitando assim o desenvolvimento do sistema. Você também estudou a aplicação dos marcadores JSP e do bean LivrariaBean no desenvolvimento da página de catálogo de produtos. Na próxima aula, continuaremos a adicionar funcionalidades ao sistema e apresentar o uso das tecnologias aprendidas até agora neste curso. Até lá! 1. Descreva como você implementou o modelo Model-View-Controller, em especial: a. o papel do Servlet controlador no desenvolvimento dos sistemas como o da Locadora virtual; b. o papel dos arquivos JSP; c. o papel de beans como o de nome LivrariaBean. Desenvolvimento Web Aula 14 – Desenvolvendo uma livraria virtual – Parte 3 Olá, meu amigo, na aula anterior você estudou a modelagem das classes relacionadas ao Servlet controlador da Livraria virtual e de alguns de seus arquivos JSP, em especial, a da tela inicial e a da tela de catálogo de livros. Você viu que a implementação mostrada possuía uma duplicação de código nos arquivos JSP, não é? Duplicação de código normalmente é uma coisa ruim e que pode ser evitada ou reduzida, e o caso específico de duplicação de código nos arquivos JSP da Livraria virtual será tema de análise desta aula. Além disso, nesta aula você ainda estudará a implementação de uma outra funcionalidade do sistema da livraria virtual e será requisitado a implementá-la também no projeto da Locadora virtual. Desejo-lhe uma boa aula! Ao final da aula, você será capaz de: • entender a importância de reusar código em arquivos JSP; • saber como adicionar cabeçalhos e rodapés automaticamente a diferentes arquivos JSP. Promovendo o reuso de cabeçalhos e rodapés de arquivos JSP Na aula anterior, você observou que os arquivos JSP responsáveis por montar as telas do sistema da Livraria virtual possuem uma espécie de cabeçalho e rodapé em comum. Se observarmos bem, vamos notar que as páginas iniciam com a declaração dos taglibs a serem utilizados, que no caso são: <%@ taglib uri="https://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="https://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> Você pode estar pensando agora que, na verdade, nem todos os arquivos JSP irão usar a taglib fmt, por exemplo. Isso é verdade, inclusive, quando olhamos o código-fonte do arquivo livraria.jsp, responsável por montar a tela de entrada do sistema. Entretanto, não existe um efeito negativo maior em se colocar a declaração de uma taglib que não é utilizada no arquivo. Dessa forma, vamos considerar a situação onde todas as páginas irão conter a declaração das duas taglibs, ok? Além da declaração das taglibs, todas as páginas possuem um código inicial HTML em comum, mostrado a seguir: Minha Livraria Virtual


 

Minha Livraria Virtual


 
Esse código é responsável por montar a parte superior da tela, como mostrado na Figura 1. Figura 1 – Cabeçalho utilizado em todas as telas da Livraria Virtual Além disso, todas as páginas do sistema têm uma referência de direitos autorais da Livraria Virtual, como mostrado pela Figura 2. Figura 2 - Rodapé utilizado em todas as páginas do sistema O código responsável pela geração desse rodapé foi visto na aula anterior e é replicado a seguir:
 
Copyright © 2010 Livraria Virtual.
Note que, embora o conteúdo replicado não seja muito grande, a quantidade de arquivos que o sistema pode ter pode ser razoavelmente grande, ou pelo menos suficiente para ocorrerem problemas durante as etapas de manutenção do sistema. Por exemplo, imagine que você tenha que atualizar alguma informação, como mudar de 2010 para 2010-2011 o período de copyright mostrado no rodapé das páginas. Todas as páginas teriam que ser editadas para se realizar essa alteração. Além do trabalho de ter que abrir todas as páginas e editar a mesma alteração, você ainda corre o risco de esquecer de alterar algumas páginas e a informação de copyright ser mostrada corretamente em algumas páginas e erroneamente em outras. Nesse caso, as consequências dessa inconsistência de informação não seriam graves, mas imagine se a informação a ser alterada fosse o meio de contato com a empresa (telefone, endereço etc.). Alguns clientes poderiam visualizar e utilizar informação de contato inválida, levando a perda de clientes, oportunidades de negócio, vendas etc. Agora que você já visualizou bem o problema e as suas consequências, vamos ver como podemos eliminar a duplicação de código mostrada. De fato, você tem pelo menos duas alternativas, sendo que essas duas serão comentadas agora e consideram a criação de um arquivo de cabeçalho, para inclusão do código que aparece no início dos arquivos JSP, e de um arquivo de rodapé, contendo a parte do código final a ser incluída nos arquivos JSP do sistema. O código-fonte desses dois arquivos são mostrados na Listagem 1 e 2. Como você pode notar, o conteúdo desses arquivos é exatamente igual ao conteúdo que é replicado em todos os arquivos JSP. Pode até parecer estranho para você ter um arquivo JSP que abre marcadores e sem os fechar, mas isso fará sentido em seguida. 01: <%@ taglib uri="https://java.sun.com/jsp/jstl/core" prefix="c" %> 02: <%@ taglib uri="https://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> 03: 04: 05: 06: 07: Minha Livraria Virtual 08: 09: 10: 11: 12:
13:
14:
  15:

16: 17: Minha Livraria Virtual 18: 19:

20:
21: 22:
  23:
Listagem 1 – Código-fonte do arquivo cabecalho.jsp 01:
  02:
03:
Copyright © 2010 Livraria Virtual.
04: 05: Listagem 2 – Código-fonte do arquivo rodape.jsp Agora que temos os arquivos de cabeçalho e rodapé criados, esse conteúdo precisa ser removido dos arquivos JSP anteriormente criados, ou seja, dos arquivos livraria.jsp e catalogo.jsp. Muito cuidado ao fazer isso, ok? Não vá remover código a mais ou a menos! Deve ser removida apenas a parte inicial e final desses arquivos, cujo conteúdo já está contemplado pelos arquivos de cabecalho.jsp e rodape.jsp. Muito bem, o próximo passo agora é fazer com que o conteúdo do arquivo cabecalho.jsp seja adicionado no início de todos os outros arquivos JSP do sistema, e que o conteúdo do arquivo rodape.jsp seja adicionado no final desses arquivos. Uma primeira forma de realizar isso é utilizar a diretiva de inclusão de conteúdo <%@ include %> em arquivos JSP. Algo como ter que colocar a seguinte expressão em todos os demais arquivos JSP, onde “. . .“ representa o conteúdo específico de cada arquivo: <%@ include file="cabecalho.jsp" %> . . . <%@ include file="rodape.jsp" %> O uso dessas instruções é responsável por incluir no arquivo JSP o conteúdo de cabecalho.jsp no início do arquivo e o conteúdo de rodape.jsp no final do arquivo. Apesar de ser uma boa solução, essa alternativa demanda cuidado, pois precisamos colocar essas linhas em todos os arquivos. Ainda corremos o risco de esquecer de colocar essa linha em alguns arquivos. Como a ideia é colocar o cabeçalho e o rodapé em todas as páginas, você pode utilizar uma forma de incluir esse conteúdo em todos os arquivos JSP e de forma automática! Para isso, primeiro vamos colocar os arquivos criados em uma pasta chamada modelos. Depois, vamos renomear a extensão dos dois arquivos de .jsp para para .jspf. A extensão .jspf quer dizer JSP fragment, ou seja, fragmento JSP. Essas duas mudanças não são obrigatórias, mas boas práticas que vão facilitar o que iremos fazer a seguir. Bom, para fazer com que os arquivos cabecalho.jspf e rodape.jspf sejam adicionados a todos os arquivos JSP, basta alterarmos o seguinte trecho de código do arquivo web.xml: livraria /livros/* false false false mudando para o seguinte código: livraria /livros/* false false false /modelos/cabecalho.jspf /modelos/rodape.jspf Com isso, os arquivos JSP que se encontram na pasta livros (na verdade, cuja URL inicia-se com /livros/ ) terão inclusos no início do arquivo o conteúdo do arquivo cabecalho.jspf (/modelos/cabecalho.jspf) e no seu final o conteúdo do arquivo rodape.jspf (/modelos/rodape.jspf). Essa inclusão é feita de forma automática, então, basta colocar um arquivo JSP na pasta livros para que ele passe a ter o referido cabeçalho e rodapé. Bem simples, não é? 1. No sistema da Livraria Virtual, implemente o reuso de código do cabeçalho e do rodapé. Execute e verifique o funcionamento correto das telas inicial e de catálogo de livros. 2. Implemente um reuso de código similar no seu projeto da Locadora Virtual. Demais funcionalidades da Livraria Virtual No restante desta aula e na próxima, você irá estudar a implementação das demais funcionalidades da Livraria Virtual. Lembrando, claro, que um sistema real precisa ter muitas outras funcionalidades além das mostradas aqui! Cada funcionalidade (ou tela) nova do sistema exige uma nova URL, e seu mapeamento no Servlet controlador deve ser colocado no arquivo web.xml. No código a seguir, você pode ver as URLs que serão desenvolvidas no restante desta aula. ... Controlador /livros/detalhesLivro Controlador /livros/mostrarCarrinho Controlador /livros/comprar Controlador /livros/reciboCompra ... As funcionalidades que iremos ver estão relacionadas a: • ver detalhes de um determinado livro; • mostrar e alterar o conteúdo do carrinho de compras; • confirmar a compra; • visualizar o recibo. Os links para a maioria das funcionalidades já se encontra na tela de catálogo (ver Figura 3) e a implementação de cada uma dessas implementações será mostrada nas próximas seções desta e da próxima aula! Figura 3 - Tela de catálogo, possui link para detalhar livro 1. Altere o arquivo web.xml colocando o conteúdo mostrado, de forma a habilitar as URLs das funcionalidades a serem implementadas. 2. Considerando as funcionalidades que iremos trabalhar no sistema da Livraria Virtual, crie URLs para essas funcionalidades no contexto do seu projeto de Locadora Virtual e configure-as no arquivo web.xml da Locadora Virtual. Detalhando o conteúdo de um livro Na página de catálogo de livros, existe um link de visualizar detalhes de cada livro, correto? Esse link é a URL /livros/detalhesLivro e é acionado ao se clicar no nome do livro desejado (ver Figura 3). Por exemplo, ao acessar o link do primeiro livro mostrado na Figura 3, a tela da Figura 4 será apresentada, mostrando informações sobre o livro selecionado. Figura 4: tela mostrando informações sobre o livro selecionado A implementação dessa funcionalidade é relativamente simples, já que na classe livraria.negocio.LivrariaBean existe o método getLivro() que, dado o código do livro, retorna o livro se ele existir no estoque. Para você relembrar o código relativo a esse método, ele é mostrado novamente a seguir. . . . public class LivrariaBean { private String idLivro = "0"; public void setIdLivro(String id) { this.idLivro = id; } public Livro getLivro() throws LivroNaoEncontradoException { return (Livro) sistema.getLivro(idLivro); } . . . } No caso da implementação da funcionalidade detalhar livro, esse bean é utilizado para, dado o código de um livro (seu atributo idLivro), realizar a consulta no estoque por esse livro (método getLivro()). Vamos ao conteúdo do arquivo detalhesLivro.jsp, então. Primeiro, segue o código para que possamos utilizar o LivrariaBean. Esse código é o mesmo encontrado no arquivo catalogo.jsp. Em seguida, precisamos verificar se o parâmetro idLivro foi passado. Isso é feito pelo código a seguir, através da instrução . Caso o valor de idLivro não seja vazio, criamos uma variável local de nome id com o valor igual ao do parâmetro idLivro (uso do ). Depois disso, utilizamos essa variável para configurar o atributo idLivro do objeto LivrariaBean (uso do ). Por fim, o livro retornado pelo método getLivro de LivrariaBean (expressão ${livrariaBean.livro}) é armazenado em uma variável local de nome livro, criada por outra instrução Muito bem, agora estamos com uma variável contendo o livro que queremos detalhar na página. Isso é feito através do código mostrado a seguir. Apresentamos o título do livro através da expressão ${livro.titulo}. De forma similar, apresentamos os autores, ano de publicação, descrição e preço do livro. Para apresentação do preço, usamos o formato de dinheiro através do marcador .

${livro.titulo}

 Autoria de ${livro.autores}   (${livro.ano})
 

Descrição

${livro.descricao}

Preço:

Já que o usuário está vendo os detalhes de um livro, imagina-se que ele está interessado em comprar. Dessa forma, adicionamos o código a seguir para criar uma URL chamando a página de catálogo e passando o parâmetro Add com o código do livro sendo visualizado. Lembrando o que foi discutido na aula anterior, se o usuário clicar nesse link, o livro será adicionado ao carrinho, não é mesmo?

Adicionar ao carrinho      Esse foi o código a ser executado se o código do livro for passado corretamente (ver fechamento com o ). Vamos agora coloca apenas mais uma URL, que é a de voltar à página de catálogo, sem adicionar nenhum livro ao carrinho (parâmetro Add é vazio). Continuar comprando

Só reforçando, todas as páginas JSP irão ter que incluir o cabeçalho e rodapé contidos nos arquivos cabecalho.jsp e rodape.jsp. 1. Implemente a página de detalhes de livro conforme explicado. Execute a Livraria Virtual e exercite essa funcionalidade, verificando que sua implementação está funcionando corretamente. 2. Implemente essa funcionalidade de detalhar produto no contexto da Locadora Virtual, executando depois o sistema e verificando que sua implementação está funcionando corretamente. 3. Para complementar a fixação do conhecimento, faça uma busca na internet sobre a configuração dos cabeçalhos e rodapés. A pesquisa por palavras-chave como JSP, prelude e coda podem ajudar a trazer resultados mais relevantes nos principais engenhos de busca da internet. Resumo Nesta aula, você estudou como eliminar a duplicação de código que se repete em várias páginas JSP. Você pode fazer isso criando um arquivo para guardar o conteúdo do cabeçalho e outro para guardar o conteúdo do rodapé. No arquivo web.xml, é possível então indicar quais arquivos JSP devem ter esses conteúdos adicionados. Fizemos isso para todos os arquivos JSP armazenados na pasta livros, não foi? Por fim, você estudou a implementação da funcionalidade que mostra os detalhes de um determinado livro. Na próxima aula, finalizaremos o sistema de Livraria Virtual adicionando algumas últimas funcionalidades. Até lá! 1. Descreva quais os problemas gerados pela duplicação de código em arquivos JSP. 2. Descreva formas de se evitar essa duplicação, em particular, a duplicação de conteúdos de cabeçalho e rodapé. Desenvolvimento Web Aula 15 – Desenvolvendo uma livraria virtual – parte 4 Professores autores Olá caro aluno, estamos quase finalizando nosso exemplo de aplicação em Servlet e JSP, a Locadora Virtual. Nesta aula, você estudará a implementação das demais funcionalidades que iremos implementar. Desejo-lhe uma boa aula! Ao final desta aula, você será capaz de: • saber quando e como usar as tecnologias Servlet e JSP; • compreender como implementar a funcionalidade de mostrar carrinho e de efetuar a compra dos livros. Funcionalidades relativas ao carrinho de compras Muito bem, você já estudou quase toda a implementação do sistema de Livraria Virtual que estamos propondo. Para terminarmos, falta apenas você estudar a implementação das funcionalidades relacionadas ao carrinho de compras: • mostrar o conteúdo do carrinho de compras; • realizar a compra e visualizar o recibo. Lembrando que na aula anterior você já viu a configuração do arquivo web.xml para essas funcionalidades. Basicamente, o seguinte trecho de código foi colocado no arquivo, indicando as URLs que serão utilizadas para acionar essas funcionalidades: Controlador /livros/mostrarCarrinho Controlador /livros/comprar Controlador /livros/reciboCompra 1. Verifique se a configuração do arquivo web.xml de seu projeto Livraria Virtual contém a configuração para as funcionalidades citadas. 2. Verifique se a configuração do arquivo web.xml de seu projeto de Locadora Virtual contém a configuração para as funcionalidades citadas. Mostrar conteúdo do carrinho Vamos começar com a implementação da funcionalidade de apresentação do conteúdo do carrinho de compras. Considere a tela apresentada pela Figura 1. Dado que foi colocado livros no carrinho de compras, podemos agora acessar o link “Ver carrinho de compras” para visualizar o conteúdo do carrinho. A tela que apresenta o conteúdo do carrinho de compras é mostrada na Figura 1. Figura 1 - Tela do sistema após adicionar um livro ao carrinho de compras Para implementar a tela que mostra o conteúdo do carrinho, você precisa criar o arquivo mostrarCarrinho.jsp na pasta livros. Vamos estudar agora o conteúdo desse arquivo, começando pela declaração do LivrariaBean: Esse código é o mesmo utilizado na página de catálogo, servindo para criar e configurar o bean LivrariaBean. Já para mostrar o conteúdo do carrinho, primeiro vamos confirmar que o carrinho está realmente com algum conteúdo. Esse teste é feito no código abaixo (), mostrando inclusive a quantidade de itens existente no carrinho (${sessionScope.cart.numeroItens}). Note que a decisão sobre o uso do singular (1 livro) ou plural (ex: 2 livros) também é feita usando o comando . Quantidade de itens do carrinho: ${sessionScope.cart.numeroItens} livro. livros. Em seguida, montamos uma tabela com um cabeçalho indicando as informações a serem apresentadas:
  Depois disso, devemos apresentar os itens do carrinho, certo? Isso é feito usando uma instrução para navegar nos itens do carrinho (objetos do tipo ItemCompra). Para cada item de compra, o livro é pego e armazenado. Informações como título e preço são apresentadas. Um link para visualizar os detalhes de cada livro e outro para removê-lo do carrinho também estão sendo criados usando-se a instrução . Além disso, temos que mostrar o total do valor da compra. Lembre-se de que o carrinho de compras tem um método chamado getTotal(), para isso (${sessionScope.cart.total}):
Quantidade Título Preço
${itemCompra.quantidade} ${livro.titulo}   Remover

Subtotal

 

Por fim, devemos criar os links para Continuar comprando, Finalizar a compra e de Esvaziar o carrinho de compras: Continuar comprando    Finalizar compra    Esvaziar carrinho Para que as operações de remover e de limpar possam funcionar, você deve alterar o código do Servlet controlador para considerar essas ações, através do código mostrado a seguir. Note que se a URL /livros/mostrarCarrinho receber o id de um livro como parâmetro, o mesmo será removido do carrinho. Além disso, se o parâmetro limpar tiver valor igual a limpar, o carrinho será esvaziado. Isso está consistente com o código JSP que acabamos de ver. Em caso de dúvida, olhe novamente o código JSP mostrado anteriormente para verificar isso. else if (acaoSelecionada.equals("/livros/mostrarCarrinho")) { idLivro = request.getParameter("remover"); if (idLivro != null) { carrinho.remover(idLivro); } limpar = request.getParameter("limpar"); if ((limpar != null) && limpar.equals("limpar")) { carrinho.limpar(); } } Vamos aproveitar e adicionar algumas mensagens no sistema? Logo depois da declaração de uso do bean LivrariaBean, podemos colocar o seguinte código: O carrinho de compras foi esvaziado!
 
O seguinte livro foi removido do carrinho: ${livroRemovido.titulo}.
 
Isso fará com que uma mensagem de confirmação seja apresentada nos casos de esvaziar ou de remover um livro do carrinho de compras. Olhe atentamente ao código JSP mostrado e observe o uso das instruções JSP responsáveis pela apresentação dessas mensagens. As Figuras 2, 3 e 4 ilustram isso. Figura 2 - Carrinho de compras com dois livros Figura 3 - Carrinho de compras com um dos livros removido Figura 4 - Carrinho de compras esvaziado 1. No sistema de Livraria Virtual, implemente a funcionalidade de mostrar carrinho de compras conforme apresentado nesta aula. Execute e teste que sua implementação está correta. 2. No seu projeto de Locadora Virtual, implemente a funcionalidade de mostrar carrinho de compras de forma similar àquela usada na Livraria Virtual. Execute e teste que sua implementação está correta. Realizar a compra do conteúdo do carrinho e mostrar recibo Para efetuar a compra dos produtos encontrados no carrinho, o usuário deve clicar no link Finalizar compra. Esse link, segundo o código JSP do arquivo mostrarCarrinho.jsp, nos leva a URL /livros/comprar. O arquivo JSP relativo a essa URL (comprar.jsp) é mostrado a seguir. É apresentado o valor total da compra e um formulário contendo dois campos, o nome do usuário e o número de seu cartão de crédito (ver Figura 5). Depois de preencher essas informações, o usuário deve clicar no botão comprar para confirmar a compra.

Valor total da compra:

Para efetuar a compra dos livros selecionados, informe os seguintes dados:

Nome:
Número do cartão:
Figura 5 - Tela de compra dos livros Ao clicar no botão comprar, o usuário é levado a URL /livros/recibo. Essa URL irá gerar um recibo de compra para o usuário. Isso é mostrado pela Figura 6. É apresentada uma mensagem de agradecimento com o nome do usuário e indica-se que a data de envio dos livros é de cinco dias depois da data de compra (data atual), visando confirmação do pagamento do cartão. Figura 6 - Tela de recibo de compra Para gerar o código da tela de recibos, primeiro temos que efetuar a compra dos livros. Isso é feito pelo método comprarLivros() da livraria, o qual é acionado pelo Servlet controlador quando adicionamos ao mesmo o seguinte código: else if (acaoSelecionada.equals("/livros/recibo")) { try { livraria.comprarLivros(carrinho); } catch (CompraException ex) { ex.printStackTrace(); } } Já para montar a página JSP de recibo/confirmação de compra, temos o seguinte código para o arquivo recibo.jsp:

Obrigado ${param.nome}.


Seus livros serão enviados para você em .

Continuar comprando     Note o uso da classe Date e da configuração da data representada pelo objeto como igual à data atual (now.time) adicionada de 5 dias (equivalente a 432000000 milisegundos). 1. No sistema de Livraria Virtual, implemente a funcionalidade de efetuar a compra dos livros adicionados ao carrinho de compras conforme apresentado nesta aula. Execute e teste que sua implementação está correta. 2. No seu projeto de Locadora Virtual, implemente a funcionalidade de efetuar a compra de forma similar àquela usada na Livraria Virtual. Execute e teste que sua implementação está correta. Na próxima aula, você irá começar a estudar sobre JavaScript, uma linguagem que vai lhe permitir realizar a programação de códigos que irão executar no navegador Web, ou seja, no lado do cliente. Isso vai tornar a aplicação bem mais dinâmica! Até lá! Leitura complementar Para complementar a fixação do conhecimento, faça uma releitura dos links mostrados a seguir. • Resumo Nesta aula, você estudou a implementação das funcionalidades de mostrar o conteúdo do carrinho de compras e de efetuar a compra dos livros, mostrando inclusive um prazo de entrega de cinco dias. Foi possível perceber o uso constante de instruções JSP, como as taglib JSTL e marcadores e . 1. Descreva como as tecnologias dos Servlets e JSP foram usadas para o desenvolvimento da Livraria Virtual. 2. Descreva de forma geral como foi implementada a funcionalidade de mostrar carrinho e de efetuar a compra dos livros. Desenvolvimento Web Aula 16 –JavaScript: Introdução e Sintaxe Professores autores Até a metade deste curso,você aprendeu como desenvolver aplicações Web usando servlets, JSP e JSF. Além disso, no curso de autoria Web você aprendeu como desenvolver páginas Web estáticas, usando HTML. Na aula de hoje você vai começar a aprender uma maneira de tornar suas páginas Web mais interativas e consequentemente mais interessantes para os usuários finais que a acessarem. Bons estudos! Ao final desta aula, você deverá ser capaz de: • entender os conceitos e a utilização de JavaScript; • saber a sintaxe de Javascript; • compreender o tratamento de Evento de elementos HTML. Introdução O JavaScript é uma linguagem de script que quando incorporada em uma página Web, mas especificamente HTML, permite incrementar a apresentação e interatividade da mesma. Essa linguagem permite ao programador ter acesso a elementos de uma página web, como imagens, campos de um formulário links etc. Além disso, estes elementos podem ser modificados via programação mesmo após a página ter sido carregada e exibida para o usuário, pois o JavaScript permite capturar eventos, como um click do mouse ou uma tecla pressionada de seu teclado e executar ações especificadas pelo programador. Javascript foi desenvolvido inicialmente pela Netscape e era conhecido pelo nome de LiveScript. Adotado no fim do ano de 1995, pela Sun (que também desenvolveu o Java), ela tomou assim o seu nome atual Javascript. Javascript não é próprio do browser Netscape. No decorrer dos tempos, outros navegadores também incorporaram o JavaScript, inclusive hoje a maioria dos navegadores Web dão suporte à mesma. Apesar disso, podem existir alguns problemas de compatibilidade entre diferentes browsers, ou seja, scripts que rodam em um navegador podem não executar ou ter um comportamento diferente em um outro navegador. A sintaxe do Javascript é baseada na linguagem Java, mas a similaridade entre as duas termina por aí, visto que elas possuem diversas diferenças, como pode ser visto no Quadro 1: JavaScript Java Interpretada pelo navegador enquanto ele lê a página HTML. Compilada em bytecode e executado normalmentefora do navegador pela máquina virtual Java Programação simples com poder limitado (indicado para aplicações simples) Linguagem de programação mais complexa, mas mais poderosa Baseado em Objetos (Possui objetos, mas não é totalmente compatível com o conceito de orientação a objetos) Orientado a objetos Referências a variáveis são checados em tempo de execução. Referências a variáveis são checados em tempo de compilação. Os tipos das varíaveis não são declarados. Fortemente tipada (os tipos das variáveis devem ser definidos na declaração da mesma) Quadro 1 - Diferenças entre Java e JavaScript A única execeção são os Applets, que são programas Java que rodam no navegador Web. Mas ainda assim, não são embutidos junto ao código HTML, como é o caso de JavaScript. Primeiros Passos Para começar a desenvolver seus primeiros scripts em JavaScript, poucos requisitos são necessários. São eles descritos a seguir. • Conhecimento de HTML. Como o JavaScript é embutido dentro de uma página HTML e manipula os elementos da página, é indispensável o conhecimento de HTML para construir páginas Web interativas com JavaScript. • Navegador Web que suporte JavaScript (Os navegadores atuais mais populares suportam. Ex. Internet Explorer, Firefox, Chrome) para executar seus scripts. • Editor de Texto qualquer. Assim como código HTML, qualquer editor pode ser usado para desenvolver código JavaScript. Além do mais existem diversos editores específicos para HTML que também facilitam o desenvolvimento de código JavaScript. Se você já possui todos os requisitos necessários para fazer um programa JavaScript, então, é hora de começar. Como já foi comentado antes, o código JavaScript é embutido dentro de uma página HTML, mais especificamente ele é colocado dentro da tag Outro exemplo básico, que, ao invés de abrir uma caixa de diálogo, imprime a mensagem diretamente na página HTM. Nesse caso, o comando documento.write() pode ser usado para adicionar textos dentro da página HTML. Pode-se utilizar, inclusive tags HTML para incrementar o texto, como pode ser visto no exemplo abaixo. Primeiro Exemplo Nos dois casos acima, a tag Como era de se esperar, o mesmo problema ocorreria se um script tentasse acessar uma variável ou função que só está declarada depois dela. Por essa razão, para assegurar que scripts funcionem corretamente, é uma boa prática declarar funções e variáveis, que são usadas em vários lugares, dentro da tag de . Como essa tag vem antes do , seus elementos serão naturalmente carregados antes de qualquer elemento que estiver do , como é apresentado na trecho abaixo. Script no head Como pode ser visto, a instrução da linha 10 usa uma varíável chamada mensagem que foi definida na linha 5 (na próxima seção será explicado mais detalhes sobre declaração de variável), em um trecho de script declarado dentro da tag . Apesar do exemplo mostrar somente um bloco de Uma outra maneira de usar código JavaScript sem ter que necessariamente misturar os comandos HTML com JavaScript é utilizar referências para arquivos externos que possuam código JavaScript. No exemplo abaixo, dois arquivos são criados, um contendo somente código HTML e outro contendo somente código JavaScript. Script no head Como pode ser visto, a instrução da linha 10 usa uma variável chamada mensagem que foi definida na linha 5 (na próxima seção será explicado mais detalhes sobre declaração de variável), em um trecho de script declarado dentro da tag . Apesar do exemplo mostrar somente um bloco de Como pode ser visto, ao contrário da linguagem Java, não se declara o tipo de dados de uma variável. Isso significa que não é necessário colocar o tipo (int, float, double, char e outros) da variável em Javascript. Apesar de não ser necessário a declaração dos tipos das variáveis, os valores carregados por elas podem ser de tipos distintos. No exemplo anterior, a variável mensagem é do tipo string, enquanto as variáveis idade e _altura são do tipo inteiro e decimal, respectivamente. A TIPO DESCRIÇÃO Números Podem ser números inteiros ou números decimais (valores com ponto) Cadeias de caracteres Qualquer cadeia de caractere entre ' (aspa simples) ou “ (aspas duplas) Booleanos As palavras true para verdadeiro e false para falso As constantes possuem a mesma função das variáveis, mas seu valor não pode ser alterado. Para se declarar constantes, usa-se a palavra const antes do nome da constante. Assim como outras linguagens de programação, as variáveis podem ser locais ou globais. Variáveis globais podem ser usadas por qualquer função em todos os códigos JavaScript presentes na página (desde que declaradas antes de usá-las). Elas são declaradas fora e antes de todas funções que a usam, mas podem estar contidas em qualquer local da página. Já as variáveis locais são declaradas (usando a palavra var) e invocadas somente dentro da função. Apesar de vermos o assunto de funções em detalhes mais adiante, abaixo segue um trecho de código JavaScript que exemplifica o uso de variáveis locais e globais. Como pode ser visto, a variável mensagem é declarada na linha e usada nas linhas 6 e 11, enquanto a variável numero1 e numero2 são locais, sendo definidas dentro das duas funções nas linhas 5 e 10, respectivamente. Operadores Os operadores são úteis para manipular as variáveis, em JavaScript existem diversos operadores, veja quais são eles. a. Operadores Matemáticos São os operadores que permitem a realização de operações aritméticas sob as variáveis. b. Operadores Lógicos São os operadores que permitem a realização de operações lógicas sob as variáveis. São normalmente utilizados em comandos condicional (como o if) e repetições (como o while). São aplicáveis a valores booleanos (true e false) e seu resultado também é um booleano. c. Operadores de Comparação São os operadores que permitem a realização de operações de comparação sob variáveis. São muito utilizados em comandos condicional (como o if) e repetições (como o while), pois o resultado de sua avaliação é um valor booleano. d. Operadores Combinados São operadores que simplificam a sintaxe de operações aritméticas bem comuns, servindo como atalho mais compacto da operação. Comandos IF O comando IF, como em toda linguagem, é usado simplesmente para testar uma condição. O exemplo abaixo ilustra o uso do if para decidir o que será adicionado como conteúdo da página. A função prompt() exibe uma mensagem para o usuário e aguarda a digitação de um texto e confirmação do usuário. O trecho abaixo, por exemplo, solicita que o usuário digite um número e depois avalia se esse número é maior ou menos que 10, exibindo uma mensagem diferente, para cada caso. WHILE O comando while, assim como em Java, é usado como comando de repetição aplicado a uma condição. Na maioria das vezes ele é aplicado quando não podemos determinar a priori quantidade de repetições que nosso laço vai ter. No exemplo abaixo, é apresentado um script que adiciona
(quebra de linha) enquanto i<=10. FOR A expressão for permite executar um bloco de instruções um determinado número de vezes, de acordo com um certo critério. No exemplo abaixo, é apresentado um script que exibe uma lista não-numerada contendo os números de 1 a 10, um em cada item. As instruções break e continue podem ser usadas nos dois tipos de laços. O break permite interromper prematuramente um laço, enquanto o continue permite suspender o ciclo atual e continuar o próximo ciclo do laço. SWITCH/CASE A expressão switch serve para avaliar o valor de uma variável e executar um código associado, de acordo com o valor da mesma (o que é definido em cada um case). O exemplo abaixo demonstra o uso do swith/case. Funções Assim como nas principais linguagens de programação, JavaScript também possui uma maneira de se criar funções. Uma função nada mais é do que um pedaço de programa destinado a uma tarefa bem específica e que pode ser utilizado vária vezes em diferentes pontos do programa principal. Em JavaScript, uma função é definida, usando-se a palavra function. O trecho abaixo apresenta a declaração da função imprimeLista, que escreve dez itens numa lista não ordenada de HTML. Nesse caso, a função foi declarada dentro do , mas ela poderia ser declarada em qualquer local da página, desde que seja antes de ser usada. Script no head As funções em JavaScript também podem receber valores como parâmetros. Observe o exemplo abaixo. Neste caso, a função imprimeLista foi melhorada para receber a quantidade de itens da lista como parâmetro. Assim como variáveis, que não se declara o tipo, nos parâmetros da função o tipo não é definido. Script no head Por fim, uma função também pode retornar um valor após sua execução. Neste caso, basta usar o comandoreturn para retornar o valor. O exemplo abaixo ilustra uma função que recebe um número como parâmetro e retorna o dobro de seu valor. Tratamento de Eventos Durante o carregamento e exibição de uma página HTML diversos eventos podem acontecer. Esses eventos podem ser cliques e movimentos do mouse ou teclas digitadas pelo usuário, além de muitos outros eventos que serão discutidos nesta seção. Esses eventos ocorrem em uma diversidade de situação diferente e alguns deles são específicos de alguns tipos de componentes. Por exemplo, o evento de tecla digitada pode ser produzido quando o usuário digita algo sobre um componente de texto (como o textarea ou input text). Já em elementos como imagens ou listas não-ordenadas esse evento não existe e nem teria muito sentido, já que o usuário não pode digitar algo em cima de uma imagem numa página Web. Os eventos são produzidos normalmente em vários elementos da página Web ao longo do seu carregamento e utilização. Finalizada essa visão geral sobre eventos em HTML, resta a questão que é saber o que JavaScript tem a ver com os eventos produzidos por elementos HTML. Apesar de não parecer claro em um primeiro momento, essa é uma das partes mais importantes da utilização de JavaScript em páginas Web. A grande resposta para essa charada é que esses eventos podem ser tratados pelo programador, ou, em outras palavras, o programador pode definir o que fazer quando um determinado evento acontecer, e isso é feito em JavaScript. Por exemplo, o programador pode definir uma ação (um código JavaScript) que deve ser executada ao se passar o mouse por cima de uma imagem específica. O trecho de código abaixo, por exemplo, define que a função abreAlerta deve ser executada quando o usuário passar o mouse por cima da imagem (onMouseOver). Script no head Cada tag HTML suporta diferentes tipos de eventos. A maioria dos eventos é precedido da palavra ON, como foi o caso do exemplo (Evento de MouseOver, ou seja, passar o mouse por cima, é tratado informando a propriedade onMouseOver da tag IMG). Outro evento que pode ser tratado na tag IMG é o evento de MouseOut, através da propriedade onMouseOut. Esse evento é gerado quando o usuário sai com o mouse de cima da imagem. O exemplo a seguir ilustra o tratamento de múltiplos eventos de um mesmo elemento HTML. Script no head Nesse caso, os eventos de entradas e saídas de mouse da área imagem são tratados pelas funções contOver e contOut, enquanto o clique em qualquer local da página executa a função imprimeAlerta. Existe uma infinidade de eventos a serem tratados. Alguns eventos são comuns a vários tipos de elementos, enquanto outros só existem para determinados elementos. O Quadro 1 contém alguns eventos que são comuns a vários tipos de elementos. Para saber todos os eventos por tipo de elemento HTML, acesse . Quadro 1 - Exemplos de eventos bem comuns Chegamos ao fim da nossa aula 16. Na próxima aula, você verá aspectos avançados do JavaScript, em particular como acessar e até alterar os elementos de formulários, imagens e outros elementos HTML. Resumo Nesta aula, você aprendeu a sintaxe básica de JavaScript, como inserir código JavaScript dentro de páginas HTML e como executar ações específicas quando determinados eventos ocorrerem. 1. Desenvolva uma página HTML que ao ser carregada exibe um alerta informando que acabou de ser carregada (Dica: pesquise qual evento é gerado ao se carregar a página HTML). 2. Desenvolva uma página HTML contendo um link (apontando para o Google, por exemplo), que quando clicado abre um alerta antes informando que você está sendo redirecionado para o Google. 3. Desenvolva uma página HTML, contendo um script que solicita ao usuário que digite a operação a ser executada usando o comando prompt (As opções podem ser '+', '-', '*' e '/'). Depois, solicita que o usuário digite o primeiro valor e depois o segundo, por fim, exibe a operação e o seu resultado na própria página HTML, seguindo a especificação de formatação abaixo: operando1 operacao operando2 = resultado (note que a operação deve estar em negrito e o resultado além de negrito deve estar em uma fonte maior). 4. Desenvolva uma página HTML que contém um script que solicita ao usuário que digite um número e exibe uma figura qualquer o número de vezes que foi digitado pelo usuário. Desenvolvimento Web Aula 17 – JavaScript: Acessando objetos (Parte 1) Na aula passada, você começou a aprender uma maneira de tornar suas páginas Web mais interativas e consequentemente mais interessantes para os usuários finais que a acessarem, que é através da programação em JavaScript. Nesta aula, você vai aprender aspectos mais avançados da linguagem, em particular como trabalhar com objetos e como manipular objetos que representam a página Web e seus elementos (tags), o navegador, as janelas etc. Bons estudos! Ao final desta aula, você deverá ser capaz de: • definir e usar seus próprios objetos; • saber manipular objetos da biblioteca de JavaScript. Introdução JavaScript não é uma linguagem completamente orientada a objetos, mas possui o elemento base da programação orientada a objetos, que são os objetos. Apesar disso, ela não implementa alguns conceitos típicos de linguagens dessa natureza, tais como herança, interfaces, visibilidade etc. Por esse motivo, é comum dizer que ela é baseada em objetos e não orientada a objetos. Além disso, iremos perceber mais adiante que a implementação do conceito de objetos em JavaScript é algo extremamente simplificado, bem diferente de Java. Além da possibilidade de se criar novos tipos de objetos e manipulá-los através da programação, também é possível acessar todos os elementos HTML presentes em uma página Web, através de JavaScript. Em outras palavras, quando o browser carrega uma página Web, ela cria uma série de objetos representando os diversos elementos da página (Imagens, formulários, botões etc.). Esses por sua vez podem ser acessados e modificados após a página ter sido iniciada, através de JavaScript. Defininindo e Manipulando Objetos Assim como em Java, em JavaScript um objeto é criado a partir da definição de uma estrutura. Essa estrutura em Java é chamada de classe, mas em JavaScript ela se resume a um construtor. Um construtor nada mais é que uma função comum. O exemplo abaixo demonstra a definição de um construtor e logo em seguida como uma instância de um objeto desse tipo é criado. Essa instância se faz através da palavra-chave new , indicando a criação de um novo objeto. function Livro() { // Pode definir ações que devem ser executadas ao se instanciar um objeto do tipo Livro } var meuLivro = new Livro(); Atributos Como era de se esperar, ao se definirem objetos, é natural pensar em como definir os seus atributos. Em JavaScript, esses atributos são definidos dentro do próprio construtor do objeto, já que não existem classes. O trecho de código abaixo demonstra a definição de dois atributos para objetos do tipo Livro. function Livro() { this.titulo = “Harry Potter”; this.autor = “J.K. Rowling”; } var meuLivro = new Livro(); alert(meuLivro.titulo); O exemplo acima é bem limitado, visto que ele só irá permitir a criação de objetos sempre com o mesmo título e autor. Na prática, seria interessante que o título e autor pudessem ser passados como parâmetros. O trecho de código abaixo demonstra essa possibilidade, em um exemplo bem mais real, onde o construtor é parametrizado e dessa forma diversos objetos podem ser criados com títulos e autores diferentes. function Livro(tituloPar,autorPar) { this.titulo = tituloPar; this.autor = autorPar; } var meuLivro = new Livro(“Harry Potter”,“J.K. Rowling”); var meuLivro2 = new Livro(“As Crônicas de Nárnia”,“C.S. Lewis”); alert(meuLivro.titulo); alert(meuLivro2.titulo); 1. Defina classes para representar os conceitos existentes em uma Livraria, em particular crie classes para cada um desses conceitos: Livro, Autor, Gênero. Pense nos atributos de cada um deles e na relação entre eles. 2. Desenvolva um script que a partir da função prompt cria um livro (com todas as informações relacionadas) e depois exibe as informações do livro (dados digitados pelo usuários) usando a função alert. Métodos Assim como atributos, objetos também possuem métodos, que representam as ações que podem ser executadas sob o objeto. Para criarmos métodos, devemos fazer o mesmo que foi feito para se definir atributos. O trecho de código abaixo demonstra a definição de um novo método chamado getDescricao, que ao ser chamado retorna um string resultado da concatenação do título e autor do livro. function Livro(tituloPar,autorPar) { this.titulo = tituloPar; this.autor = autorPar; this.getDescricao = function(){ return this.titulo +” - “+this.descricao} } var meuLivro = new Livro(“Harry Potter”,“J.K. Rowling”); var meuLivro2 = new Livro(“As Crônicas de Nárnia”,“C.S. Lewis”); alert(meuLivro.getDescricao()); alert(meuLivro2.getDescricao()); Apesar do exemplo não ter demonstrado isso, os métodos podem receber parâmetros e também podem alterar os atributos do objeto no qual ele foi chamado. O trecho de código abaixo demonstra um novo método chamado adicionarAutor que além de receber um parâmetro, altera o valor do atributo autor. function Livro(tituloPar,autorPar) { this.titulo = tituloPar; this.autor = autorPar; this.getDescricao = function(){ return this.titulo +” - “+this.descricao} this.adicionarAutor = function(novoAutor){ this.autor = thia.autor + “,”+novoAutor} } var meuLivro = new Livro(“Harry Potter”,“J.K. Rowling”); meuLivro.adicionarAutor(“XXXX”); alert(meuLivro.getDescricao()); 1. Crie o método de validar, que é responsável por verificar se todos os atributos foram informados (se são diferentes de null); 2. Crie o método temMesmoAutor que recebe um livro como parâmetro e verifica se ele é do mesmo autor, retornando true caso positivo e false caso contrário. Trabalhando com vetores Assim como Java e boa parte das linguagens de programação, JavaScript permite a manipulação de variáveis como vetores ou matrizes. Como você já sabe, um vetor ou matriz é usado em situações quando precisamos de que uma única variável possa guardar vários valores diferentes. Além disso, vetores e matrizes permitem que a manipulação desse conjunto de valores possa ser feita de maneira simples. Em JavaScript, um vetor é criado de forma similar a um objeto, usando a classe Array, conforme pode ser visto no exemplo abaixo. Neste exemplo, dois vetores são criados, um chamado titulos e o outro chamado codigos. Perceba que, assim como qualquer variável, o tipo de dados que será armazenado no vetor não é definido. var titulos = new Array(); //declaração do vetor var codigos = new Array() ; O trecho em seguida demonstra como é feita a manipulação dos valores do vetor. Inicialmente, são atribuídos valores a cada uma das posições do vetor e depois essas informações são acessadas e impressas na página. Por fim, é impressa a informação da quantidade de títulos através do acesso à propriedade length, que informa o tamanho de um vetor. titulos[0] = “As crônicas de Nárnia”; titulos[1] = “Contato”; codigos[0] = 101010; codigos[1] = 234001; for(i=0;i<2;i++){ document.writeln(codigos[i] + ” --- “ + titulos[i]); } document.write(“Total de Títulos: ”+titulos.length); Biblioteca de objetos de JavaScript Com JavaScript, além de definir e criar novos objetos, como mostrado na Seção 2, é possível acessar e manipular outros objetos que estão acessíveis durante a execução do JavaScript contido em uma página Web. São diversos objetos disponíveis, desde funções básicas, como manipulação de string ou funções matemáticas, até objetos que representam os elementos (tags) contidos em uma página Web. De um modo geral, esses objetos podem ser divididos em três grupos, são eles: • Objetos DOM – objetos que representam os elementos HTML contidos na página Web; • Objetos JavaScript – objetos que possuem funções de caráter utilitário, tais como funções matemáticas (seno, cosseno, máximo, mínimo, potencial etc.), manipulação de texto (substrings, busca em texto etc.); • Objetos do Browser – acesso a objetos que representam as ferramentas do ambiente onde a página Web está executando, em particular o navegador. Com esses objetos, é possível abrir novas janelas, acessar histórico de navegação, acessar informações do navegador ou do próprio servidor. Cada um desses grupos e os objetos pertencentes aos mesmos, serão apresentados em mais detalhes nas seções seguintes. Objetos DOM Através dessa biblioteca, é possível manipular os objetos que representam a página Web e que foram criados pelo navegador no momento em que a página foi carregada. Ainda mais, é possível acessar os elementos da página, como imagens, inputs etc. e alterar propriedades dos mesmos após a página ter sido carregada. Para cada elemento (tag) presente numa página Web, um objeto correspondente é criado pelo navegador e acessível via JavaScript. Esses objetos, como era de se esperar, possuem propriedades e métodos que podem ser acessados e executados, respectivamente, através de JavaScript. Esse modelo de objetos (tipos de objetos, atributos, métodos e relacionamento entre eles) é padronizado pelo W3C e conhecido como HTML DOM (Document Object Model). A especificação completa desse modelo de objetos pode ser vista em . Objeto Document Para cada página HTML que é carregada no navegador, é criado um objeto document. Através desse objeto, é possível acessar todos os elementos HTML (tags) definidos na página com o uso de JavaScript. O quadro abaixo apresenta os principais atributos que podem ser acessados a partir desse objeto. Quadro 1 - Atributos do objeto document No exemplo a seguir, são definidas duas imagens na página HTML e no código JavaScript é escrito na página a quantidade de imagens existentes. Essa contagem das imagens é feita através do acesso ao vetor de imagens (document.images).

Número de imagens:

O trecho de código abaixo apresenta um exemplo bem interessante. A ideia do trecho abaixo é aumentar o tamanho de uma figura sempre que o usuário passar o mouse por cima dela. Nesse exemplo, iremos combinar a tratamento de eventos, já foi visto na aula 16, com a biblioteca DOM. Como pode ser visto, duas funções são definidas (aumentar e diminuir), as quais recebem como parâmetro uma variável que representa a posição da imagem (as imagens são armazenada no vetor de imagens de acordo com a ordem como elas foram definidas na página HTML). Essas funções aumentam a largura e altura (propriedades width e height, respectivamente) da imagem que está na posição que foi passada como parâmetro. Por fim, pode-se verificar que na declaração das imagens é definido o tratamento para os eventos MouseOver e MouseOut, que representam o tratamento do evento de pôr o mouse em cima e tirar o mouse de cima da figura, respectivamente. Perceba, que para a imagem com id imagem1 é passado o parâmetro 0 (representa a posição 0 do vetor), e para imagem2 é passado o parâmetro 1 (representa a posição 1 do vetor). Assim como as imagens da página foram acessadas e suas propriedades alteradas, é possível fazer o mesmo para os links, âncoras e formulários, somente mudando o atributo que será acessado no objeto document. Além dos atributos, o objeto document possui alguns métodos, como mostrado na Quadro 2. Quadro 2 - Métodos do objeto document Os três primeiros métodos são métodos que permitem acessar qualquer elemento da página HTML. Diferentemente dos atributos que só permitem o acesso a elementos do tipo imagem, link, âncora ou formulário, com uso desses métodos é possível ter acesso a qualquer elemento da página, desde elementos de título (

,

,

, etc.) até elementos de formulários (, Table O objeto Table representa uma tabela em HTML. Para cada tag em um documento HTML, um novo objeto Table é criado. Como qualquer outra tag, esse objeto pode ser manipulado e alterado durante o acesso do usuário à página. Com o uso de JavaScript, é possível acessar o conteúdo de qualquer célula da tabela, podendo inclusive alterá-lo. Também é possível alterar a formatação, como o tamanho, bordas, espaçamentos etc. Além de tudo isso, é possível acrescentar novas linhas e células a uma tabela já criada. Muita coisa pode ser feita em uma tabela através do uso de JavaScript, tudo vai depender da sua imaginação. No Quadro a seguir são definidos os principais atributos do objeto Table. Quadro 1 - Atributos do objeto Table Para manipular tabelas, dois outros objetos relacionados precisam ser conhecidos, são eles: • TableRow – Representa uma linha da tabela (tag ); • TableCell – Representa uma célula da tabela (tag ) e adiciona-o após a posição definida. Nesse nosso exemplo, recuperamos a quantidade de linhas já existentes calculando o tamanho do vetorrows do objeto Table. A nova linha é armazenada em uma variável chamada novaLinha, que é usada para se criar duas células, pelo uso do método insertCell. Por fim, o conteúdo da célula é modificado através do acesso ao atributo innerHTML. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
). O exemplo abaixo demonstra a manipulação de tabelas em JavaScript. Nesse exemplo, o usuário deve digitar nome e CPF em dois campos textos e depois clicar no botão. Ao fazer isso, uma nova linha na tabela é criada contendo duas células, cada uma com os valores digitados nos campos de texto. Como pode ser visto, o comando insertRow que é invocado no objetoTable cria um novo objeto do tipo TableRow (tag
Nome CPF
Maria Bonita 029.221.222-22

Nome:
CPF:
Objetos JavaScript Fazem parte deste grupo objetos de uso geral, os quais estão presente em outras linguagens de programação e independem de uma página HTML. Estes objetos são: • Array – É usado para armazenar múltiplos valores em uma única variável. • Boolean – É usado para manipular e fazer conversões entre tipos booleanos e outros. • Date – É usado para se trabalhar com datas e horas. • Math – Possui diversas funções matemáticas. • Number - É usado para manipular e fazer conversões entre tipos numéricos e outros. • String – É usado para manipular textos • RegExp – É usado para manipular expressões regulares em textos (casamento de padrão e funções de "search-and-replace"). Por questão de tempo, iremos abordar neste curso somente os objetos Math, Date e String, pois são os mais comumente usados em scripts. Para saber mais detalhes sobre os outros, é só acessar:https://www.w3schools.com/jsref/. Date O objeto Date é usado quando é necessário trabalhar com datas e horas. Ele representa uma data, incluindo informação de ano, mês, dia, hora, minuto, segundo e milésimo de segundo. Além disso, ele oferece diversas facilidades para manipulação de valores que são datas, além de ter diversos métodos que permitem a conversão de outros tipos de dados (como string e inteiro) em datas e vice-versa. Um objeto Date pode ser criado de quatro diferente maneiras, são elas: • Criar uma data a partir da informação do relógio do computador onde o script está sendo executado. var d = new Date(); • Criar uma data a partir de um número inteiro que representa a quantidade de milésimos de segundos, desde de 1 de janeiro de 1970 (Mesmo que o padrão Java). var d = new Date(milisegundos); • Criar uma data a partir da conversão de uma string formatada. var d = new Date(dataString); • Criar uma data considerando um determinado ano, mês, dia, hora, minuto, segundo e milésimo de segundo. var d = new Date(ano, mes, dia, hora, minuto, segundo, milisegundo); O trecho de código abaixo exemplifica como apresentar na página a hora atual da máquina onde a página é carregada. 01 02 03 04 05 06 07 08 Além dos construtores, existem várias funções interessantes para manipular datas e horas. A Erro: Origem da referência não encontrada apresenta os principais métodos existentes no objeto Date. Quadro 2 - Métodos do objeto Date O trecho de código a seguir é referente a implementação de um relógio digital, usando as funções do objeto Date. Como pode ser visto, a função calcularHora recupera a data e hora atual a partir do construtor padrão do objeto Date e depois guarda em variáveis separadas as informações de hora, minuto e segundo. O minuto e segundo são formatados através do uso da função auxiliar formata(), que tem como objetivo acrescentar um zero na frente caso o número só tenha um dígito (menor que 10). Depois, é criada uma variável chamada horário, que guarda a hora atual no formato: HORA:MINUTO:SEGUNDO. Por fim, o elemento HTML com id igual a “relogio” é recuperado, através do uso da função document.getElementById e o seu conteúdo alterado para apresentar a hora atual no formato legível. A última linha da função, a chamada ao método setTimeout, funciona como um agendamento de uma chamada de função. Em outras palavras, essa linha faz com que a função calcularHora seja chamada novamente daqui a 500 milésimos de segundos. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
Math O objeto Math oferece diversas funções matemáticas, bem comuns, tais como seno, cosseno, máximo, mínimo, logarítmo etc. Além disso, ele também possui algumas constantes muito usadas em operações matemáticas, tais como PI, E (Número de Euler), Raiz quadrada de 2 etc. O Quadro 3 apresenta em mais detalhes as funções existentes. Quadro 3 - Métodos do objeto Math. O trecho de código abaixo exemplifica o uso de algumas funções matemáticas. 01 02 03 04 05 06 07 08 09 10 11 String Esses objetos são usados para manipular e armazenar textos. Eles oferecem diversos métodos relacionados à manipulação de textos. O Quadro 4 apresenta os principais métodos que podem ser usados em strings. Quadro 4 - Métodos do objeto string Segue abaixo dois trechos de código e seus respectivos resultados: Trecho 1: Resultado Trecho 1: 10 -1 6 Trecho 2: Resultado Trecho 2: lo world! lo w Objetos do Browser Neste grupo, estão os objetos que representam o ambiente onde a página Web está executando, em particular o navegador. Estes objetos são: • Window – Representa uma janela aberta no navegador. • Navigator – Representa o próprio navegador. • History – Contém as URLs visitadas pelo usuário. • Location – Contém informações relacionadas ao endereço sendo acessado. Dica: Neste curso, iremos abordar somente os três mais usados, que são Window, Navigator e History. Mais detalhes sobre os outros podem ser acessados em: . Window O objeto Window representa uma janela do navegador aberta. No entanto, se o documento contém frames ( or