Sei sulla pagina 1di 5

Os protocolos da camada de transporte

A Internet tem dois protocolos principais na camada de transporte, um protocolo sem


conexões e outro orientado a conexões. Nas próximas seções, estudaremos os dois. O protocolo sem
conexões é o UDP. O protocolo orientado a conexões é o TCP. Como o UDP é basicamente o IP com
um pequeno cabeçalho, vamos começar por ele. Também examinaremos duas aplicações do UDP.

1. Introdução ao UDP
O conjunto de protocolos da Internet admite um protocolo de transporte sem conexões, o
UDP (User Datagram Protocol). O UDP oferece um meio para as aplicações enviarem datagramas IP
encapsulados sem que seja necessário estabelecer uma conexão. O UDP transmite segmentos que
consistem em um cabeçalho de 8 bytes, seguido pela carga útil. O cabeçalho é mostrado na Figura
6.23. As duas portas servem para identificar os pontos extremos nas máquinas de origem e destino.
Quando um pacote UDP chega, sua carga útil é entregue ao processo associado a porta de destino.
Essa associação ocorre quando a primitiva BIND ou algo semelhante é usado. De fato, o principal
valor de se ter o UDP em relação ao uso do IP bruto é a adição das portas de origem e destino. Sem os
campos de portas, a camada de transporte não saberia o que fazer com o pacote. Com eles, a camada
entrega segmentos corretamente.

Figura 6.23: O cabeçalho do UDP

A porta de origem é usada principalmente quando uma resposta dever ser devolvida a
origem. Copiando o campo Source port do segmento de entrada no campo Destination port do
segmento de saída, o processo que transmite a resposta pode especificar qual processo na máquina
transmissora deve recebe-lo.
O campo UDP length inclui o cabeçalho de 8 bytes e os dados. O campo UDP checksum é
opcional e armazenado como 0 se não for calculado (um valor 0 verdadeiro calculado é armazenado
com todos os bits iguais a 1). É tolice desativa-lo, a menos que a qualidade dos dados não tenha
importância (por exemplo, no caso de voz digitalizada).
Vale a pena mencionar explicitamente algumas ações que o UDP não realiza. Ele não realiza
controle de fluxo, controle de erros ou retransmissão após a recepção de um segmento incorreto.
Tudo isso cabe aos processos do usuário. O que ele faz é fornecer uma interface para o protocolo IP
com o recurso adicional de demultiplexação de vários processos que utilizam as portas. Isso é tudo
que ele faz. Para aplicações que precisam ter controle preciso sobre o fluxo de pacotes, o controle de
erros ou a sincronização, o UDP fornece apenas aquilo que é determinado.
Uma área na qual o UDP é especialmente útil é a de situações cliente/servidor. Com
frequência, o cliente envia uma pequena solicitação ao servidor e espera uma pequena resposta de
volta. Se a solicitação ou a resposta se perder, o cliente simplesmente chegara ao timeout e tentará
de novo. Não só o código é simples, mas é necessário um número menor de mensagens (uma em cada
sentido) do que no caso de um protocolo que exige uma configuração inicial.
Uma aplicação que utiliza o UDP desse modo é o DNS (Domain Name System), protocolo da
camada de aplicação. Em resumo, um programa que precisa pesquisar o endereço IP de algum nome
de host — por exemplo, www.cs.berkeley.edu — pode enviar um pacote UDP contendo o nome do
host a um servidor DNS. O servidor responde com um pacote UDP que contém o endereço IP. Não é
necessária nenhuma configuração antecipada e também nenhum encerramento posterior. Basta
enviar duas mensagens pela rede.

1.1 Chamada de procedimentos remotos


Em um certo sentido, enviar uma mensagem a um host remoto e obter de volta uma resposta
é muito semelhante a criar uma chamada de função em uma linguagem de programação. Em ambos
os casos, você começa com um ou mais parâmetros e recebe de volta um resultado. Essa observação
levou as pessoas a tentarem organizar interações de solicitação/resposta em redes no formato de
chamadas de procedimentos. Tal organização torna as aplicações de rede muito mais fáceis de
programar e mais familiares. Por exemplo, imagine um procedimento chamado get_IP_address
(host_name) que funciona enviando um pacote UDP a um servidor DNS e aguardando a resposta,
chegando ao timeout e tentando de novo, caso não receba uma resposta com rapidez suficiente. Desse
modo, todos os detalhes de redes podem ficar ocultos do programador.
O trabalho fundamental nessa área foi realizado por Birrell e Nelson (1984). Em resumo, o
que Birrell e Nelson sugeriram foi permitir que os programas chamassem procedimentos localizados
em hosts remotos. Quando um processo na máquina 1 chama um procedimento na máquina 2, o
processo de chamada em 1 é suspenso, e a execução do procedimento chamado ocorre em 2. As
informações podem ser transportadas do chamador até o chamado nos parâmetros, e pode voltar no
resultado do procedimento. Nenhuma passagem de mensagens é visível para o programador. Essa
técnica é conhecida como RPC (Remote Procedure Call — chamada de procedimento remoto) e se
tornou a base para muitas aplicações de redes. Tradicionalmente, o procedimento chamador é
conhecido como cliente, e o procedimento chamado é conhecido como servidor; também usaremos
esses nomes aqui.
A ideia por trás da RPC é tornar uma chamada de procedimento remoto o mais semelhante
possível a uma chamada local. Na forma mais simples, para chamar um procedimento remoto, o
programa cliente deve estar vinculado a um pequeno procedimento de biblioteca, chamado stub do
cliente, que representa o procedimento do servidor no espaço de endereços do cliente. De modo
semelhante, o servidor está vinculado a um procedimento chamado stub do servidor. Esses
procedimentos ocultam o fato de que a chamada de procedimento do cliente até o servidor não é
local.
As etapas reais na criação de uma RPC são mostradas na Figura 6.24. A etapa 1 é a chamada
do cliente ao stub do cliente. Essa é uma chamada de procedimento local, com os parâmetros
colocados na pilha da maneira normal. A etapa 2 é o stub do cliente reunindo os parâmetros em uma
mensagem e efetuando uma chamada de sistema para enviar a mensagem. A reunião dos parâmetros
é chamada empacotamento (marshaling). A etapa 3 é o núcleo enviando a mensagem da máquina
cliente até a máquina servidora. A etapa 4 é o núcleo passando o pacote recebido ao stub do servidor.
Finalmente, a etapa 5 é o stub do servidor chamando o procedimento servidor com os
desempacotados. A resposta segue o mesmo caminho no sentido inverso.
O principal detalhe que devemos observar nesse caso é que o procedimento cliente, escrito
pelo usuário, simplesmente realiza uma chamada de procedimento normal (isto é, local) ao stub do
cliente, que tem o mesmo nome que o procedimento servidor. Tendo em vista que o procedimento
cliente e stub do cliente estão no mesmo espaço de endereços, os parâmetros são repassados no
modo habitual. De forma semelhante, o procedimento servidor é chamado por um procedimento em
seu espaço de endereços com os parâmetros que espera. Para o procedimento servidor, nada é
incomum. Desse modo, em vez de ser realizada a E/S em soquetes (interface de programa
conveniente para a rede), a comunicação de rede é feita simulando-se uma chamada de procedimento
normal.

Figura 6.24: Etapas na criação de uma chamada de procedimento remoto. Os stubs estão
sombreados

Apesar da elegância conceitual da RPC, existem alguns períodos ocultos. Um deles é o uso de
parâmetros de ponteiros. Normalmente, a passagem de um ponteiro a um procedimento não e
problema. O procedimento chamado pode usar um ponteiro do mesmo modo que o chamador o
utiliza, porque ambos os procedimentos convivem no mesmo espaço de endereço virtuais. Com a
RPC, a passagem de ponteiros é impossível, porque o cliente e o servidor estão em espaços de
endereços diferentes. Em alguns casos, podem ser usados artifícios para torna possível a passagem
de ponteiros.
Um segundo problema é que, em linguagens com tipificação fraca como C, é perfeitamente
valido escrever um procedimento que calcula o produto interno de dois vetores (arrays), sem
especificar o tamanho de cada vetor. Cada um deles poderia terminar com um valor especial
conhecido apenas pelo procedimento de chamada e pelo procedimento chamado. Sob essas
circunstancias, é essencialmente impossível para o stub do cliente empacotar os parâmetros: ele não
tem como determinar o tamanho desses parâmetros.
Um terceiro problema é que nem sempre é possível deduzir os tipos dos parâmetros, nem
mesmo a partir de uma especificação formal ou do próprio código. Um exemplo é printf, que pode ter
qualquer número de parâmetros (pelo menos um), e os parâmetros podem ser uma mistura
arbitraria de números inteiros, curtos, longos, de caracteres, de strings, de números em ponto
flutuante de diversos tamanhos e de outros tipos. Tentar chamar prinif como um procedimento
remoto seria praticamente impossível, porque C é uma linguagem muito permissiva. Porém, uma
regra estabelecendo que a RPC pode ser usada desde que você não programe em C (ou em C++) não
seria muito popular.
Um quarto problema se relaciona ao uso de variáveis globais. Normalmente, o procedimento
Chamador e o procedimento chamado podem se comunicar usando variáveis globais, além de
parâmetros. Se o procedimento chamado for agora deslocado para uma máquina remota, o código
falhara, porque as variáveis globais não serão mais compartilhadas.
Esses problemas não pretendem sugerir que a RPC é impossível. De fato, ela é amplamente
utilizada, mas são necessárias algumas restrições para faze-la funcionar bem na pratica. É claro que
a RPC não precisa usar pacotes UDP, mas RPC e UDP se adaptam bem, e o UDP é comumente utilizado
para RPC. Porém, quando os parâmetros ou resultados são maiores que o pacote máximo do UDP, ou
quando a operação solicitada não é idempotente (isto e, não pode ser repetida com segurança, como
ocorre quando se incrementa um contador), pode ser necessário instalar uma conexão TCP e enviar
a solicitação por ela, em vez de usar o UDP.

2. TCP (Transfer Control Protocol)

O UDP é um protocolo simples e tem alguns usos específicos, como interações


cliente/servidor e multimídia; porém, para a maioria das aplicações da Internet, é necessária uma
entrega confiável e em sequência. O UDP não pode proporcionar isso, e assim foi preciso criar outro
protocolo. Ele se chama TCP e é o principal elemento da Internet.

2.1 Introdução ao TCP

O TCP (Transmission Control Protocol) foi projetado especificamente para oferecer um fluxo
de bytes fim a fim confiável em uma inter-rede não confiável. Uma inter-rede é diferente de uma
única rede porque suas diversas partes podem ter topologias, larguras de banda, retardos, tamanhos
de pacote e outros parâmetros completamente diferentes. O TCP foi projetado para se adaptar
dinamicamente as propriedades da inter-rede e ser robusto diante dos muitos tipos de falhas que
podem ocorrer.
Cada máquina compatível com o TCP tem uma entidade de transporte TCP, que pode ser um
procedimento de biblioteca, um processo do usuário ou parte do núcleo. Em todos os casos, ele
gerencia fluxos e interfaces TCP para a camada IP. Uma entidade TCP aceita fluxos de dados do
usuário provenientes de processos locais, divide-os em partes de no máximo 64 Kb (na pratica, com
frequência temos 1460 bytes de dados, para que ele possa caber em um único quadro Ethernet com
os cabeçalhos IP e TCP) e envia cada parte em um datagrama IP distinto. Quando os datagramas IP
que contém dados TCP chegam a uma máquina, eles são enviados a entidade TCP, que restaura os
fluxos de bytes originais. Para simplificar, as vezes utilizamos apenas "TCP", a fim de fazer referência
tanto a entidade de transporte TCP (um software) quanto ao protocolo TCP (um conjunto de regras).
Pelo contexto, ficara claro a qual deles estaremos nos referindo. Por exemplo, em "O usuário envia os
dados para TCP", está claro que estamos nos referindo a entidade de transporte TCP.
A camada IP não oferece qualquer garantia de que os datagramas serão entregues da forma
apropriada; portanto, cabe ao TCP administrar os timers e retransmiti-los sempre que necessário. Os
datagramas também podem chegar fora de ordem; o TCP também terá de reorganiza-los em
mensagens na sequência correta. Resumindo, o TCP deve fornecer a confiabilidade que a maioria dos
usuários deseja, mas que o IP não oferece.

2.2 O modelo de serviço do TCP


O serviço TCP é obtido quando tanto o transmissor quanto o receptor criam pontos extremos
chamados soquetes. Cada soquete tem um número de soquete (endereço) que consiste no endereço
IP do host e em um número de 16 bits local para esse host, chamado porta. Porta é o nome usado pelo
TCP para um TSAP (endereços de transporte que os processos podem ouvir para receber solicitações
de conexão). Para que o serviço TCP funcione, e necessário que uma conexão seja explicitamente
estabelecida entre um soquete da máquina transmissora e um soquete da máquina receptora.
Um soquete pode ser utilizado por várias conexões ao mesmo tempo. Em outras palavras,
duas ou mais conexões podem terminar no mesmo soquete. As conexões são identificadas nas duas
extremidades pelos identificadores de soquetes, ou seja (soquete1, soquete2). Nenhum número de
circuito virtual ou qualquer outro identificador é usado.
As portas com números abaixo de 1024 são denominadas portas conhecidas e são
reservadas para serviços padrão. Por exemplo, qualquer processo que deseje estabelecer uma
conexão com um host para transferir um arquivo usando FTP pode se conectar a porta 21 do host de
destino e entrar em contato com seu daemon (programa de computador que roda de forma
independente em plano de fundo) de FTP. A lista de portas conhecidas é dada em www.iana.org. Mais
de 300 já foram atribuídas. Certamente seria possível fazer o daemon de FTP se associar a porta 21
durante a inicialização, fazer o daemon telnet se associar a porta 23 em tempo de inicialização e assim
por diante. Porém, isso ocuparia a memória com daemons que ficariam ociosos na maior parte do
tempo. Em vez disso, geralmente se tem um único daemon, chamado inetd (Internet daemon) no
UNIX que se associa a várias portas e espera pela primeira conexão de entrada. Quando isso ocorre,
o inetd ativa um novo processo e executa nele o daemon apropriado, deixando esse daemon tratar a
solicitação. Desse modo, os daemons diferentes de inetd só estão ativos quando há trabalho para eles
realizarem. O inetd descobrem que porta devem usar em um arquivo de configuração.
Consequentemente, o administrador do sistema pode configura-lo de modo a ter daemons
permanentes nas portas mais ocupadas (por exemplo, a porta 80) e inetd nas portas restantes.
Todas as conexões TCP são full-duplex e ponto a ponto. Full-duplex quer dizer que o trafego
pode ser feito em ambas as direções ao mesmo tempo. Ponto a ponto significa que cada conexão
possui exatamente dois pontos terminais. O TCP não admite os processos de multidifusão e difusão.
Uma conexão TCP e um fluxo de bytes e não um fluxo de mensagens. As fronteiras das mensagens
não são preservadas de uma extremidade a outra. Por exemplo, se o processo transmissor executar
quatro gravações de 512 bytes em um fluxo TCP, esses dados poderão ser entregues ao processo
receptor em quatro partes de 512 bytes, em duas de 1024 bytes, uma de 2048 bytes (ver Figura 6.28)
ou em qualquer outra divisão. Não há um meio do receptor detectar a(s) unidade(s) em que os dados
foram gravados.

Figura 6.28: (a) Quatro segmentos de 512 bytes enviados como datagramas IP separados. (b) Os
2048 bytes de dados entregues a aplicação em uma única chamada READ

No UNIX, os arquivos também têm essa propriedade. O leitor de um arquivo não é capaz de
distinguir se ele foi gravado um bloco por vez, um byte por vez ou todo de uma vez. A exemplo do
que acontece com um arquivo UNIX, o software TCP não tem ideia do significado dos bytes, e também
não está interessado em descobri-lo. Um byte é apenas um byte.
Quando uma aplicação repassa dados para a entidade TCP, ela pode envia-los imediatamente
ou armazena-los em um buffer (para aguardar outros dados e enviar um volume maior de uma só
vez), de acordo com suas necessidades. Entretanto, há ocasiões em que a aplicação realmente quer
que os dados sejam enviados de imediato. Por exemplo, suponha que um usuário tenha se conectado
a uma máquina remota. Depois que uma linha de comandos é preenchida e a tecla Enter (ou Carriage
Return) é pressionada, é essencial que a linha seja transportada à máquina remota imediatamente, e
não guardada no buffer até a chegada da próxima linha. Para forçar a saída dos dados, as aplicações
podem usar o flag PUSH, que informa ao serviço TCP para não retardar a transmissão.
Algumas aplicações antigas utilizavam o flag PUSH como um tipo de marcador para assinalar
os limites das mensagens. Ainda que esse artificio as vezes funcione, ele pode falhar, pois nem todas
as implementações do TCP repassam o flag PUSH para a aplicação no lado receptor. Além disso, se
outros flags PUSH chegarem antes da transmissão do primeiro (por exemplo, porque a linha de saída
está ocupada), o serviço TCP terá a liberdade de agrupar todos os dados que contiverem o flag PUSH
em um único datagrama IP, sem qualquer separação entre as várias partes.
Um último recurso do serviço TCP que vale a pena mencionar é o dos dados urgentes.
Quando um usuário interativo pressiona a tecla DEL ou as teclas CTRL- C para interromper um
processo remoto já iniciado, a aplicação transmissora adiciona algumas informações de controle ao
fluxo de dados e o entrega ao TCP juntamente com um flag URGENT. Isso faz com que o serviço TCP
pare de acumular dados e transmita tudo imediatamente. Quando os dados urgentes são recebidos
no destino, a aplicação receptor a é interrompida (na terminologia UNIX, ela recebe um sinal) e para
tudo o que estiver fazendo para ler o fluxo de dados e encontrar os dados urgentes. O final dos dados
urgentes é marcado para que a aplicação saiba quando eles terminarem. O início dos dados urgentes
não é marcado, e a aplicação deve saber identifica-lo. Basicamente, esse esquema oferece um
mecanismo de sinalização pouco sofisticado, deixando a maior parte do trabalho para a aplicação.

2.3 O protocolo TCP


Nesta seção, apresentaremos uma visão geral do protocolo TCP. Uma característica
fundamental do TCP que domina o projeto do protocolo é que cada byte em uma conexão TCP tem
seu próprio número de sequência de 32 bits. Quando a Internet começou, as linhas entre roteadores
eram principalmente linhas dedicadas de 56 kbps, e assim um host funcionando a toda velocidade
demorava mais de uma semana para percorrer todos os números de sequência. Nas velocidades das
redes modernas, os números de sequência podem ser consumidos a uma taxa alarmante, como
veremos mais adiante. São usados números de sequência de 32 bits separados para confirmações e
para o mecanismo de janelas, como descreveremos a seguir.
As entidades transmissoras e receptoras do TCP trocam dados na forma de segmentos. Um
segmento TCP consiste em um cabeçalho fixo de 20 bytes (além de uma parte opcional), seguido por
zero ou mais bytes de dados. O software TCP decide qual deve ser o tamanho dos segmentos. Ele
pode acumular dados de várias gravações em um único segmento ou dividir os dados de uma única
gravação em vários segmentos. Dois fatores restringem o tamanho do segmento. Primeiro, cada
segmento, incluindo o cabeçalho do TCP, deve caber na carga útil do IP, que é de 65.515 bytes. Em
segundo lugar, cada rede tem uma unidade máxima de transferência, ou MTU (Maximum Transfer
Unit) e cada segmento deve caber na MTU. Na pratica, a MTU geralmente tem 1500 bytes (o tamanho
da carga útil Ethernet) e, portanto, define o limite superior de tamanho de segmentos.
O protocolo básico utilizado pelas entidades TCP é o protocolo de janela deslizante. Quando
envia um segmento, o transmissor também dispara um timer. Quando o segmento chega ao destino,
a entidade TCP receptora retorna um segmento (com ou sem dados, de acordo com as circunstancias)
com um número de confirmação igual ao próximo número de sequência que espera receber. Se o
timer do transmissor expirar antes da confirmação ser recebida, o segmento será retransmitido.
Apesar desse protocolo parecer simples, há muitos detalhes sobre ele que veremos a seguir. Os
segmentos podem chegar fora de ordem; assim, os bytes 3072 a 4095 podem chegar, mas não podem
ser confirmados, porque os bytes 2048 a 3071 ainda não chegaram. Além disso, os segmentos podem
se atrasar tanto que o timer do transmissor expira e ele tem de retransmiti-los.
As retransmissões podem incluir diferentes faixas de bytes em relação a transmissão
original, exigindo uma administração cuidadosa para controlar quais bytes foram recebidos
corretamente. Porém, cada byte no fluxo tem seu próprio deslocamento exclusivo, isso pode ser feito.
O TCP deve estar preparado para lidar com todos esses problemas e resolve-los de maneira eficiente.
Foi feito um grande esforço no sentido de otimizar o desempenho dos fluxos TCP, mesmo diante dos
problemas da rede.

Potrebbero piacerti anche