Buscar

Sockets em C - BOM-proj-aplic-internet

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 83 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 83 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 83 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

Projeto e Desenvolvimento de 
Aplicações Cliente / Servidoras 
Para a INTERNET 
 
 
 
 
 
 
João Carlos Gluz 
Canoas, RS, março de 2001 
Projeto e Desenvolvimento de Aplicações para a Internet 
2 Copyright  2001 João Carlos Gluz 
Sumário 
SUMÁRIO ................................................................................................................................................ 2 
CAPÍTULO I - O MODELO CLIENTE / SERVIDOR....................................................................... 4 
1.1. MOTIVAÇÃO..................................................................................................................................... 4 
1.2. TERMINOLOGIA................................................................................................................................ 4 
1.3. COMPLEXIDADE RELATIVA .............................................................................................................. 5 
1.4. PADRONIZAÇÃO ............................................................................................................................... 6 
1.5. PROTOCOLOS DE COMUNICAÇÃO ..................................................................................................... 6 
1.6. FORMATOS DE INFORMAÇÕES .......................................................................................................... 6 
1.7. PARAMETRIZAÇÃO DE CLIENTES...................................................................................................... 7 
1.8. USO DE CONEXÕES........................................................................................................................... 7 
1.9. INFORMAÇÕES DE ESTADO ............................................................................................................... 8 
CAPÍTULO II - INTERFACE DE PROGRAMAÇÃO PARA APLICAÇÕES DE REDE ............ 9 
2.1. CONCORRÊNCIA ............................................................................................................................... 9 
2.2. PROCESSOS E PROGRAMAS............................................................................................................. 10 
2.3. COMUNICAÇÃO ENTRE PROCESSOS ................................................................................................ 11 
2.4. SINCRONISMO................................................................................................................................. 11 
2.5. INTERFACES DE PROGRAMAÇÃO..................................................................................................... 12 
2.6. INTRODUÇÃO A INTERFACE SOCKET............................................................................................... 13 
CAPÍTULO III - INTERFACE SOCKETS ......................................................................................... 16 
3.1. CRIANDO E ELIMINANDO SOCKETS .................................................................................................. 16 
3.2. ESPECIFICANDO ENDEREÇOS .......................................................................................................... 17 
3.3. ESTABELECENDO CONEXÕES.......................................................................................................... 18 
3.4. FORÇANDO A AMARRAÇÃO DE ENDEREÇOS .................................................................................... 19 
3.5. RECEBENDO CONEXÕES.................................................................................................................. 20 
3.6. TRANSFERÊNCIA DE DADOS ........................................................................................................... 21 
CAPÍTULO IV - ARQUITETURA GENÉRICA DE CLIENTES................................................... 23 
4.1. INTRODUÇÃO.................................................................................................................................. 23 
4.2. CARACTERÍSTICAS DE UMA APLICAÇÃO CLIENTE............................................................................ 23 
4.3. ALGORITMO BÁSICO DE UM CLIENTE .............................................................................................. 23 
4.4. LOCALIZAÇÃO DO SERVIDOR.......................................................................................................... 24 
4.4. IDENTIFICAÇÃO DO SERVIÇO........................................................................................................... 26 
4.5. PREENCHIMENTO DA ESTRUTURA DE ENDEREÇOS........................................................................... 28 
4.6. CRIAÇÃO DO SOCKET ...................................................................................................................... 28 
4.7. ESTABELECIMENTO DA CONEXÃO COM O SERVIDOR....................................................................... 29 
4.8. TRANSFERÊNCIA DOS DADOS.......................................................................................................... 30 
4.9. FINALIZAÇÃO DA CONEXÃO............................................................................................................ 32 
4.10. COMUNICAÇÃO POR DATAGRAMAS .............................................................................................. 32 
Projeto e Desenvolvimento de Aplicações para a Internet 
3 Copyright  2001 João Carlos Gluz 
4.11. EXEMPLO DE UM CLIENTE HTTP.................................................................................................. 35 
CAPÍTULO V - ARQUITETURA DE APLICAÇÕES SERVIDORAS.......................................... 39 
5.1. ALGORITMO BÁSICO....................................................................................................................... 39 
5.2. CONCORRÊNCIA VERSUS ITERATIVIDADE NO TRATAMENTO DAS REQUISIÇÕES ............................... 40 
5.3. O USO DE CONEXÕES NO TRATAMENTO DAS REQUISIÇÕES.............................................................. 40 
5.4. QUANDO USAR CADA TIPO DE SERVIDOR ........................................................................................ 42 
5.5. EXEMPLO DE UM SERVIDOR HTTP ................................................................................................. 42 
CAPÍTULO VI – O PROTOCOLO HTTP......................................................................................... 57 
6.1. OPERAÇÃO GERAL DO PROTOCOLO................................................................................................ 57 
6.2. NOTAÇÃO USADA PARA APRESENTAR O FORMATO DAS MENSAGENS .............................................. 58 
6.2.1. Definição de um Formato....................................................................................................... 58 
6.2.2. Opções de Formatos............................................................................................................... 58 
6.2.3. Caracteres / Símbolos Especiais ............................................................................................ 60 
6.2.4. Elementos Básicos.................................................................................................................. 60 
6.3. FORMATO BÁSICO DAS MENSAGENS ............................................................................................... 60 
6.4. MENSAGENS DE REQUISIÇÃO ......................................................................................................... 63 
6.4.1. Métodos das Requisições........................................................................................................ 63 
6.4.2. Identificador de Recurso da Requisição................................................................................. 64 
6.4.3. Cabeçalhos de Requisição...................................................................................................... 65 
6.5. MENSAGENS DE RESPOSTA.............................................................................................................66 
6.5.1. Linha de Estado da Resposta ................................................................................................. 66 
6.5.2. Códigos de Estados e sua Descrições .................................................................................... 66 
6.5.3. Cabeçalhos de Resposta......................................................................................................... 68 
6.6. ENTIDADE ...................................................................................................................................... 68 
6.7. DESCRIÇÃO DOS MÉTODOS ............................................................................................................ 69 
6.7.1. GET........................................................................................................................................ 69 
6.7.2. HEAD..................................................................................................................................... 69 
6.7.3. POST...................................................................................................................................... 69 
6.8. CÓDIGOS DE ESTADO ..................................................................................................................... 70 
6.9. CAMPOS DOS CABEÇALHOS............................................................................................................ 72 
6.9.1. Cabeçalho Geral .................................................................................................................... 72 
6.9.2. Cabeçalho das Requisições .................................................................................................... 73 
6.9.3. Cabeçalho das Respostas ....................................................................................................... 74 
6.9.4. Cabeçalho das Entidades ....................................................................................................... 75 
6.10. AUTENTICAÇÃO DE ACESSO......................................................................................................... 77 
CAPÍTULO VII - REQUISIÇÕES HTTP COM FORMUIÁRIOS CODIFICADOS ................... 79 
7.1. MÉTODOS APLICÁVEIS ................................................................................................................... 79 
7.2. ALGORITMO DE CODIFICAÇÃO ....................................................................................................... 79 
7.3. EXEMPLO ....................................................................................................................................... 80 
7.4. SUGESTÕES DE TRABALHOS ........................................................................................................... 80 
7.4.1. Implementação do Tratamento de Formulários e CGI no WebServ ...................................... 80 
7.4.2. Implementação de páginas ativas no WebServ ...................................................................... 82 
 
Projeto e Desenvolvimento de Aplicações para a Internet 
4 Copyright  2001 João Carlos Gluz 
Capítulo I - O Modelo Cliente / Servidor 
A pilha de protocolos TCP/IP provê um serviço genérico fim-a-fim de comunicação entre pares (peer-
to-peer)de aplicações ou processos, mas não define como esta comunicação será organizada entre os 
pares de aplicações. 
O modelo ou paradigma dominante (quase que único) para organizar a comunicação entre as aplicações é 
o modelo denominado: 
CLIENTE / SERVIDOR 
1.1. Motivação 
A principal motivação por trás do uso do modelo cliente/servidor tem a ver com o problema de 
sincronização da comunicação entre as aplicações tentando interagir, principalmente se se levar em conta 
a velocidade com que são executadas as aplicações e também são atendidas as solicitações de 
comunicação pela rede. 
O modelo divide as aplicações em duas grandes classes ou categorias: 
• Clientes: sempre iniciam o processo de comunicação 
• Servidores: sempre esperam que uma (ou mais) aplicação inicie a 
 comunicação 
Além disso este modelo resolve uma questão importante relacionada ao funcionamento do TCP/IP: 
A pilha TCP/IP não fornece nenhum tipo de mecanismo para ativação (indicação na terminologia 
OSI/ISO) de aplicações quando da chegada de alguma mensagem particular, portanto deve 
sempre haver uma aplicação já executando esperando pelas mensagens que chegam da 
rede. Pela própria maneira como se organizam as aplicações no modelo cliente/servidor este 
problema fica naturalmente resolvido. 
1.2. Terminologia 
Clientes e servidores são aplicações, programas ou processos do sistema operacional, isto é, são 
softwares. Ocasionalmente, entretanto, a terminologia é aplicada, em particular o termo servidor, 
diretamente para as máquinas que estão executando alguma aplicação servidora. Para não haver confusão 
estas máquinas serão denominadas no texto de máquinas servidoras e não de servidores. O termo 
servidor será reservado apenas para as aplicações. 
A divisão entre estas duas classes de aplicações será a mesma vista anteriormente: 
Projeto e Desenvolvimento de Aplicações para a Internet 
5 Copyright  2001 João Carlos Gluz 
Os clientes serão sempre as aplicações que começam o processo de comunicação, enviando usualmente 
uma ou mais requisições para um servidor remoto. 
Por sua vez os servidores serão os programas que esperarão por estas requisições, recebidas 
(tipicamente) através da rede de comunição que os liga com os clientes remotos. 
Implicitamente se assumirá que as aplicações cliente/servidoras serão remotas umas das outras, embora 
isto não seja mandatório (por exemplo em caso de testes pode-se executar tanto o cliente quanto o 
servidor na mesma máquina). 
A troca destas informações entre o par cliente / servidor se dará através de um protocolo de aplicação 
que é apenas um protocolo de comunicação definido especificamente para tratar dos problemas e 
questões envolvidas na interação do cliente com o servidor. No caso da Internet a identificação do 
protocolo de aplicação (e do serviço correspondente) será feita através de portas “lógicas” 
disponibilizadas pelo protocolo TCP (ou UDP). 
Os servidores irão implementar um serviços que serão acessados remotamente pelos clientes através dos 
protocolos de aplicação. Normalmente os protocolos de aplicação, em aplicações cliente / servidoras, são 
organizadas em dois tipos de mensagens: 
• mensagens de requisição ou solicitação de serviços, que são enviadas dos clientes aos servidores 
• mensagens de resposta, que são enviadas dos servidores aos clientes, em resposta as requisições. 
As requisições enviadas pelos clientes solicitam aos servidores que executem algum tipo de tarefa. Um 
servidor ao receber uma requisição de um cliente deverá executar uma ação correspondente e enviar a 
mensagem de resposta indicando que ação foi tomada. Por exemplo num dado instante o servidor pode 
realmente executar uma tarefa que atenda a solicitação feita pelo cliente, porém num outro instante de 
tempo, pelo fato de já estar sobrecarregado de tarefas, o servidor pode não fazer nada e apenas avisar isto 
ao cliente. 
Uma outra questão importante relacionada aos protocolos de aplicação tem a ver com os formatos das 
mensagens trocadas entre o cliente e o servidor. Em geral, além de ser necessário especificar os formatos 
de requisições, respostas, etc., pode também ser necessário especificar o formato dos dados ou 
informações que serao trocados dentro destas mensagens, principalmente quando as questões de formato 
de informações forem realmente importantes para a aplicação. Neste caso será necessário definir 
formatos de representação de informações apropriados e que sejam independentes de arquiteturas de 
máquinas ou sistemas operacionais onde os clientes e servidores estão implementados. 
1.3.Complexidade Relativa 
Os servidores serão normalmente as aplicações mais complexas de se implementar neste par de 
aplicações por várias razões: 
Servidores usualmente deverão executar com privilégios especiais dentro do sistema operacional não só 
porque terão que interagir intimamente com o subsistema de rede do S.O. (por ter que esperar 
Projeto e Desenvolvimento de Aplicações para a Internet 
6 Copyright  2001 João Carlos Gluz 
requisições) mas também porque muitas vezes para atender as requisições remotas terão de a necessidade 
de acessar informações privilegiadas do S.O. e ocasionalmente ter que executar computações laboriosas. 
Porém ao executar operações em modo privilegiado os servidores deverão implementar código para 
executar as seguintes tarefas: 
• Autenticação através da verificação da identidade dos usuários (clientes remotos) 
• Autorização ou não das requisições de acordo com o perfil do usuário 
• Garantir a segurança no acesso aos dados dos usuários 
• Garantir a privacidade das informações fornecidas pelos usuários 
• Proteçao aos recursos do S.O. 
Além disso quando for necessário a execução de computações laboriosas ou extensas o para atender uma 
dada requisição será necessário utilizar recursos de processamento concorrente do S.O. complicando 
ainda mais a implementação dos servidores. 
1.4. Padronização 
As aplicações cliente/servidoras podem ainda ser classificadas em relação ao fato de serem baseadas 
sobre serviços ou protocolos padronizados (well-known ports definidas através de RFC/STD específica) 
ou serem baseadas sobre serviços não padrão de uso local a uma organização ou de propriedade de 
alguma corporação. 
1.5. Protocolos de Comunicação 
Os protocolos de comunicação entre os pares de clientes e servidores definem formalmente como será a 
troca de informações entre eles. Estes protocolos podem ser padronizados ou não com visto 
anteriormente, mas também podem ser genéricos ou específicos, no sentido de que poderão ser usados 
apenas para um dado serviço (protocolo mais específico) ou poderão ser usados para suportar uma ampla 
variedade de serviços distintos (mais genérico). 
 Por exemplo o protocolo TELNET pode ser usado para suportar praticamente qualquer tipo de serviço 
cuja interação com os usuários esteja baseada no paradigma de terminal de texto remoto. 
Por outro lado um protocolo como o FTP tem um uso bem mais restrito, sendo utilizado essencialmente 
(como o próprio nome indica) apenas para a transferência de arquivos. 
1.6. Formatos de Informações 
Geralmente são tratadas de forma independente as questões envolvendo a especificação dos protocolos 
usados na comunicação cliente/servidor e as questões envolvendo o formato das informações trocadas 
entre estas aplicações. Não que estas questões sejam totalmente independentes, mas apenas que um 
tratamento diferenciado destes dois tipos de questões facilita a generalização do uso do protocolo, uma 
Projeto e Desenvolvimento de Aplicações para a Internet 
7 Copyright  2001 João Carlos Gluz 
vez que a falta de geralidade de uso de um protocolo está mais intimamente ligada aos formatos de 
informação que podem ser intercambiados dentro do protocolo do que com a as mensagens de controle 
usadas pelo mesmo. 
Sendo assim atualmente se define um formato padrão único para o “envelope” destas mensagens e se 
define um formato extensível para o conteúdo destas mensagens. 
Exemplos de pares de protocolo / formato: 
TELNET / VT-100 
HTTP / HTML 
SMTP / MIME 
SNMP / MIB-II 
1.7. Parametrização de Clientes 
Quando uma aplicação clientes é definida, normalmente vale a pena permitir que a mesma possa ser 
parametrizada para uma ampla gama de aplicações. Pelo menos a especificação do endereço e porta a ser 
usada no servidor remoto devem poder ser parametrizados. 
A combinação de um cliente flexível bastante parametrizável com protocolos e formatos de mensagens 
genéricos permite a criação de verdadeiras aplicações clientes multifuncionais. Por exemplo existem 
inúmeros tipos de serviços disponibilizados por máquinas servidoras que caem dentro do paradigma de 
terminal de texto interativo remoto (desde o login remoto do shell até interfaces orientadas a menus ou 
formulários de aplicações específicas), sendo que se pode usar o cliente TELNET para o acesso a estes 
diferentes serviços, desde que se possa parametrizar não só o endereço da máquina servidora, mas 
também a porta TCP e provavelmente o tipo de emulação de terminal requerido. 
Um exemplo mais recente de aplicação cliente multifuncional é o dos clientes HTTP/HTML 
(NetscapeTM, IExplorerTM, etc.) que hoje servem como front-end para um sem número de aplicações e 
serviços (na verdade quase que substituindo o antigo paradigma de terminal de texto). 
1.8. Uso de Conexões 
O termo conexão dentro de redes de computadores tem uma semântica bem específica relacionado a 
criação e uso de um canal de transporte fim-a-fim robusto, confiável e ordenado (uma espécie de “tubo” - 
pipe) para a troca de informações entre o par de aplicações. Serviços orientados a conexão são então 
serviços que faze uso deste tipo de canal de comunicação robusto e confiável. Na arquitetura da Internet 
este é o serviço baseado no protocolo TCP. 
Por outro lado as redes de comunicação geralmente também disponibilizam um tipo de serviço de 
transporte mais segmentado, orientado a mensagens individuais que normalmente não garante nem a 
confiabilidade na entrega e nem o ordenamento correto de seqüência de mensagens, apesar de ser 
Projeto e Desenvolvimento de Aplicações para a Internet 
8 Copyright  2001 João Carlos Gluz 
baseado no conceito de melhor esforço possível de entrega (best-effort delivery). Na arquitetura da 
Internet este serviço é fornecido através do protocolo UDP. 
Um dos pontos mais importantes que deve ser decidido quando uma aplicação cliente/servidora está 
sendo especificada é que tipo de serviço de transporte se irá usar: com suporte a conexões ou sem suporte 
a conexões. 
Apesar do fato do uso de conexões implicar no uso de maiores recursos de processamento e memória por 
parte do sistema operacional, tem-se como regra geral para implementação de aplicações 
cliente/servidoras que se deve sempre usar o serviço com suporte a conexões (também chamado de 
serviço orientado a conexão) exceto nos seguintes casos: 
1. O protocolo de troca de informações entre o cliente e o servidor já foi especificado (numa RFC 
por exemplo) e exige o uso de UDP (trata por sua própria conta os problemas de confiabilidade e 
ordenação). 
2. Este tipo de aplicação requer o uso de difusão (broadcast) de mensagens. 
3. O custo, em termos de recursos de processamento e memória, necessário para se criar e manter as 
conexões é realmente inaceitável para a aplicação. 
1.9. Informações de Estado 
As informações mantidas por um servidor sobre como estão as interações com os seus clientes remotos é 
denominada de informações de estado. Entretanto a manutenção e armazenamento deste tipo de 
informação não são obrigatórios, ou seja, existem servidores que não precisam manter informações sobre 
o estado das suas diversas interações. 
A necessidade ou não de um servidor de manter o estado das interações é, na verdade, uma questão de 
especificação de protocolos: se uma dada mensagem de protocolo somente puder ser corretamente 
interpretada em função de mensagens recebidas anteriormente então é praticamente impossível construir 
um servidor que não mantenha informações de estado. 
Por outro lado o armazenamento das informações pode reduzir em muito as necessidades de 
comunicação entre o cliente e o servidor, por permitir que as mensagens façam referência a um contexto 
de comunicação prévio. Caso contrário seria necessário que cada mensagem encapsula-se todas as 
informações necessáriaspara o atendimento de uma dada requisição (operações idempotentes). 
Contudo, o uso de informações de estado pode ocasionar sérios problemas se houverem problemas de 
comunicação entre as duas aplicações. Se mensagens forem perdidas ou duplicadas, as informações de 
contexto guardadas no servidor poderão se tornar inválidas sem que ele necessariamente o reconheça. 
Além disso se o cliente sofrer uma pane, for reinicializado e a comunicação com o servidor for 
restabelecida sem que o servidor tenha sido informado da pane anterior então as informações de estado 
armazenadas no servidor serão totalmente errôneas. 
Projeto e Desenvolvimento de Aplicações para a Internet 
9 Copyright  2001 João Carlos Gluz 
Capítulo II - Interface de Programação para 
Aplicações de Rede 
2.1. Concorrência 
Genericamente o termo concorrência se refere a execução de um conjunto de computações que parecem 
estar sendo executadas de forma simultânea. Estas computações podem estar realmente sendo 
executadas simultaneamente (ou seja sendo executadas em paralelo), ou a sua simultaneidade pode estar 
sendo simulada através de algum mecanismo de multiplexação de uso da CPU (simulação de paralelismo 
através da execução em tempo-compartilhado ou time-sharing). Entretanto, para os usuários e 
programadores , a diferença entre paralelismo simulado e o real é praticamente nula. 
Numa rede, as aplicações rodando em diferentes máquinas estão efetivamente executando em paralelo 
uma em relação as outras. A rede fornece o mecanismo de intercomunicação destas aplicações e deve 
garantir, entre outras, coisas que a comunicação entre um dado par de aplicações não interfira na 
comunicação de outro par. Por exemplo supondo a existência de 4 aplicações: A, B, C e D rodando em 
quatro diferentes máquinas MA, MB , MC e MD , de acordo com a figura abaixo: 
A B
C D
M
 A
M
 B
M
 C
M
 C
 
Neste caso as aplicações estão realmente executando em paralelo e a troca de informações entre elas está 
sendo feita de forma realmente simultânea. 
Dentro de uma máquina ou computador também pode existir concorrência (tanto simulada quanto real). 
Por exemplo numa dada estação de trabalho, um usuário pode ter diferentes clientes sendo executados 
concorrentemente (um ou mais acessos FTP, um acesso TELNET, várias páginas HTML abertas e em 
processo de transferência, etc.). Atualmente os Sistemas Operacionais (SO) modernos, disponíveis nas 
nossas estações de trabalhos e computadores pessoais já disponibilizam o processamento concorrente de 
forma natural para as aplicações clientes. 
Projeto e Desenvolvimento de Aplicações para a Internet 
10 Copyright  2001 João Carlos Gluz 
No caso das aplicações e máquinas servidoras a concorrência é ainda mais crucial para garantir um certo 
nível de eficiência e desempenho. Por exemplo numa dada máquina servidora de arquivos certamente 
que a concorrência é importante para garantir que um cliente remoto não fique esperando que os outros 
(p. ex.) 100 usuários anteriores terminem as transferências dos seus arquivos para, somente então, ser 
atendido. 
Porém em relação às aplicações servidoras, a concorrência geralmente não é naturalmente criada pelo 
S.O. devendo ser implementada na própria aplicação servidora pelo programador. Isto incorpora um 
grande grau de complexidade no projeto e implementação de aplicações servidoras. Contudo este tipo de 
cuidado na implementação é realmente de crucial importância, podendo fazer toda a diferença entre um 
servidor bem aceito pelo mercado e com excelentes características e um servidor que seja um virtual 
fracasso em termos de mercado. 
2.2. Processos e Programas 
Em sistemas concorrentes o termo (ou conceito) de processo define a unidade fundamental de 
computação. Diferente do conceito de programa o conceito de processo evoca um significado mais 
dinâmico e ativo: um processo é um programa que está realmente em estado de execução numa dada 
máquina num dado intervalo de tempo. 
Entretanto a relação entre processos e programas é bastante próxima, uma vez que um programa pode ser 
entendido com a especificação precisa de como um dado processo deve executar. Por exemplo um 
programa contém código e dados tipicamente armazenados um dado arquivo. Quando um usuário quer 
que o SO execute um dado programa, na verdade, o que o SO faz em resposta a esta solicitação é criar 
um processo com as informações fornecidas pelo arquivo de programa. Entre outras tarefas menores o 
SO criará este novo processo pelos seguintes atividades: 
• uma área de código será alocada na memória e o código carregado nela, se porventura uma outra 
instância ou processo deste programa já não estiver em execução; 
• uma área de memória será alocada especificamente para este processo; 
• uma estrutura de dados contendo uma descrição do estado do processo (um um descritor de processo) 
será criada no núcleo do SO para manter o controle de estado da execução do processo. 
Finalmente se não houver nenhum outro processo mais prioritário em execução o novo processo será 
ativado. 
Nota: atualmente está se tornando muito comum tanto o termo quanto o uso de threads ou linhas de 
execução, como unidade de execução concorrente mínima. Na verdade o termo ou conceito de threads 
está mais relacionado às características que alguns SOs de uso geral, como o Linux e o Windows NT, 
apresentam quando da criação dos seus processos, do que a alguma diferença fundamental entre este 
conceito e o conceito de processo. Nestes SOs as threads nada mais são do que apenas “processos leves” 
(que, aliás, é um termo usado como sinônimo de linha de execução ou thread) que executam no mesmo 
espaço de dados que o processo “completo” original e que, portanto, são mais “leves” para criar e não 
exigem tantos recursos do SO para a sua execução. 
Projeto e Desenvolvimento de Aplicações para a Internet 
11 Copyright  2001 João Carlos Gluz 
2.3. Comunicação entre Processos 
Existem três grandes formas de processos diferentes se comunicarem entre si. Dependendo tanto do SO 
subjacente da arquitetura da máquina em que estão em execução, a comunicação entre os processo 
poderá ser feita através de: 
1) Uso de memória compartilhada. 
2) Troca de mensagens independentes de informação. 
3) Estabelecimento de conexões de comunicação. 
A primeira forma, memória compartilhada, geralmente é usada apenas no caso de processos em execução 
numa arquitetura paralela. No caso de aplicações distribuídas numa rede, são mais usadas as duas últimas 
formas de comunicação. 
O TCP/IP permite o uso de qualquer uma das duas: 
• a troca de mensagens pode ser facilmente implementada tanto através do UDP quando as 
exigências de confiabilidade e sincronismo não são tão grandes. Caso sejam pode-se implementar 
a troca confiável e síncrona através do TCP, 
• a função mais importente desempenhada pelo TCP é justamente a criação e estabelecimento de 
conexões de comunicação confiáveis e robustas entre pares de aplicações. 
2.4. Sincronismo 
Tanto no caso da comunicação por mensagens, mas principalmente na comunicação por conexões, 
existe um requisito básico de “acerto” ou “acordo” temporal entre os pares de aplicações, que é, na 
verdade, um problema relacionado a como garantir a sincronização entre estes processos, ou seja, como 
garantir que eles estejam tanto executando quando se comunicando simultaneamente. 
Uma forma razoável de resolver este problema seria prevendo nos diversos tipos de serviço de rede a 
serem providos pelo SO, um mecanismo de sinalização ou de “ativação automática” de um processo / 
programa quando do recebimento de algum determinado tipo de mensagem ou conexão da rede. Porém, 
por várias razões (inclusive por problemas de segurança) o TCP/IP não prevê nenhuma forma de 
sinalização ou ativação automática de processos. Sendoassim somente as mensagens ou conexões que já 
tenham algum processo ativo esperando por elas é que serão efetivamente tratadas. As demais mensagens 
serão descartadas e as conexões recusadas. 
O modelo cliente/servidor já prevê uma solução apropriada para este problema de sincronismo com a 
divisão do universo de aplicações em clientes que sempre começam a comunicação e servidoras que 
estarão sempre esperando pelo estabelecimento de comunicação de algum cliente remoto. 
Para que seja possível implementar isto na programação em TCP/IP, o recurso que o SO deverá 
disponibilizar é algum mecanismo de registro que permita que um processo avise para o SO quais tipos 
de conexões ou mensagens (portas no caso do TCP) estará disposto a receber. 
Projeto e Desenvolvimento de Aplicações para a Internet 
12 Copyright  2001 João Carlos Gluz 
2.5. Interfaces de Programação 
O TCP é como o próprio nome indica apenas um protocolo e não uma interface de programação de 
aplicações. O TCP normalmente é implementado através de um conjunto de processos ou rotinas do SO, 
que constituem o módulo ou entidade de controle da camada de transporte de rede. 
O protocolo TCP serve justamente para definir precisamente como será forma de comunicação entre as 
entidades de transporte de um par de máquinas remotas, mas não foi feito para definir como estas 
entidades de controle de transporte irão se comunicar com os processos de aplicação. 
A definição de como este tipo de comunicação entre o módulo de controle do TCP e os diversos 
processos de aplicação deve ser feita através de uma Interface de Programação de Aplicação (sigla em 
inglês API - Applications Programming Interface) específica que disponibiliza para os programadores os 
serviços do módulo TCP. 
Reduzida as suas características mínimas uma API é pouco mais do que uma lista que especifica quais 
são as rotinas e funções de acesso a estes serviços. Quando necessário também é parte constituinte de 
uma API a especificação dos tipos de dados adicionais necessários para a implementação destes serviços. 
Do ponto de vista mais moderno uma API é na verdade a porção visível ao programador de um Tipo 
Abstrato de Dados ou de uma Classe de Objetos (Programação Orientada a Objetos). 
Quando o TCP/IP foi incorporado originalmente aos sistemas UNIX, não foi criada imediatamente uma 
nova API para uso dos serviços destes protocolos. Na época se assumiu que os serviços de rede 
disponibilizados pelo TCP (notadamente o estabelecimento de conexões) seriam, grosso modo, 
equivalentes aos serviços fornecidos pelo sistema de arquivos (paradigma open-read-write-close de 
manipulação arquivos). 
Aplic.
A
Aplic.
B
Arquivo de A para B
Arquivo de B para A 
Isto não é tão disparatado quanto possa parecer, porque a comunicação através de uma conexão entre 
duas aplicações pares pode realmente ser idealizada como sendo feita através de operações de entrada e 
saída padrão sobre um par de arquivos: um para saída da aplicação A e entrada na B e o outro para o 
caminho inverso. 
Porém este tipo de abstração, embora de bastante valia e ainda presente como um subconjunto da 
interface atual de desenvolvimento de aplicações TCP/IP, apresenta sérias limitações no que tange a 
sincronização entre os pares de aplicações. 
Projeto e Desenvolvimento de Aplicações para a Internet 
13 Copyright  2001 João Carlos Gluz 
Foi justamente para resolver este problema que a Universidade da Califórnia em Berkeley criou a 
interface sockets de programação e uso dos serviços da camada de transporte de rede. A interface sockets 
se adapta muito bem ao uso do protocolo TCP/IP para comunicação com máquinas remotas, mas nada 
impede que outros protocolos de transporte sejam usados (ISO TP4 ou Novell IPX/SPX por exemplo), 
ou que o serviço não possa ser implementado como um mecanismo local de intercomunicação dos 
processos internos a uma máquina. 
2.6. Introdução a Interface Socket 
Tanto o TCP quanto o UDP, que são os dois protocolos de transporte usados na Internet, implementam 
uma série de “portas” de comunicação dentro de um dado computador ou máquina. Estas portas 
TCP/UDP são, na verdade, pontos internos de atendimentos dos serviços de transporte. 
O TCP/UDP suporta até 65.535 portas distintas porque é usado um campo de 16 bits para identificação 
de porta nestes protocolos. Na estrutura de intercomunicação prevista pelo TCP/IP os serviços e 
protocolos criados pelas aplicações de rede deverão obrigatoriamente ser mapeados nestas portas. 
Os serviços de aplicação padronizados e de uso público são identificados através de números de portas 
TCP ou UDP reconhecidas publicamente (em inglês well known ports). Tanto as portas quanto os 
serviços reconhecidos publicamente são definidos através de documentos específicos para isto: as RFCs 
(Request For Comments) publicados pela IETF (Internet Engineering Task Force) no seu próprio site 
(www.ietf.org). 
Nada impede, entretanto, que se possa definir e usar uma porta qualquer para um serviço proprietário, 
exceto se esta porta já estiver sendo usada nos computadores em questão para um outro serviço. 
Um outro detalhe interessante relacionado ao uso das portas é que, embora não haja nenhuma obrigação 
para que os serviços fornecidos pelo TCP usem as mesmas portas que os fornecidos através do UDP, 
tem-se mantido, pelo menos nos serviços públicos que são fornecidos pelos dois protocolos, a 
compatibilidade entre os números de porta. 
O paradigma básico de trabalho no sockets é que a troca de informações entre um par de aplicações deve 
ser feita por uma conexão de comunicação. Uma vez que esta conexão seja estabelecida todos os dados 
enviados por uma aplicação são recebidos pela outra, na mesma ordem em que foram enviados. O 
serviço de sockets não prevê nenhum tipo de estruturação dos dados além do nível de caractere, ou seja, o 
sockets não faz nenhuma pressuposição sobre como será o formato de mensagens ou registros enviados 
pela conexão. Se houver algum tipo de estruturação nos dados além do nível de caractere então ela 
deverá ser tratada pelas aplicações que estarão usando os sockets. 
Projeto e Desenvolvimento de Aplicações para a Internet 
14 Copyright  2001 João Carlos Gluz 
A E
C
D
 B
Cada um é um
socket distinto
 
São previstas na interface sockets mecanismos para estabelecer (abrir) conexões e encerrar (fechar) 
conexões. Também são previstos mecanismos que permitem a um processo de aplicação se registrar 
junto ao SO indicando qual ou quais portas este processo estará usando tanto para o recebimento de 
mensagens quanto ao envio. Por último também se pode indicar se o uso destas portas será feito de forma 
ativa pelo processo, isto é, o processo começará a usá-las abrindo conexões com uma máquina remota 
(caso típico de um cliente) ou então será empregado passivamente através da espera da chegada de 
conexões (caso típico de um servidor). 
A identificação completa de uma conexão socket de uma máquina A para uma máquina B, dentro do 
universo de todas as conexões sockets possíveis numa Internet, é formada por quatro números ou 
endereços distintos: 
IP-A: Endereço IP da máquina A 
Porta-A: Nro. de porta TCP ou UDP da máquina A 
IP-B: Endereço IP da máquina B 
Porta-B: Nro. de porta TCP ou UDP da máquina B. 
Na figura a seguir pode-se ver a relação entre portas internas e endereço IP. Também fica claro que um 
socket é idenficado de forma única somente pelos endereços de IP e porta das máquinas de origem e 
destino: 
Projeto e Desenvolvimento de Aplicações para a Internet 
15 Copyright  2001 João Carlos Gluz 
 P1
 P2
 IP1 P3
 P4
 P5
 P10
 P11 IP2
 P12
 P1
 P2 IP3
São 5 sockets distintos:
IP1-P1-IP2-P10
IP1-P2-IP2-P10
IP1-P3-IP2-P12
IP3-P1-IP2-P12
IP1-P5-IP3-P2Projeto e Desenvolvimento de Aplicações para a Internet 
16 Copyright  2001 João Carlos Gluz 
Capítulo III - Interface Sockets 
3.1. Criando e eliminando sockets 
A primitiva usada para criar um socket é a seguinte: 
 sock_descr = socket( protocol_family, type, protocol ) 
onde os parâmetros protocol_family e protocol definem, respectivamente, a família de 
protocolos e, caso existam vários protocolos nesta família, qual o protocolo específico. Para o caso da 
arquitetura TCP/IP basta usar PF_INET como valor de protocol_family e 0 como valor de 
protocol. O parâmetro type define o tipo de serviço que se pretende empregar no socket sendo 
criado. Para o uso na Internet são possíveis dois tipos de serviço: 
SOCK_STREAM serviço orientado a conexão baseado no protocolo de transporte TCP 
SOCK_DGRAM serviço orientado a datagrama, baseado no protocolo de transporte UDP 
O parâmetro retornado sock_descr irá conter o identificador do descritor do novo socket sendo criado, 
caso não tenha ocorrido nenhum erro na criação do socket. Entretanto se o socket não puder ser criado 
então a primitiva socket irá retornar um valor negativo. 
Quando um socket não necessitar mais ser usado deve-se chamar a primitiva close1: 
 close( sock_descr ) 
para informar ao S.O. que o socket não está mais em uso. 
 
 // Tenta criar um socket TCP 
 sock_descr = socket( PF_INET, SOCK_STREAM, 0 ). 
 if (sock_descr < 0) 
 { 
 printf(“Erro: socket nao pode ser criado”); 
 exit(0); 
 } 
 
 
 
1 A interface WinSockets do Microsoft Windows (TM) usa a primitiva closesocket em vez de close 
Projeto e Desenvolvimento de Aplicações para a Internet 
17 Copyright  2001 João Carlos Gluz 
3.2. Especificando endereços 
A primitiva socket cria um socket novo, ou seja, aloca os recursos necessários do S.O. para uso de um 
socket e também avisa ao S.O. que se vai passar a usar comunicação por sockets neste processo. Esta 
primitiva, entretanto, não estabelece nenhuma conexão com uma máquina remota. 
Para estabelecer conexões (ou enviar mensagens no caso do serviço orientado a datagramas) é necessário 
especificar o endereço IP da máquina remota, bem como o identificador de porta desta máquina remota. 
Isto é feito através das estruturas: sockaddr e sockaddr_in. A primeira destas (sockaddr) 
descreve o formato genérico dos endereços que podem ser usados na interface sockets e a segunda 
(sockaddr_in) descreve o formato de endereços específico para a Internet. Como neste texto o 
enfoque principal é em programação sockets sobre a Internet, somente o formato sockaddr_in será 
apresentado: 
 
 struct in_addr 
 { 
 uint32_t s_addr; 
 }; 
 
 struct sockaddr_in 
 { 
 unsigned char sin_len; /* tamanho total */ 
 unsigned short sin_family; /* tipo do endereco, deve 
 ser AF_INET */ 
 unsigned short sin_port; /* porta TCP ou UDP */ 
 struct in_addr sin_addr; /* endereco IP */ 
 char sin_zero[8]; /* nao usado (zerado) */ 
 }; 
 
O campo sin_len define o tamanho completo do endereço (somente é usado em familias de protocolos 
com endereços de tamanho variável, pode ser deixado em 0 no caso da Internet). 
O campo sin_family define a família de protocolos do endereço (para Internet deve ser AF_INET). 
O campo sin_port define qual porta TCP ou UDP será usada e o campo sin_addr define o endereço IP do 
host a ser contatado. 
Importante: 
Tanto a porta quanto o endereço devem estar em formato de dados da rede. Portanto caso se esteja 
alterando o valor destes campos a partir de variáveis internas do tipo short ou long, deve-se 
obrigatoriamente usar as primitivas de conversão: htons e htonl, que convertem, respectivamente, os 
tipos short e long do formato de dados de host para o formato de dados da rede. 
Projeto e Desenvolvimento de Aplicações para a Internet 
18 Copyright  2001 João Carlos Gluz 
 
 ... 
 
 sra.sin_port = htons( 80 ); /* vai usar a porta 80 - HTTP */ 
 sra.sin_addr.s_addr = inet_addr(“200.238.29.30”); 
 
 ... 
 
O contrario também é verdadeiro, para se armazenar estes campos em variáveis internas que depois 
poderão ser impressas (corretamente) deve-se usar as primitivas de conversão: ntohs e ntohl, que 
convertem, respectivamente, os tipos short e long do formato de dados de rede para o formato de 
dados do host. 
A rotina inet_addr usada no exemplo converte uma string contendo um endereço IP em notação ponto-
decimal para o endereço de binário correspondente, já no formato long da Internet. 
3.3. Estabelecendo conexões 
De posse do novo socket e do endereço da máquina e porta remota que se pretende contatar (devidamente 
armazenados numa estrutura sockaddr_in) é possível então estabelecer uma conexão (socket) com a 
máquina remota se se optou pelo serviço orientado a conexões (baseado no TCP). 
É importante notar que o ato de estabelecer ativamente uma conexão com uma máquina remota, 
caracteriza a aplicação como sendo cliente (somente clientes começam a interação, servidores ficam 
esperando pela chegada das solicitações). Sendo assim, somente clientes irão tentar estabelecer a conexão 
inicial. 
A primitiva para se estabelecer conexões no sockets é a seguinte: 
 result = connect( sock_descr, ptr_rem_addr, addrlen ) 
onde sock_descr contém o descritor de um socket previamente criado, ptr_rem_addr é um 
apontador para uma estrutura sockaddr_in que contém o endereço IP e a porta da máquina remota e 
addrlen é fixo devendo ser preenchido com sizeof( sockaddr_in ). 
Esta primitiva tentará estabelecer uma conexão TCP com a máquina remota. Se for bem sucedida deverá 
retornar 0, caso contrário retornará -1. O endereço IP e porta da máquina remota são fornecidos por 
ptr_rem_addr, porém para o endereço do lado local do socket, se nada mais for especificado, o S.O. 
usará o endereço IP local do host como endereço IP do lado local. Quanto a porta, também se nada mais 
for especificado, o S. O. usará a primeira porta TCP que não está sendo utilizada algum outro processo e 
disponibiliza esta porta para estabelecer a conexão. 
Projeto e Desenvolvimento de Aplicações para a Internet 
19 Copyright  2001 João Carlos Gluz 
 
 ... 
 res = connect( sock_descr, &sra, sizeof( sockaddr_in )); 
 if (res<0) 
 { 
 printf(“Erro: nao conseguiu estabelecer conexao!”); 
 exit(0); 
 } 
 ... 
 
 
3.4. Forçando a amarração de endereços 
Do ponto de vista de redes, do lado do cliente pouca coisa é necessário fazer além de criar um socket, 
estabelecer uma conexão com um servidor remoto, transferir dados de/para o servidor e encerrar o 
socket. Porém do lado do servidor as coisas são um pouco mais complicadas. 
Em primeiro lugar, como o servidor irá ficar esperando passivamente por conexões (ou mensagens 
datagramas) em determinadas portas é necessário especificar quais serão estas portas, ou seja, não se 
pode deixar a cargo, como no caso do cliente, do S.O. a definição de que porta usar. 
Além disso servidores são, normalmente, máquinas muito poderosas que muitas vezes estão conectadas 
fisicamente mais de uma rede IP distintas. Neste caso o servidor irá ter um endereço IP diferente para 
cada uma destas redes, podendo atender a alguns serviços em uma delas mas não necessariamente nas 
outras (pense num firewall ou num proxy-server que obviamente prestam serviços distintos dependendo 
de as requisições virem da Intranet ou da Internet). 
No caso de servidores ligados a múltiplas redes, também deve-se especificar que serviço (porta) o 
servidor irá atender para cada endereço IP. 
Para resolver estes problemas é usada a primitiva bind: 
 result = bind( sock_descr, ptr_local_addr, addrlen ) 
onde sock_descr contém o descritor de um socket previamente criado, ptr_local_addr é um 
apontador para uma estruturasockaddr_in que contém o endereço IP e a porta da máquina local e 
addrlen é fixo devendo ser preenchido com sizeof( sockaddr_in ). 
Caso não se queira especificar um endereço IP específico (com o efeito colateral, no caso de servidores 
com múltiplas portas, de se amarrar a porta a este socket para todos os diferentes endereços IP) pode-se 
preencher o campo sin_addr da estrutura apontada por ptr_local_addr com o valor 
INADDR_ANY, indicando então qualquer endereço IP existente na máquina. 
Se não houve nenhum problema na amarração do socket então a primitiva retornará 0, caso ocorra algum 
problema (por exemplo o endereço IP solicitado não existe ou a porta especificada já está em uso) o valor 
retornado será -1. 
Projeto e Desenvolvimento de Aplicações para a Internet 
20 Copyright  2001 João Carlos Gluz 
 
 ... 
 sla.sin_addr = INADDR_ANY; 
 sla.sin_port = htons( 80 ); 
 res = bind( sock_descr, &sla, sizeof( sockaddr_in ) ); 
 ... 
 
 
3.5. Recebendo conexões 
O próximo passo para um servidor é avisar ao S.O. que as conexões remotas poderão ser aceitas. Isto é 
feito pela primitiva listen: 
 listen( sock_descr, queue_size ) 
Esta primitiva não apenas avisa que o socket sock_descr irá ficar num estado passivo de espera de 
conexões mas também serve para informar o S.O. qual será o tamanho máximo da fila de requisições 
associada ao socket, através do parâmetro queue_size. 
Contudo esta primitiva não espera pelas conexões - ela apenas avisa ao S.O. para que este coloque o 
socket em modo passivo. Para atender as conexões entrantes será usada a primitiva accept: 
 new_sock_descr = accept( sock_descr, ptr_rem_addr, ptr_addrlen ) 
onde sock_descr contém o descritor de um socket previamente criado, que já foi amarrado a um 
endereço através de bind e que já foi posto em modo passivo através de listen, ptr_rem_addr é um 
apontador para uma estrutura sockaddr_in que conterá o endereço IP e a porta da máquina remota 
quanto a conexão for estabelecida e ptr_addrlen é um apontador para um inteiro que (na prática em 
redes Internet) será preenchido com o valor sizeof( sockaddr_in ). 
A primitiva accept é bloqueadora, isto é, a execução da aplicação fica “travada” até que uma nova 
conexão seja recebida. Somente após a recepção desta conexão é que a aplicação voltará a executar. 
Além disso os dados desta nova conexão poderão ser acessados através do novo socket sendo criado 
(new_sock_descr). O socket original sock_descr somente serve para especificar qual o endereço 
IP e porta a ser usados no lado local (serve também como um ponto de referência para a fila de 
requisições que o S.O. cria para o socket), mas não deverá ser usado para a troca de dados! 
 
 ... 
 listen( sock_descr, 15 ); 
 new_sd = accept( sock_descr, ptr_rem_addr, ptr_addrlen ); 
 ... 
 
Projeto e Desenvolvimento de Aplicações para a Internet 
21 Copyright  2001 João Carlos Gluz 
3.6. Transferência de Dados 
Uma vez que a conexão tenha sido estabelecida a troca de dados por meio da interface sockets é uma 
atividade bastante simples. Existem dois conjuntos de primitivas para esta troca de dados: 
• as primitivas de escrita / leitura derivadas da API do sistema de arquivos (read / write) 
• novas primitivas de envio / recepção específicas da interface sockets (send / recv / sendto / 
recvfrom) 
A seguir é apresentada a lista destas primitivas, com uma breve descrição da operação das mesmas: 
res = read( sock_descr, ptr_buf, len ) 
res = recv( sock_descr, ptr_buf, len, flags ) 
Estas duas primitivas recebem dados de um socket definido pelo parâmetro sock_descr. Os dados 
recebidos na área apontada por ptr_buf, com um tamanho máximo de len bytes. É importante 
salientar que tanto read quanto recv somente retornam, quando len bytes forem lidos ou em caso 
contrário a conexão terá sido desfeita e o número de bytes finais que se conseguiu ler sem erro é dado por 
res. O parâmetro flags serve para solicitar algumas opções especiais da interface sockets (entre estes 
a opção de se fazer um recv não bloqueante por exemplo). 
res = write( sock_descr, ptr_buf, len ) 
res = send( sock_descr, ptr_buf, len, flags ) 
Estas duas primitivas escrevem ou enviam dados para o socket definido pelo parâmetro sock_descr. 
A área apontada por ptr_buf deverá conter os dados (bytes) a serem transferidos e o parâmetro len 
define o tamanho desta área. Da mesma forma que no caso das primitivas de recepção se o valor 
retornado pelas primitivas (res) for diferente de len então a conexão foi desfeita e res terá o número 
dos últimos bytes que se conseguiu efetivamente enviar. O parâmetro flags também serve para 
solicitar algumas opções especiais da interface socket. 
sendto( sock_descr, ptr_buf, len, flags, ptr_dest_addr, addrlen ) 
recvfrom( sock_descr, ptr_buf, len, flags, ptr_orig_addr, ptr_addrlen ) 
As primitivas e procedimentos vistos até aqui se adaptam a transferência de dados usando conexões 
TCP. Mas no caso de se usar o serviço orientado a datagrama do UDP, o processo todo é na verdade 
muito mais simples: 
• do lado do cliente, em vez de estabelecer uma conexão (connect) e depois transferir dados, basta 
informar qual mensagem e qual a máquina e porta destino. Esta é a função da primitiva sendto, 
que envia dados para uma máquina e porta remotas. 
• do lado do servidor a seqüência de passos ainda implica em se usar bind e listen, porém o uso do 
accept é desnecessário, pois basta usar a primitiva recvfrom para receber dados de uma máquina 
e porta remota. 
Projeto e Desenvolvimento de Aplicações para a Internet 
22 Copyright  2001 João Carlos Gluz 
Os parâmetros sock_descr, ptr_buf e len têm os mesmos significados vistos anteriormente. Da 
mesma forma que no connect o parâmetro ptr_dest_addr aponta para uma estrututura 
sockaddr_in que contém o endereço da máquina e porta remota que se deseja enviar a mensagem. 
Também da mesma forma que no accept o parâmetro ptr_orig_addr irá conter o endereço IP e 
porta da máquina que enviou a mensagem. Os parâmetros addrlen e ptr_addrlen têm significado 
igual que os parâmetros de mesmo nome em connect e accept. 
Nota: 
Estas primitivas somente devem ser usadas em sockets que estejam desconectados. 
Projeto e Desenvolvimento de Aplicações para a Internet 
23 Copyright  2001 João Carlos Gluz 
Capítulo IV - Arquitetura Genérica de Clientes 
4.1. Introdução 
As primitivas sockets de programação vistas até agora permitem a construção de um sem-número de 
aplicações de rede. Porém, a verdade é que apenas entender quais são as primitivas de comunicação 
disponibilizadas por uma determinada arquitetura de rede (seja ela TCP/IP, ou Novell  IPX/SPX ou 
Microsoft  / IBM  NetBIOS  ou qualquer outra arquitetura) não é suficiente para se começar a 
projetar e implementar aplicações de rede úteis, eficientes e muitos menos otimizadas. 
Para tanto é necessário também se ater as características e conceitos envolvidos na criação de aplicações 
para operar em redes. Dito de outra forma: embora um bom conhecimento das capacidades das 
primitivas de uma dada interface de programação para redes seja necessário ele não é suficiente, tendo 
que ser complementado pelo conhecimento de como se pode estruturar a comunicação entre as 
aplicações e programas. 
Dando seguimento a este processo agora se passará a analisar as características e conceitos envolvidos 
em programação de aplicações cliente/servidoras, começando pela arquitetura genérica usada nas 
aplicações clientes. 
4.2. Características de uma aplicação cliente 
De maneira geral as aplicações que agem como clientes são conceitualmente mais simples do que as 
aplicações servidoras: 
• Em primeiro lugar, geralmente não é necessário que as aplicações clientes seja capazes ou tenham 
que tratar concorrentemente as suas conexões como servidor (ou servidores). 
• Usualmente também não é necessário que uma aplicação cliente tenha privilégios especiais de S.O. 
(como, p.ex., os necessários para se obter acesso ao cadastro de usuários do sistema). 
• Por último, em conseqüência disso também não é normalmente necessário que a aplicação cliente 
tenha que reforçar ou garantir critérios de segurança de acesso (em geral se usa clientes para se ter 
acesso a sistemas remotos, portanto são os servidores remotos que terão que controlar este acesso). 
4.3. Algoritmo básico de um cliente 
As aplicações clientes tem uma estrutura básica bastante simples seguindo o algoritmo apresentado no 
diagrama visto a seguir: 
Projeto e Desenvolvimento de Aplicações para a Internet 
24 Copyright  2001 João Carlos Gluz 
(1) Localização do servidor
(3) Preenchimento da
estrutura de endereço
(4) Criação do socket
Conexão ou
mensagem ?
(5.a) Estabelecimento da
conexão com o servidor
(6.a) Transferência dos
dados
(7.a) Finalização da
conexão com o servidor
(5.b) Envio das
mensagens
(6.b) Recepção das
mensagens
(2) Identificação do
serviço
Conexão Mensagem
 
 
Os primeiros passos (1 a 4), que são comuns aos clientes que usam streams ou datagramas como forma 
de comunicação, servem basicamente para preparar o início desta comunicação. Depois disso, será 
necessário adequar os passos executados (e as primitivas sockets chamadas) para o tipo de comunicação 
escolhido inicialmente. 
4.4. Localização do servidor 
Existem vários métodos que podem ser usados para localizar o endereço IP do servidor remoto: 
(1) definir este endereço fixamente no programa, como constante de compilação 
(2) obter o nome do servidor a partir da linha de comando 
(3) solicitar ao usuário a identificação do servidor 
(4) ter algum mecanismo de configuração na aplicação que permita definir o nome (default) do servidor 
Projeto e Desenvolvimento de Aplicações para a Internet 
25 Copyright  2001 João Carlos Gluz 
(5) obter dados sobre o servidor a partir de arquivos de informação do sistema 
(6) usar um outro protocolo / serviço de rede para obter os dados do servidor 
O primeiro modo embora possa parecer bastante restritivo, ainda assim pode ser empregado se em vez de 
se armazenar um endereço IP fixo no código da aplicação, se armazenar um nome de servidor. Desta 
forma, pelo uso de aliases, um administrador de sistema poderá configurar o cliente para o seu sistema de 
forma apropriada. 
Porém as formas mais usuais para clientes utilizados diretamente por usuários finais são as (2), (3) e (4) 
que permitem ao cliente ser configurado com o nome do servidor quando é executado pelo usuário. 
As formas (5) e (6) também são bastante utilizadas, mas em geral em clientes mais próximos do S.O., 
que fornecem serviços mais básicos (como acesso a arquivos, impressoras ou portas seriais remotas) ou 
em outras arquiteturas de rede que não o TCP/IP (a alternativa (6) em particular). A implementação 
destas alternativas é muito dependente tanto do S.O. sendo usado quanto, também, da arquitetura de rede 
empregada. Por exemplo, embora o TCP/IP não tenha muitos mecanismos de divulgação de que serviços 
uma dada irá prestar, existem arquiteturas de redes como o IPX/SPX que são fortemente baseadas em 
mecanismos de divulgação ou publicação de serviços. 
Independente do modo como o identificador do servidor é obtido, é importante que o cliente tenha a 
capacidade de trabalhar com nomes de servidores e não apenas com endereços IP destas máquinas. Para 
tanto é necessário não apenas reconhecer e armazenar corretamente estes nomes, mas também chamar as 
rotinas apropriadas para converter estes nomes em endereços (não esqueça que as primitivas sockets de 
comunicação usam endereços e não nomes). 
A rotina que faz esta conversão é a: 
 struct hostent *gethostbyname( char *name ) 
que retorna o endereço referente ao nome na estrutura hostent, vista a seguir: 
 
 struct hostent 
 { 
 char *h_name; /* nome oficial do host */ 
 char **h_aliases; /* nomes adicionais */ 
 int h_addrtype; /* tipo do endereco */ 
 int h_length; /* tamanho do endereco */ 
 char **h_addr_list; /* lista de enderecos */ 
 }; 
 
Os campos que contem nomes ou endereços são organizados como listas porque as máquinas (hosts) 
podem ter múltiplas interfaces e, portanto, múltiplos endereços e possivelmente nomes. Porém para o 
caso mais comum, em que se quer o (primeiro) endereço correspondente a um nome, pode-se usar a 
notação h_addr ao invés de h_addr_list graças a seguinte definição contida no(s) header(s) da 
interface sockets: 
Projeto e Desenvolvimento de Aplicações para a Internet 
26 Copyright  2001 João Carlos Gluz 
 
 #define h_addr h_addr_list[0] 
 
Para se obter o endereço correspondente ao nome do servidor, basta usar um trecho de código similar ao 
seguinte: 
 
 ... 
 
 struct hostent *phe; 
 char *serv_name = “www.ulbra.tche.br”; 
 
 ... 
 
 if ( (phe = gethostbyname( serv_name )) != NULL ) 
 { 
 /* Endereco IP valido, que esta’ contido em phe->h_addr */ 
 } 
 else { 
 /* Houve algum erro na resolucao do nome */ 
 } 
 
Uma vez de posse do endereço correto do servidor pode-se passar a etapa seguinte. 
4.4. Identificação do serviço 
O serviço que um dado cliente irá solicitar a um servidor remoto está implícito na própria implementação 
do cliente (e do servidor também), ou seja, se foi implementado um cliente para se fazer tranferência de 
arquivos do servidor remoto de/para a máquina local, não se deve esperar que seja possível usar este 
serviço para fazer, por exemplo, o login remoto ao shell de comandos do servidor remoto. 
Apesar disso, a identificação de onde, dentro do servidor remoto, “se encontra” este serviço pode mudar 
de servidor para servidor. Além disso, diferentes tipos de “serviço”, pelo menos do ponto de vista do 
usuário final, podem ser acessados por um mesmo tipo de cliente: voltando ao caso do login remoto, um 
cliente como o TELNET que provê acesso ao login remoto, pode facilmente prover acesso da mesma 
forma a, por exemplo, uma aplicação de controle de contabilidade que seja baseada em menus e 
formularios textuais. 
Na arquitetura TCP/IP a identificação do serviço é feita por uma porta do TCP ou do UDP, dependendo 
se ele é prestado por, respectivamente, streams ou datagramas. O TCP/IP não dispôe de um mecanismo 
genérico de divulgação e publicação automática de serviços (exceto pelo mecanismo relativamente 
simples implementado pelo DNS), sendo assim a associação de que porta pertence a que serviço (e de 
que porta remota o cliente deverá usar) é feita através de três formas básicas (não mutuamente 
exclusivas): 
(1) uma definição padrão, publicada em RFC e STD sobre os serviços reconhecidos publicamente, com 
suas respectivas portas (well known ports) 
Projeto e Desenvolvimento de Aplicações para a Internet 
27 Copyright  2001 João Carlos Gluz 
(2) arquivos locais aos sistemas que dão nomes aos serviços associados as portas, sejam estes serviços 
públicos ou proprietários 
(3) especificação direta do número de porta a ser usada no servidor remota quando da execução do 
cliente 
Desta forma um cliente que busca páginas HTML numa máquina remota, pode se valer do fato de que a 
porta padrão para este tipo de serviço é a 80 e usar este número se nenhuma informação adicional for 
fornecida. 
Além disso, caso o cliente não tenha internamente armazenado o número propriamente dito da porta 
padrão dos servidores HTML, ele pode se valer do fato de que esta porta está associada ao protocolo 
denominado de “http” e buscar no arquivo de identificação de serviços do sistema, qual a porta 
correspondente a este protocolo. 
Por último, o cliente pode solicitar ao usuário que forneça a porta que iráusar para se conectar ao 
servidor remoto. 
Nos casos em que o cliente irá buscar o número da porta pelo nome do protocolo ou serviço (seja ele pré-
definido na aplicação ou fornecido pelo usuário) existe uma rotina específica que fará este tipo de 
tradução: 
struct servent *getservbyname( char *nome_servico, char *nome_protocolo ) 
que retorna a seguinte estrutura de dados: 
 
 struct servent 
 { 
 char *s_name; /* nome oficial do servico */ 
 char **s_aliases; /* nomes adicionais */ 
 int s_port; /* porta deste servico */} 
 char *s_proto; /* nome do protocolo (udp ou tcp) */ 
 }; 
 
Para se obter a porta padrão para o protocolo “http” basta executar um trecho de código similar ao 
seguinte: 
Projeto e Desenvolvimento de Aplicações para a Internet 
28 Copyright  2001 João Carlos Gluz 
 
 ... 
 
 struct servent *pse; 
 
 ... 
 
 if ( (pse = getservbyname( “http”, “tcp”)) != NULL ) 
 { 
 /* Porta padrao localizada e armazenada em pse->s_port */ 
 } 
 else { 
 /* Nao achou nenhuma pora para o nome de servico e nome 
 de protocolo indicados */ 
 } 
 
4.5. Preenchimento da estrutura de endereços 
Depois da localização do servidor remoto e identificação do serviço, basta definir e preencher a estrutura 
de armazenamento de endereços padrão para a interface sockets (estrutura sockaddr_in). Na seção 
Especificando endereços do Capítulo III - Interface Sockets, se apresenta esta estrutura e as regras 
para o seu preenchimento, salientando-se o cuidado que se deve ter em termos de conversão de tipos de 
dados (principalmente inteiros short e long) do formato padrão da Internet para o formato da 
arquitetura de máquina em que se está trabalhando (rotinas htons e htonl) e vice-versa (rotinas ntohs e 
ntohl). 
Um ponto importante em relação aos endereços IP, quando se está analisando a operação dos clientes, 
tem a ver com o fato de que uma identificação completa de um socket na Internet precisa de 4 valores: 
dois já foram obtidos, o endereço IP e a porta do servidor remoto e dois são relativos à máquina local: o 
endereço IP desta e a porta TCP ou UDP que se usará como origem para o socket. 
A regra básica para os clientes é bastante simples: 
No caso dos clientes não se preocupe em especificar o endereço IP ou porta local, deixe esta 
função para o S.O. que se encarregará de encontrar os valores apropriados. 
Forçar a amarração de endereços (através da primitiva bind) poderá ocasionar problemas e conflitos não 
só com o pool de números de porta sob controle do S.O. mas também poderá ser de difícil gerenciamento 
principalmente quando a máquina dispuser de diversas interfaces de rede e, por conseguinte, diversos 
endereços IP. 
4.6. Criação do socket 
A criação do socket é um processo simples e direto, sem maiores problemas, cujas características já 
forma apresentadas na primeira seção do documento Parte III - Interface Sockets que é justamente a seção 
Criando e eliminando sockets. O terceiro parâmetro da primitiva sockets pode ser sempre deixado com 
zero porque: somente o protocolo TCP fornece o serviço de conexões (SOCK_STREAM) e somente o 
Projeto e Desenvolvimento de Aplicações para a Internet 
29 Copyright  2001 João Carlos Gluz 
UDP fornece o serviço de mensagens / datagramas (SOCK_DGRAM), sendo assim o último parâmetro é 
irrelevante e pode ser deixado em 0. 
Notas: 
Existe uma diferença entre a interface WinSocket usada nos sistemas Windows 9x, 2000 e NT  e a 
interface BSD Socket usado nos diversos tipos de UNIX, no Linux e no FreeBSD: o valor retornado pela 
primitiva socket no caso dos sistemas Windows tem o tipo SOCKET que não pode ser considerado como 
um int. Sendo assim no caso dos sistemas Windows a regra é testar o valor retornado pela primitiva 
socket contra a constante INVALID_SOCKET, se eles forem iguais isto implica que o Windows não 
conseguir criar o novo socket. No caso dos sistemas UNIX-like, uma opção para manter a 
compatibilidade de código fonte é definir a constante INVALID_SOCKET como (-1). 
Outro detalhe importante: nos sistemas Windows, antes de se chamar qualquer primitiva ou rotina da 
interface sockets é obrigatório chamar a rotina WSAStartup para avisar ao S.O. que esta aplicação irá 
usar a interface sockets (para saber os detalhes sobre esta rotina consulte a documentação da Microsoft) 
4.7. Estabelecimento da conexão com o servidor 
Caso tenha sido escolhida a comunicação por conexões, então o próximo passo após a criação do socket é 
(tentar) estabelecer a conexão com o servidor remoto. Obviamente que este não é um processo 
determinístico que irá sempre ser atendido. Várias coisas podem acontecer nesta etapa de forma que a 
conexão não seja estabelecida: 
(1) endereço IP fornecido pode ser inválido (não existe máquina com este endereço), 
(2) endereço pode ser válido mas o servidor pode estar desligado ou ter sofrido algum tipo de pane, 
(3) endereço pode estar OK, o servidor pode estar ligado, mas o segmento de rede que o conecta ao resto 
da Internet pode estar com algum tipo de falha, 
(4) a porta fornecida pode simplesmente estar errada, ou seja, o servidor não atende nenhum serviço 
nesta porta, 
(5) ou ainda a rede pode estar congestionada e o tempo entre o envio de uma requisição e a chegada da 
resposta (tempo de resposta) pode ser inaceitável (imagine se ele for de 5 minutos!) 
(6) endereço pode estar OK, a porta também, a rede OK, o servidor ligado, mas completamente ocupado 
atendendo outros clientes, neste caso ele apenas recusa a conexão ou logo no início desta avisa ao 
cliente que ele não pode atendê-lo, 
(7) etc., etc. e etc. ( o número e grau de problemas que podem ocorrer numa rede são simplesmente 
inacreditáveis, na verdade, o fato das redes funcionarem e funcionarem normalmente bem é que é 
realmente inacreditável). 
Esta enorme série de problemas que podem ocorrer no estabelecimento da conexão (alguns deles 
obviamente também podem ocorrer depois) serve apenas para alertar o projetista e desenvolvedor que se 
Projeto e Desenvolvimento de Aplicações para a Internet 
30 Copyright  2001 João Carlos Gluz 
deve fazer um bom trabalho de reconhecimento de tipo de erro que ocorreu e também permitir ao usuário 
da aplicação cliente ter a chance de alterar suas opções para tentar buscar ou usar outro servidor, porta, 
etc. 
Afora isto, basta chamar a primitiva socket e testar seu resultado. Esta primitiva irá executar 4 tarefas: 
(1) verificar se o identificador de socket é válido, 
(2) preencher o endereço remoto do socket com os dados do segundo parâmetro, 
(3) escolher, junto com o S.O. um endereço IP e de porta apropriado para o lado local do socket (caso 
isto não tenha sido pré-definido com a primitiva bind), 
(4) iniciar o processo de estabelecimento da conexão TCP com o servidor remoto, retornando uma 
indicação se este processo teve sucesso ou não. 
4.8. Transferência dos dados 
A fase de transferência de dados é a parte crítica da aplicação, porque é nela que o serviço será 
efetivamente prestado. É durante esta fase que deverá entrar em operação o protocolo de aplicação. 
Embora as aplicações possam ter distintos tipo de protocolos de aplicação, usando os mais diversos de 
mecanismos de comunicação, em geral as aplicações de rede que seguem o paradigma cliente / servidor 
organizam os protocolos de aplicação em interações requisição / resposta. 
Este tipo de interação, onde o cliente envia mensagens que fazem requisições ou solicitações ao servidor 
remoto e, por sua vez, este servidor atende a estas requisições executando alguma tarefa local e enviando 
uma mensagem de resposta para o cliente, define, na prática, a própria filosofia de se fazer sistemas 
cliente / servidores. 
Além disso, este tipo de interação pode ser facilmente incorporada tanto a operação por conexões (ondea 
interação requisição / resposta tem um comportamento mais confiável) quanto na operação por 
datagramas (onde a confiabilidade da interação requisição/resposta não é garantida pela rede, devendo, se 
necessário, ser garantida pela aplicação). 
A seguir é apresentado um trecho de código que justamente demonstra este tipo de interação resposta, 
para o caso de um cliente HTTP que busca um arquivo remoto (no exemplo se restringiu a operação à 
versão 0.9 do HTTP, por permitir demonstrar de forma mais simples esta interação): 
 
 /* Conseguiu se conectar ao servidor remoto. 
 Agora, usando o protocolo HTTP, solicita o 
 arquivo remoto e o mostra na tela. 
 */ 
 /* Envia um request de arquivo no formato mais simples 
 possivel do HTTP (requisicao GET usada no HTTP 0.9) 
 */ 
 sprintf( sbuf, "GET %s \r\n", argv[3] ); 
 sbuf_len = strlen( sbuf ); 
Projeto e Desenvolvimento de Aplicações para a Internet 
31 Copyright  2001 João Carlos Gluz 
 
 if (send( sock_id, sbuf, sbuf_len, 0 ) != sbuf_len) 
 { 
 printf( "Erro na transmissao do request!\n" ); 
 exit(0); 
 } 
 
 /* Espera pelo arquivo enviado em resposta e apresenta 
 o mesmo na tela 
 */ 
 while ((rbuf_len=recv(sock_id, rbuf, MAXSIZ_RBUF, 0))==MAXSIZ_RBUF) 
 { 
 rbuf[MAXSIZ_RBUF] = '\0'; 
 printf( rbuf ); 
 } 
 
 if (rbuf_len>0) 
 { 
 rbuf[rbuf_len] = '\0'; 
 printf( rbuf ); 
 } 
 
Um ponto importante deve ser ressaltado na implementação da comunicação entre o par cliente / 
servidor: 
O protocolo TCP não preserva (não fornece) nenhum tipo de limitador para registros ou blocos 
de dados: um stream TCP ligando um par de aplicações se apresenta como um fluxo contínuo 
bidirecional de caracteres de 8 bits (bytes) sendo transmitidos de uma aplicação para a outra. 
Na sua forma mais simples, dentro deste fluxo não existirá nenhum tipo de caracter especial ou 
sinalização de qualquer forma implementada pelo TCP. Isto implica que se houverem limitações lógicas, 
do ponto de vista do protocolo de aplicação, às mensagens trocadas entre o cliente e o servidor isto 
deverá ser explicitamente implementado nestas aplicações. 
Porém um ponto deve ser levado em conta: assim como o TCP não preserva ou controla qualquer tipo de 
sinalização / limitação de mensagens, assim também não se deve assumir que ao enviar uma mensagem 
(ou bloco de dados) de tamanho L por uma primitiva send ou write, esta mensagem ou bloco será 
recebido com o mesmo tamanho por uma primitiva recv ou read equivalente. Muito provavelmente esta 
mensagem será recebida numa série de blocos menores de tamanho L1, L2, ...,Ln, cuja soma será igual a 
L, ou seja, L = L1 + L2 + ... + Ln. 
Isto obriga que as aplicações devem estar preparadas para receber os blocos de dados vindos da ponta 
remota em pequenas partes por vez, e, quando necessário, remontar estes pequenos blocos de dados em 
mensagens lógicas do protocolo de aplicação. 
Do lado da transmissão, embora este problema não seja tão crítico deve-se ao menos tentar evitar a 
transmissão direta (numa única chamada) de blocos muito grandes, que excedam, por exemplo o 
tamanho máximo de um datagrama IP (algo em torno de 64.000 bytes). 
Projeto e Desenvolvimento de Aplicações para a Internet 
32 Copyright  2001 João Carlos Gluz 
4.9. Finalização da conexão 
Normalmente a operação de encerramento da conexão será controlada pelo protocolo de aplicação: 
quando este protocolo chegar a fase final da transferência de informações entre o servidor e o cliente (e o 
usuário não fizer nenhuma solicitação adicional) então a conexão poderá ser encerrada. 
A forma usual para encerrar uma conexão é através da primitiva close (closesocket no caso dos sistemas 
Windows). Esta primitiva irá encerrar a comunicação nos dois sentidos, liberar o socket de volta ao S.O e 
avisar a ponta remota que a conexão TCP foi encerrada. 
Entretanto é necessário que tanto a aplicação cliente quanto a servidora estejam preparadas para o 
encerramento anômalo (abort) da conexão que pode ocorrer pelas mais diversas causas (algumas já vistas 
na seção que trata do estabelecimento da conexão). 
Outra situação que pode ocorrer é se chegar a necessidade de encerrar a conexão apenas num sentido: por 
exemplo o cliente já enviou todas as requisições que queria ao servidor e não enviará mais nenhuma 
solicitação, porém não sabe por quanto tempo irá receber mensagens de resposta do servidor. Neste 
exemplo a conexão poderia ser encerrada apenas num sentido: no sentido de transmissão do cliente ao 
servidor. 
Para tratar destes casos especiais, muitas implementações da interface socket (inclusive a WinSocket) 
disponibilizam o uso da primitiva shutdown : 
 errcode = shutdown( sock_id, direction ) 
que permite encerrar a comunicação em apenas um sentido (ou nos dois se assim for especificado, neste 
caso ela se reduz a primitiva close ou closesocket ), especificado através do parâmetro direction da 
seguinte forma: 
 0 - a recepção de dados não será mais permitida neste socket 
 1 - este socket não aceitará mais operações de transmissão de dados 
 2 - nem a transmissão nem a recepção serão permitidas 
4.10. Comunicação por datagramas 
No caso de um cliente que utiliza datagramas como forma de comunicação e que, portanto, opera sobre o 
protocolo UDP pode-se duas estratégias usar duas estratégias básicas de projeto e implementação da 
aplicação: 
(a) Usar procedimentos de estabelecimento, transferência e finalização de conexão similares aos 
empregados na comunicação por conexão (mesmas chamadas connect, send / write e recv / read). 
(b) Usar procedimentos específicos para o envio e recepção de mensagens individuais (respectivamente 
primitivas sendto e recvfrom) 
Porém um ponto deve ficar claro, a estratégia (a) não implica que uma conexão real de rede seja 
estabelecida entre o cliente e o servidor, a estratégia (a) apenas serve para indicar ao S.O. quais os 
Projeto e Desenvolvimento de Aplicações para a Internet 
33 Copyright  2001 João Carlos Gluz 
endereços IP e de porta que devem ser incorporados aos pacotes UDP enviados de uma máquina para 
outra quando uma chamada send ou write é feita. Nada mais é feito pelo S.O., uma conexão não é 
estabelecida, mensagens perdidas não são detectadas, se a ordem de chegada das mensagens foi alterada 
em relação a ordem de saída destas isto também não é percebido pelo S.O. A bem da verdade se uma 
mensagem foi subdividida em múltiplas mensagens menores, isto também não é detectado. 
Se a ocorrência destes eventos pode ocasionar problemas para o cliente ou para o servidor, então código 
específico para tratar estas ocorrências deve ser incorporado à aplicação. 
Descontando estes fatos e as possíveis implicações em termos do projeto e implementação de uma 
aplicação, a forma de uma aplicação que use a estratégia (a) é bem similar a de uma aplicação que use 
conexões, tal como já foi apresentado nas diversas seções anteriores. 
Já no caso (b) a forma da aplicação muda, sendo composta essencialmente se séries de chamadas as 
primitivas sendto e recvfrom. No caso do cliente a ordem usual será executar primeiro uma ou mais 
chamadas a sendto, para enviar a(s) requisição(ões). Depois haverá uma ou mais chamadas a recvfrom 
para se receber as respostas do servidor remoto. 
O trecho de código a seguir exemplifica o caso (b): 
Projeto e Desenvolvimento de Aplicações para a Internet 
34 Copyright  2001 João Carlos Gluz 
 
 ... 
 
 int sock_id; 
 struct sockaddr_in srv_sd; 
 char req_buf[ 512 ]; /* 512 bytes parece ser um tamanho maximo 
 razoavel para o buffer de requisicao */ 
 char resp_buf[ 512 ]; /* idem para o buffer de resposta */ 
 char *file_name; /* apontador para o nome de arquivo */ 
 int req_len; 
 char *serv_address; /* endereco IP do servidor (em ASCII) */ 
 int serv_port;

Outros materiais