Soquete Unix - Guia Rápido

Os soquetes permitem a comunicação entre dois processos diferentes na mesma máquina ou em máquinas diferentes. Para ser mais preciso, é uma maneira de falar com outros computadores usando descritores de arquivo Unix padrão. No Unix, toda ação de I / O é feita escrevendo ou lendo um descritor de arquivo. Um descritor de arquivo é apenas um inteiro associado a um arquivo aberto e pode ser uma conexão de rede, um arquivo de texto, um terminal ou qualquer outra coisa.

Para um programador, um soquete se parece e se comporta muito como um descritor de arquivo de baixo nível. Isso ocorre porque comandos como read () e write () funcionam com soquetes da mesma forma que funcionam com arquivos e canais.

Os soquetes foram introduzidos pela primeira vez no 2.1BSD e posteriormente refinados em sua forma atual com o 4.2BSD. O recurso de soquetes agora está disponível com as versões mais recentes do sistema UNIX.

Onde o soquete é usado?

Um Unix Socket é usado em uma estrutura de aplicativo cliente-servidor. Um servidor é um processo que executa algumas funções a pedido de um cliente. A maioria dos protocolos de nível de aplicativo, como FTP, SMTP e POP3, usam soquetes para estabelecer a conexão entre o cliente e o servidor e, em seguida, para a troca de dados.

Tipos de soquete

Existem quatro tipos de soquetes disponíveis para os usuários. Os dois primeiros são mais comumente usados ​​e os dois últimos raramente são usados.

Presume-se que os processos se comuniquem apenas entre soquetes do mesmo tipo, mas não há nenhuma restrição que impeça a comunicação entre soquetes de tipos diferentes.

  • Stream Sockets- A entrega em ambiente de rede é garantida. Se você enviar pelo soquete de fluxo três itens "A, B, C", eles chegarão na mesma ordem - "A, B, C". Esses sockets usam TCP (Transmission Control Protocol) para transmissão de dados. Se a entrega for impossível, o remetente receberá um indicador de erro. Os registros de dados não têm limites.

  • Datagram Sockets- A entrega em um ambiente de rede não é garantida. Eles não têm conexão porque você não precisa ter uma conexão aberta como no Stream Sockets - você cria um pacote com as informações de destino e o envia. Eles usam UDP (User Datagram Protocol).

  • Raw Sockets- Fornecem aos usuários acesso aos protocolos de comunicação subjacentes, que suportam abstrações de soquete. Esses sockets são normalmente orientados a datagramas, embora suas características exatas dependam da interface fornecida pelo protocolo. Soquetes brutos não se destinam ao usuário geral; eles foram fornecidos principalmente para aqueles interessados ​​em desenvolver novos protocolos de comunicação ou para obter acesso a alguns dos recursos mais enigmáticos de um protocolo existente.

  • Sequenced Packet Sockets- Eles são semelhantes a um soquete de fluxo, com a exceção de que os limites do registro são preservados. Essa interface é fornecida apenas como parte da abstração de soquete Network Systems (NS) e é muito importante na maioria dos aplicativos NS sérios. Os soquetes de pacotes sequenciados permitem que o usuário manipule os cabeçalhos do Protocolo de Pacote de Sequência (SPP) ou do Protocolo de Datagrama da Internet (IDP) em um pacote ou grupo de pacotes, seja escrevendo um cabeçalho de protótipo junto com os dados a serem enviados especificando um cabeçalho padrão a ser usado com todos os dados de saída e permite ao usuário receber os cabeçalhos nos pacotes de entrada.

O que vem a seguir?

Os próximos capítulos têm como objetivo fortalecer seus fundamentos e preparar uma base antes que você possa escrever programas de Servidor e Cliente usando socket . Se você deseja ver diretamente como escrever um programa cliente e servidor, pode fazê-lo, mas não é recomendado. É altamente recomendável que você vá passo a passo e conclua esses poucos capítulos iniciais para fazer sua base antes de prosseguir com a programação.

Antes de prosseguirmos com as coisas reais, vamos discutir um pouco sobre os endereços de rede - o endereço IP.

O endereço de host IP, ou mais comumente apenas o endereço IP, é usado para identificar hosts conectados à Internet. IP significa Protocolo da Internet e se refere à Camada da Internet da arquitetura geral da rede da Internet.

Um endereço IP é uma quantidade de 32 bits interpretada como quatro números ou octetos de 8 bits. Cada endereço IP identifica exclusivamente a rede do usuário participante, o host na rede e a classe da rede do usuário.

Um endereço IP é geralmente escrito em uma notação decimal com pontos na forma N1.N2.N3.N4, onde cada Ni é um número decimal entre 0 e 255 decimal (00 a FF hexadecimal).

Classes de endereço

Os endereços IP são gerenciados e criados pela Internet Assigned Numbers Authority (IANA). Existem cinco classes de endereços diferentes. Você pode determinar em qual classe um endereço IP está examinando os primeiros quatro bits do endereço IP.

  • Class A endereços começam com 0xxx, ou 1 to 126 decimal.

  • Class B endereços começam com 10xx, ou 128 to 191 decimal.

  • Class C endereços começam com 110x, ou 192 to 223 decimal.

  • Class D endereços começam com 1110, ou 224 to 239 decimal.

  • Class E endereços começam com 1111, ou 240 to 254 decimal.

Endereços começando com 01111111, ou 127 decimais, são reservados para loopback e para teste interno em uma máquina local [Você pode testar isto: você sempre deve ser capaz de fazer ping 127.0.0.1, que aponta para você]; Os endereços de classe D são reservados para multicast; Os endereços da classe E são reservados para uso futuro. Eles não devem ser usados ​​para endereços de host.

Exemplo

Class Leftmost bits Start address Finish address
UMA 0xxx 0.0.0.0 127.255.255.255
B 10xx 128.0.0.0 191.255.255.255
C 110x 192.0.0.0 223.255.255.255
D 1110 224.0.0.0 239.255.255.255
E 1111 240.0.0.0 255.255.255.255

Subnetting

Sub-rede ou sub-rede basicamente significa ramificar uma rede. Isso pode ser feito por vários motivos, como rede em uma organização, uso de diferentes mídias físicas (como Ethernet, FDDI, WAN, etc.), preservação do espaço de endereço e segurança. O motivo mais comum é controlar o tráfego da rede.

A ideia básica na criação de sub-redes é particionar a parte do identificador de host do endereço IP em duas partes -

  • Um endereço de sub-rede dentro do próprio endereço de rede; e
  • Um endereço de host na sub-rede.

Por exemplo, um formato de endereço de Classe B comum é N1.N2.SH, onde N1.N2 identifica a rede de Classe B, o campo S de 8 bits identifica a sub-rede e o campo H de 8 bits identifica o host na sub-rede.

Os nomes de host em termos de números são difíceis de lembrar e, portanto, são denominados por nomes comuns, como Takshila ou Nalanda. Escrevemos aplicativos de software para descobrir o endereço IP pontilhado correspondente a um determinado nome.

O processo de descobrir o endereço IP pontilhado com base no nome do host alfanumérico fornecido é conhecido como hostname resolution.

Uma resolução de nome de host é feita por um software especial que reside em sistemas de alta capacidade. Esses sistemas são chamados de Domain Name Systems (DNS), que mantém o mapeamento dos endereços IP e os nomes comuns correspondentes.

O arquivo / etc / hosts

A correspondência entre nomes de host e endereços IP é mantida em um arquivo chamado hosts . Na maioria dos sistemas, este arquivo é encontrado em/etc diretório.

As entradas neste arquivo se parecem com o seguinte -

# This represents a comments in /etc/hosts file.
127.0.0.1       localhost
192.217.44.207  nalanda metro
153.110.31.18   netserve
153.110.31.19   mainserver centeral
153.110.31.20   samsonite
64.202.167.10   ns3.secureserver.net
64.202.167.97   ns4.secureserver.net
66.249.89.104   www.google.com
68.178.157.132  services.amrood.com

Observe que mais de um nome pode estar associado a um determinado endereço IP. Este arquivo é usado durante a conversão do endereço IP para o nome do host e vice-versa.

Você não teria acesso para editar este arquivo, portanto, se quiser colocar qualquer nome de host junto com o endereço IP, você precisará ter permissão de root.

A maioria dos Aplicativos da Rede usa a arquitetura Cliente-Servidor, que se refere a dois processos ou dois aplicativos que se comunicam entre si para trocar algumas informações. Um dos dois processos atua como um processo cliente e outro processo atua como um servidor.

Processo do Cliente

Esse é o processo que normalmente faz uma solicitação de informações. Depois de obter a resposta, este processo pode terminar ou pode fazer algum outro processamento.

Example, O navegador da Internet funciona como um aplicativo cliente, que envia uma solicitação ao servidor da Web para obter uma página da Web em HTML.

Processo de Servidor

Este é o processo que recebe uma solicitação dos clientes. Após obter uma solicitação do cliente, este processo executará o processamento necessário, reunirá as informações solicitadas e as enviará ao cliente solicitante. Feito isso, ele fica pronto para atender outro cliente. Os processos do servidor estão sempre alertas e prontos para atender às solicitações de entrada.

Example - O servidor Web fica esperando por solicitações de navegadores da Internet e, assim que recebe qualquer solicitação de um navegador, pega uma página HTML solicitada e a envia de volta para esse navegador.

Observe que o cliente precisa saber o endereço do servidor, mas o servidor não precisa saber o endereço ou mesmo a existência do cliente antes de a conexão ser estabelecida. Uma vez que a conexão é estabelecida, ambos os lados podem enviar e receber informações.

Arquiteturas de 2 e 3 camadas

Existem dois tipos de arquiteturas cliente-servidor -

  • 2-tier architecture- Nesta arquitetura, o cliente interage diretamente com o servidor. Este tipo de arquitetura pode ter algumas falhas de segurança e problemas de desempenho. O Internet Explorer e o Web Server funcionam em uma arquitetura de duas camadas. Aqui, os problemas de segurança são resolvidos usando Secure Socket Layer (SSL).

  • 3-tier architectures- Nesta arquitetura, mais um software fica entre o cliente e o servidor. Este software intermediário é denominado 'middleware'. Middleware são usados ​​para realizar todas as verificações de segurança e balanceamento de carga em caso de carga pesada. Um middleware pega todas as solicitações do cliente e, após realizar a autenticação necessária, ele passa essa solicitação para o servidor. Em seguida, o servidor faz o processamento necessário e envia a resposta de volta ao middleware e, finalmente, o middleware passa essa resposta de volta ao cliente. Se você deseja implementar uma arquitetura de 3 camadas, pode manter qualquer middleware, como o Web Logic ou o software WebSphere, entre o servidor da web e o navegador da web.

Tipos de servidor

Existem dois tipos de servidores que você pode ter -

  • Iterative Server- Esta é a forma mais simples de servidor onde um processo servidor atende a um cliente e, após completar a primeira solicitação, recebe a solicitação de outro cliente. Enquanto isso, outro cliente continua esperando.

  • Concurrent Servers- Este tipo de servidor executa vários processos simultâneos para atender a muitas solicitações de uma vez porque um processo pode demorar mais e outro cliente não pode esperar por tanto tempo. A maneira mais simples de escrever um servidor concorrente no Unix é bifurcar um processo filho para lidar com cada cliente separadamente.

Como Fazer Cliente

As chamadas do sistema para estabelecer uma conexão são um pouco diferentes para o cliente e o servidor, mas ambos envolvem a construção básica de um soquete. Ambos os processos estabelecem seus próprios soquetes.

As etapas envolvidas no estabelecimento de um soquete no lado do cliente são as seguintes -

  • Crie um soquete com o socket() chamada de sistema.

  • Conecte o soquete ao endereço do servidor usando o connect() chamada de sistema.

  • Envie e receba dados. Existem várias maneiras de fazer isso, mas a maneira mais simples é usar oread() e write() chamadas de sistema.

Como fazer um servidor

As etapas envolvidas no estabelecimento de um soquete no lado do servidor são as seguintes -

  • Crie um soquete com o socket() chamada de sistema.

  • Vincule o soquete a um endereço usando o bind()chamada de sistema. Para um soquete de servidor na Internet, um endereço consiste em um número de porta na máquina host.

  • Ouça as conexões com o listen() chamada de sistema.

  • Aceite uma conexão com o accept()chamada de sistema. Essa chamada normalmente bloqueia a conexão até que um cliente se conecte ao servidor.

  • Envie e receba dados usando o read() e write() chamadas de sistema.

Interação cliente e servidor

A seguir está o diagrama que mostra a interação completa do cliente e do servidor -

Várias estruturas são usadas na Programação de Soquete Unix para armazenar informações sobre o endereço e a porta, e outras informações. A maioria das funções de soquete requerem um ponteiro para uma estrutura de endereço de soquete como argumento. As estruturas definidas neste capítulo estão relacionadas à família de protocolos da Internet.

sockaddr

A primeira estrutura é o sockaddr que contém as informações do soquete -

struct sockaddr {
   unsigned short   sa_family;
   char             sa_data[14];
};

Esta é uma estrutura de endereço de soquete genérica, que será passada na maioria das chamadas de função de soquete. A tabela a seguir fornece uma descrição dos campos do membro -

Atributo Valores Descrição
sa_family

AF_INET

AF_UNIX

AF_NS

AF_IMPLINK

Ele representa uma família de endereços. Na maioria dos aplicativos baseados na Internet, usamos AF_INET.
sa_data Endereço específico do protocolo O conteúdo dos 14 bytes do endereço específico do protocolo é interpretado de acordo com o tipo de endereço. Para a família da Internet, usaremos o endereço IP do número da porta, que é representado pela estrutura sockaddr_in definida abaixo.

sockaddr em

A segunda estrutura que ajuda você a fazer referência aos elementos do soquete é a seguinte -

struct sockaddr_in {
   short int            sin_family;
   unsigned short int   sin_port;
   struct in_addr       sin_addr;
   unsigned char        sin_zero[8];
};

Aqui está a descrição dos campos de membro -

Atributo Valores Descrição
sa_family

AF_INET

AF_UNIX

AF_NS

AF_IMPLINK

Ele representa uma família de endereços. Na maioria dos aplicativos baseados na Internet, usamos AF_INET.
sin_port Porta de serviço Um número de porta de 16 bits na ordem de bytes da rede.
sin_addr Endereço de IP Um endereço IP de 32 bits na ordem de bytes da rede.
sin_zero Não usado Você acabou de definir esse valor como NULL, pois ele não está sendo usado.

em addr

Essa estrutura é usada apenas na estrutura acima como um campo de estrutura e contém netid / hostid de 32 bits.

struct in_addr {
   unsigned long s_addr;
};

Aqui está a descrição dos campos de membro -

Atributo Valores Descrição
s_addr porta de serviço Um endereço IP de 32 bits na ordem de bytes da rede.

hostente

Essa estrutura é usada para manter as informações relacionadas ao host.

struct hostent {
   char *h_name; 
   char **h_aliases; 
   int h_addrtype;  
   int h_length;    
   char **h_addr_list
	
#define h_addr  h_addr_list[0]
};

Aqui está a descrição dos campos de membro -

Atributo Valores Descrição
h_name ti.com etc. É o nome oficial do host. Por exemplo, tutorialspoint.com, google.com, etc.
h_aliases TI Ele contém uma lista de aliases de nomes de host.
h_addrtype AF_INET Ele contém a família de endereços e no caso de aplicação baseada na Internet, será sempre AF_INET.
comprimento_h 4 Ele contém o comprimento do endereço IP, que é 4 para o endereço da Internet.
h_addr_list in_addr Para endereços da Internet, a matriz de ponteiros h_addr_list [0], h_addr_list [1] e assim por diante, são pontos para a estrutura in_addr.

NOTE - h_addr é definido como h_addr_list [0] para manter a compatibilidade com versões anteriores.

servo

Essa estrutura particular é usada para manter informações relacionadas ao serviço e portas associadas.

struct servent {
   char  *s_name; 
   char  **s_aliases; 
   int   s_port;  
   char  *s_proto;
};

Aqui está a descrição dos campos de membro -

Atributo Valores Descrição
s_name http Este é o nome oficial do serviço. Por exemplo, SMTP, FTP POP3, etc.
s_aliases ALIAS Ele contém a lista de aliases de serviço. Na maioria das vezes, será definido como NULL.
esporte 80 Ele terá um número de porta associado. Por exemplo, para HTTP, será 80.
s_proto

TCP

UDP

É definido para o protocolo usado. Os serviços de Internet são fornecidos usando TCP ou UDP.

Dicas sobre estruturas de soquete

As estruturas de endereço de soquete são parte integrante de todo programa de rede. Nós os alocamos, os preenchemos e passamos a eles ponteiros para várias funções de socket. Às vezes, passamos um ponteiro para uma dessas estruturas para uma função de soquete e ele preenche o conteúdo.

Sempre passamos essas estruturas por referência (ou seja, passamos um ponteiro para a estrutura, não a própria estrutura) e sempre passamos o tamanho da estrutura como outro argumento.

Quando uma função de socket preenche uma estrutura, o comprimento também é passado por referência, para que seu valor possa ser atualizado pela função. Chamamos esses argumentos de valor-resultado.

Sempre, defina as variáveis ​​de estrutura para NULL (ou seja, '\ 0') usando memset () para funções bzero (), caso contrário, pode obter valores indesejados de lixo em sua estrutura.

Quando um processo cliente deseja se conectar a um servidor, o cliente deve ter uma maneira de identificar o servidor que deseja conectar. Se o cliente souber o endereço de 32 bits da Internet do host no qual o servidor reside, ele poderá contatar esse host. Mas como o cliente identifica o processo de servidor específico em execução naquele host?

Para resolver o problema de identificação de um processo de servidor específico em execução em um host, o TCP e o UDP definiram um grupo de portas conhecidas.

Para o nosso propósito, uma porta será definida como um número inteiro entre 1024 e 65535. Isso ocorre porque todos os números de porta menores que 1024 são considerados bem conhecidos - por exemplo, telnet usa a porta 23, http usa 80, ftp usa 21, e assim por diante.

As atribuições de porta para serviços de rede podem ser encontradas no arquivo / etc / services. Se você estiver escrevendo seu próprio servidor, deve-se tomar cuidado ao atribuir uma porta a seu servidor. Você deve certificar-se de que essa porta não seja atribuída a nenhum outro servidor.

Normalmente é uma prática atribuir qualquer número de porta superior a 5.000. Mas existem muitas organizações que escreveram servidores com números de porta superiores a 5.000. Por exemplo, o Yahoo Messenger é executado em 5050, o servidor SIP é executado em 5060 etc.

Portas e serviços de exemplo

Aqui está uma pequena lista de serviços e portas associadas. Você pode encontrar a lista mais atualizada de portas de Internet e serviços associados em IANA - Atribuição de portas TCP / IP .

Service Port Number Service Description
eco 7 UDP / TCP envia de volta o que recebe.
descartar 9 UDP / TCP descarta a entrada.
dia 13 UDP / TCP retorna a hora ASCII.
Chargen 19 UDP / TCP retorna caracteres.
ftp 21 Transferência de arquivos TCP.
telnet 23 Login remoto TCP.
smtp 25 Email TCP.
dia 37 UDP / TCP retorna o tempo binário.
tftp 69 Transferência de arquivo UDP trivial.
dedo 79 Informações de TCP sobre usuários.
http 80 TCP World Wide Web.
Conecte-se 513 Login remoto TCP.
quem 513 UDP informações diferentes sobre os usuários.
Xserver 6000 Janelas TCP X (NB> 1023).

Portas e funções de serviço

O Unix fornece as seguintes funções para buscar o nome do serviço do arquivo / etc / services.

  • struct servent *getservbyname(char *name, char *proto) - Esta chamada pega o nome do serviço e o nome do protocolo e retorna o número da porta correspondente para esse serviço.

  • struct servent *getservbyport(int port, char *proto) - Esta chamada pega o número da porta e o nome do protocolo e retorna o nome do serviço correspondente.

O valor de retorno de cada função é um ponteiro para uma estrutura com a seguinte forma -

struct servent {
   char  *s_name;
   char  **s_aliases;
   int   s_port;
   char  *s_proto;
};

Aqui está a descrição dos campos de membro -

Atributo Valores Descrição
s_name http É o nome oficial do serviço. Por exemplo, SMTP, FTP POP3, etc.
s_aliases ALIAS Ele contém a lista de aliases de serviço. Na maioria das vezes, ele será definido como NULL.
esporte 80 Ele terá o número da porta associado. Por exemplo, para HTTP, será 80.
s_proto

TCP

UDP

É definido para o protocolo usado. Os serviços de Internet são fornecidos usando TCP ou UDP.

Infelizmente, nem todos os computadores armazenam os bytes que constituem um valor multibyte na mesma ordem. Considere uma Internet de 16 bits composta por 2 bytes. Existem duas maneiras de armazenar esse valor.

  • Little Endian - Neste esquema, o byte de ordem inferior é armazenado no endereço inicial (A) e o byte de ordem superior é armazenado no próximo endereço (A + 1).

  • Big Endian - Neste esquema, o byte de ordem superior é armazenado no endereço inicial (A) e o byte de ordem inferior é armazenado no endereço seguinte (A + 1).

Para permitir que máquinas com diferentes convenções de ordem de bytes se comuniquem entre si, os protocolos da Internet especificam uma convenção de ordem de bytes canônica para dados transmitidos pela rede. Isso é conhecido como Network Byte Order.

Ao estabelecer uma conexão de soquete da Internet, você deve se certificar de que os dados nos membros sin_port e sin_addr da estrutura sockaddr_in estão representados na Ordem de Byte da Rede.

Funções de ordenação de bytes

As rotinas para conversão de dados entre a representação interna de um host e a Ordem de Byte da Rede são as seguintes -

Função Descrição
htons () Curto de host para rede
htonl () Host para rede longa
ntohl () Rede para hospedar longa
ntohs () Rede para hospedar curto

Listados abaixo estão mais alguns detalhes sobre essas funções -

  • unsigned short htons(unsigned short hostshort) - Esta função converte quantidades de 16 bits (2 bytes) da ordem de bytes do host para a ordem de bytes da rede.

  • unsigned long htonl(unsigned long hostlong) - Esta função converte quantidades de 32 bits (4 bytes) da ordem de bytes do host para a ordem de bytes da rede.

  • unsigned short ntohs(unsigned short netshort) - Esta função converte quantidades de 16 bits (2 bytes) da ordem de bytes da rede para a ordem de bytes do host.

  • unsigned long ntohl(unsigned long netlong) - Esta função converte quantidades de 32 bits da ordem de bytes da rede para a ordem de bytes do host.

Essas funções são macros e resultam na inserção do código-fonte de conversão no programa de chamada. Em máquinas little-endian, o código mudará os valores para a ordem de bytes da rede. Em máquinas big-endian, nenhum código é inserido, pois nenhum é necessário; as funções são definidas como nulas.

Programa para determinar a ordem de bytes do host

Mantenha o seguinte código em um arquivo byteorder.c , compile-o e execute-o em sua máquina.

Neste exemplo, armazenamos o valor de dois bytes 0x0102 no inteiro curto e, em seguida, olhamos para os dois bytes consecutivos, c [0] (o endereço A) ec [1] (o endereço A + 1) para determinar o byte ordem.

#include <stdio.h>

int main(int argc, char **argv) {

   union {
      short s;
      char c[sizeof(short)];
   }un;
	
   un.s = 0x0102;
   
   if (sizeof(short) == 2) {
      if (un.c[0] == 1 && un.c[1] == 2)
         printf("big-endian\n");
      
      else if (un.c[0] == 2 && un.c[1] == 1)
         printf("little-endian\n");
      
      else
         printf("unknown\n");
   }
   else {
      printf("sizeof(short) = %d\n", sizeof(short));
   }
	
   exit(0);
}

Uma saída gerada por este programa em uma máquina Pentium é a seguinte -

$> gcc byteorder.c $> ./a.out
little-endian
$>

O Unix fornece várias chamadas de função para ajudá-lo a manipular endereços IP. Essas funções convertem endereços de Internet entre strings ASCII (o que os humanos preferem usar) e valores binários ordenados por byte de rede (valores que são armazenados em estruturas de endereço de soquete).

As três chamadas de função a seguir são usadas para endereçamento IPv4 -

  • int inet_aton (const char * strptr, struct in_addr * addrptr)
  • in_addr_t inet_addr (const char * strptr)
  • char * inet_ntoa (struct in_addr inaddr)

int inet_aton (const char * strptr, struct in_addr * addrptr)

Essa chamada de função converte a string especificada na notação de ponto padrão da Internet em um endereço de rede e armazena o endereço na estrutura fornecida. O endereço convertido estará em Network Byte Order (bytes ordenados da esquerda para a direita). Retorna 1 se a string era válida e 0 em caso de erro.

A seguir está o exemplo de uso -

#include <arpa/inet.h>

(...)

   int retval;
   struct in_addr addrptr
   
   memset(&addrptr, '\0', sizeof(addrptr));
   retval = inet_aton("68.178.157.132", &addrptr);

(...)

in_addr_t inet_addr (const char * strptr)

Essa chamada de função converte a string especificada na notação de ponto padrão da Internet em um valor inteiro adequado para uso como um endereço da Internet. O endereço convertido estará em Network Byte Order (bytes ordenados da esquerda para a direita). Ele retorna um endereço IPv4 ordenado por byte de rede binário de 32 bits e INADDR_NONE em caso de erro.

A seguir está o exemplo de uso -

#include <arpa/inet.h>

(...)

   struct sockaddr_in dest;

   memset(&dest, '\0', sizeof(dest));
   dest.sin_addr.s_addr = inet_addr("68.178.157.132");
   
(...)

char * inet_ntoa (struct in_addr inaddr)

Essa chamada de função converte o endereço de host da Internet especificado em uma string na notação de ponto padrão da Internet.

A seguir está o exemplo de uso -

#include <arpa/inet.h>

(...)

   char *ip;
   
   ip = inet_ntoa(dest.sin_addr);
   
   printf("IP Address is: %s\n",ip);
   
(...)

Este capítulo descreve as funções de soquete centrais necessárias para escrever um cliente e servidor TCP completo.

O diagrama a seguir mostra a interação completa entre cliente e servidor -

A função de soquete

Para realizar E / S de rede, a primeira coisa que um processo deve fazer é chamar a função de soquete, especificando o tipo de protocolo de comunicação desejado e família de protocolo, etc.

#include <sys/types.h>
#include <sys/socket.h>

int socket (int family, int type, int protocol);

Esta chamada retorna um descritor de socket que você pode usar em chamadas de sistema posteriores ou -1 em caso de erro.

Parâmetros

family - Ele especifica a família do protocolo e é uma das constantes mostradas abaixo -

Família Descrição
AF_INET Protocolos IPv4
AF_INET6 Protocolos IPv6
AF_LOCAL Protocolos de domínio Unix
AF_ROUTE Sockets de roteamento
AF_KEY Tomada Ket

Este capítulo não cobre outros protocolos, exceto IPv4.

type- Especifica o tipo de socket que você deseja. Pode assumir um dos seguintes valores -

Tipo Descrição
SOCK_STREAM Tomada de fluxo
SOCK_DGRAM Soquete de datagrama
SOCK_SEQPACKET Soquete de pacote sequenciado
SOCK_RAW Soquete cru

protocol - O argumento deve ser definido para o tipo de protocolo específico fornecido abaixo, ou 0 para selecionar o padrão do sistema para a combinação dada de família e tipo -

Protocolo Descrição
IPPROTO_TCP Protocolo de transporte TCP
IPPROTO_UDP Protocolo de transporte UDP
IPPROTO_SCTP Protocolo de transporte SCTP

A função de conexão

A função de conexão é usada por um cliente TCP para estabelecer uma conexão com um servidor TCP.

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

Esta chamada retorna 0 se se conectar com sucesso ao servidor, caso contrário, retorna -1 em caso de erro.

Parâmetros

  • sockfd - É um descritor de socket retornado pela função de socket.

  • serv_addr - É um ponteiro para struct sockaddr que contém o endereço IP e a porta de destino.

  • addrlen - Defina-o como sizeof (struct sockaddr).

A função bind

A função bind atribui um endereço de protocolo local a um socket. Com os protocolos da Internet, o endereço do protocolo é a combinação de um endereço IPv4 de 32 bits ou um endereço IPv6 de 128 bits, junto com um número de porta TCP ou UDP de 16 bits. Esta função é chamada apenas pelo servidor TCP.

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, struct sockaddr *my_addr,int addrlen);

Esta chamada retorna 0 se se conectar com sucesso ao endereço, caso contrário, retorna -1 em caso de erro.

Parâmetros

  • sockfd - É um descritor de socket retornado pela função de socket.

  • my_addr - É um ponteiro para struct sockaddr que contém o endereço IP local e a porta.

  • addrlen - Defina-o como sizeof (struct sockaddr).

Você pode colocar seu endereço IP e sua porta automaticamente

Um valor 0 para o número da porta significa que o sistema escolherá uma porta aleatória e o valor INADDR_ANY para o endereço IP significa que o endereço IP do servidor será atribuído automaticamente.

server.sin_port = 0;  		     
server.sin_addr.s_addr = INADDR_ANY;

NOTE- Todas as portas abaixo de 1024 são reservadas. Você pode definir uma porta acima de 1024 e abaixo de 65535, a menos que sejam aquelas sendo usadas por outros programas.

A função de escuta

A função de escuta é chamada apenas por um servidor TCP e executa duas ações -

  • A função de escuta converte um soquete não conectado em um soquete passivo, indicando que o kernel deve aceitar solicitações de conexão de entrada direcionadas a este soquete.

  • O segundo argumento para esta função especifica o número máximo de conexões que o kernel deve enfileirar para este socket.

#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd,int backlog);

Esta chamada retorna 0 em caso de sucesso, caso contrário, retorna -1 em caso de erro.

Parâmetros

  • sockfd - É um descritor de socket retornado pela função de socket.

  • backlog - É o número de conexões permitidas.

A função aceitar

A função de aceitação é chamada por um servidor TCP para retornar a próxima conexão concluída da frente da fila de conexão concluída. A assinatura da chamada é a seguinte -

#include <sys/types.h>
#include <sys/socket.h>

int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

Esta chamada retorna um descritor não negativo em caso de sucesso, caso contrário, retorna -1 em caso de erro. O descritor retornado é considerado um descritor de socket do cliente e todas as operações de leitura e gravação serão feitas neste descritor para se comunicar com o cliente.

Parâmetros

  • sockfd - É um descritor de socket retornado pela função de socket.

  • cliaddr - É um ponteiro para struct sockaddr que contém o endereço IP e a porta do cliente.

  • addrlen - Defina-o como sizeof (struct sockaddr).

A função de envio

A função de envio é usada para enviar dados através de soquetes de fluxo ou soquetes de datagrama CONECTADOS. Se você deseja enviar dados através de soquetes de datagrama UNCONNECTED, você deve usar a função sendto ().

Você pode usar a chamada de sistema write () para enviar dados. Sua assinatura é a seguinte -

int send(int sockfd, const void *msg, int len, int flags);

Esta chamada retorna o número de bytes enviados, caso contrário, retornará -1 em caso de erro.

Parâmetros

  • sockfd - É um descritor de socket retornado pela função de socket.

  • msg - É um indicador para os dados que você deseja enviar.

  • len - É o comprimento dos dados que você deseja enviar (em bytes).

  • flags - Está definido como 0.

A função recv

A função recv é usada para receber dados por meio de soquetes de fluxo ou soquetes de datagrama CONECTADO. Se você deseja receber dados através de sockets de datagrama UNCONNECTED, você deve usar recvfrom ().

Você pode usar a chamada de sistema read () para ler os dados. Esta chamada é explicada no capítulo de funções auxiliares.

int recv(int sockfd, void *buf, int len, unsigned int flags);

Esta chamada retorna o número de bytes lidos no buffer, caso contrário, retornará -1 em caso de erro.

Parâmetros

  • sockfd - É um descritor de socket retornado pela função de socket.

  • buf - É o buffer para ler as informações.

  • len - É o comprimento máximo do buffer.

  • flags - Está definido como 0.

A função sendto

A função sendto é usada para enviar dados por soquetes de datagramas UNCONNECTED. Sua assinatura é a seguinte -

int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);

Esta chamada retorna o número de bytes enviados, caso contrário, retorna -1 em caso de erro.

Parâmetros

  • sockfd - É um descritor de socket retornado pela função de socket.

  • msg - É um indicador para os dados que você deseja enviar.

  • len - É o comprimento dos dados que você deseja enviar (em bytes).

  • flags - Está definido como 0.

  • to - É um ponteiro para struct sockaddr para o host para o qual os dados devem ser enviados.

  • tolen - Está definido como sizeof (struct sockaddr).

A função recvfrom

A função recvfrom é usada para receber dados de soquetes de datagrama UNCONNECTED.

int recvfrom(int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen);

Esta chamada retorna o número de bytes lidos no buffer, caso contrário, retorna -1 em caso de erro.

Parâmetros

  • sockfd - É um descritor de socket retornado pela função de socket.

  • buf - É o buffer para ler as informações.

  • len - É o comprimento máximo do buffer.

  • flags - Está definido como 0.

  • from - É um ponteiro para struct sockaddr para o host onde os dados devem ser lidos.

  • fromlen - Está definido como sizeof (struct sockaddr).

A função de fechamento

A função close é usada para fechar a comunicação entre o cliente e o servidor. Sua sintaxe é a seguinte -

int close( int sockfd );

Esta chamada retorna 0 em caso de sucesso, caso contrário, retorna -1 em caso de erro.

Parâmetros

  • sockfd - É um descritor de socket retornado pela função de socket.

A função de desligamento

A função de desligamento é usada para fechar normalmente a comunicação entre o cliente e o servidor. Esta função oferece mais controle em comparação com a função de fechamento . A seguir está a sintaxe de desligamento -

int shutdown(int sockfd, int how);

Esta chamada retorna 0 em caso de sucesso, caso contrário, retorna -1 em caso de erro.

Parâmetros

  • sockfd - É um descritor de socket retornado pela função de socket.

  • how - Coloque um dos números -

    • 0 - indica que o recebimento não é permitido,

    • 1 - indica que o envio não é permitido e

    • 2- indica que o envio e o recebimento não são permitidos. Quando how está definido como 2, é a mesma coisa que close ().

A função de seleção

A função de seleção indica qual dos descritores de arquivo especificados está pronto para leitura, pronto para gravação ou tem uma condição de erro pendente.

Quando um aplicativo chama recv ou recvfrom , ele é bloqueado até que os dados cheguem a esse soquete. Um aplicativo pode estar realizando outro processamento útil enquanto o fluxo de dados de entrada está vazio. Outra situação é quando um aplicativo recebe dados de vários soquetes.

Chamar recv ou recvfrom em um soquete que não possui dados em sua fila de entrada impede a recepção imediata de dados de outros soquetes. A chamada de função select resolve este problema permitindo que o programa pesquise todos os identificadores de socket para ver se eles estão disponíveis para operações de leitura e escrita sem bloqueio.

A seguir está a sintaxe de select -

int select(int  nfds, fd_set  *readfds, fd_set  *writefds, fd_set *errorfds, struct timeval *timeout);

Esta chamada retorna 0 em caso de sucesso, caso contrário, retorna -1 em caso de erro.

Parâmetros

  • nfds- Especifica a gama de descritores de arquivo a serem testados. A função select () testa os descritores de arquivo no intervalo de 0 a nfds-1

  • readfds- Ele aponta para um objeto do tipo fd_set que, na entrada, especifica os descritores de arquivo a serem verificados para estarem prontos para leitura, e na saída, indica quais descritores de arquivo estão prontos para ler. Pode ser NULL para indicar um conjunto vazio.

  • writefds- Ele aponta para um objeto do tipo fd_set que na entrada, especifica os descritores de arquivo a serem verificados para estarem prontos para escrever, e na saída, indica quais descritores de arquivo estão prontos para escrever. Pode ser NULL para indicar um conjunto vazio.

  • exceptfds- Ele aponta para um objeto do tipo fd_set que, na entrada, especifica os descritores de arquivo a serem verificados quanto às condições de erro pendentes e na saída indica quais descritores de arquivo têm condições de erro pendentes. Pode ser NULL para indicar um conjunto vazio.

  • timeout- Ele aponta para uma estrutura de tempo que especifica por quanto tempo a chamada de seleção deve pesquisar os descritores para uma operação de E / S disponível. Se o valor de tempo limite for 0, então select irá retornar imediatamente. Se o argumento de tempo limite for NULL, então o select irá bloquear até que pelo menos um arquivo / identificador de soquete esteja pronto para uma operação de E / S disponível. Caso contrário, o select retornará após a quantidade de tempo no tempo limite ter decorrido OU quando pelo menos um descritor de arquivo / soquete estiver pronto para uma operação de E / S.

O valor de retorno de select é o número de identificadores especificados nos conjuntos de descritores de arquivo que estão prontos para E / S. Se o limite de tempo especificado pelo campo de tempo limite for atingido, selecione o retorno 0. As seguintes macros existem para manipular um conjunto de descritores de arquivo -

  • FD_CLR(fd, &fdset)- Limpa o bit para o descritor de arquivo fd no conjunto de descritores de arquivo fdset.

  • FD_ISSET(fd, &fdset)- Retorna um valor diferente de zero se o bit para o descritor de arquivo fd é definido no conjunto de descritores de arquivo apontado por fdset , e 0 caso contrário.

  • FD_SET(fd, &fdset) - Define o bit para o descritor de arquivo fd no descritor de arquivo definido fdset.

  • FD_ZERO(&fdset) - Inicializa o conjunto de descritores de arquivo fdset para ter zero bits para todos os descritores de arquivo.

O comportamento dessas macros é indefinido se o argumento fd for menor que 0 ou maior ou igual a FD_SETSIZE.

Exemplo

fd_set fds;

struct timeval tv;

/* do socket initialization etc.
tv.tv_sec = 1;
tv.tv_usec = 500000;

/* tv now represents 1.5 seconds */
FD_ZERO(&fds);

/* adds sock to the file descriptor set */
FD_SET(sock, &fds); 

/* wait 1.5 seconds for any data to be read from any single socket */
select(sock+1, &fds, NULL, NULL, &tv);

if (FD_ISSET(sock, &fds)) {
   recvfrom(s, buffer, buffer_len, 0, &sa, &sa_len);
   /* do something */
}
else {
   /* do something else */
}

Este capítulo descreve todas as funções auxiliares, que são usadas durante a programação de soquetes. Outras funções auxiliares são descritas nos capítulos -Ports and Servicese rede Byte Orders.

A função de escrita

A função de gravação tenta gravar nbyte bytes do buffer apontado por buf para o arquivo associado ao descritor de arquivo aberto, fildes .

Você também pode usar a função send () para enviar dados para outro processo.

#include <unistd.h>

int write(int fildes, const void *buf, int nbyte);

Após a conclusão bem-sucedida, write () retorna o número de bytes realmente gravados no arquivo associado a fildes. Este número nunca é maior do que nbyte. Caso contrário, -1 é retornado.

Parâmetros

  • fildes - É um descritor de socket retornado pela função de socket.

  • buf - É um indicador para os dados que você deseja enviar.

  • nbyte- É o número de bytes a serem gravados. Se nbyte for 0, write () retornará 0 e não terá outros resultados se o arquivo for um arquivo normal; caso contrário, os resultados não são especificados.

A função de leitura

A função read tenta ler nbyte bytes do arquivo associado ao buffer, fildes, no buffer apontado por buf.

Você também pode usar a função recv () para ler dados para outro processo.

#include <unistd.h>

int read(int fildes, const void *buf, int nbyte);

Após a conclusão bem-sucedida, write () retorna o número de bytes realmente gravados no arquivo associado a fildes. Este número nunca é maior do que nbyte. Caso contrário, -1 é retornado.

Parâmetros

  • fildes - É um descritor de socket retornado pela função de socket.

  • buf - É o buffer para ler as informações.

  • nbyte - É o número de bytes a ler.

A função fork

A função fork cria um novo processo. O novo processo denominado processo filho será uma cópia exata do processo de chamada (processo pai). O processo filho herda muitos atributos do processo pai.

#include <sys/types.h>
#include <unistd.h>

int fork(void);

Após a conclusão bem-sucedida, fork () retorna 0 para o processo filho e o ID do processo filho para o processo pai. Caso contrário, -1 é retornado ao processo pai, nenhum processo filho é criado e errno é definido para indicar o erro.

Parâmetros

  • void - Isso significa que nenhum parâmetro é necessário.

A função bzero

The bzero function places nbyte null bytes in the string s. This function is used to set all the socket structures with null values.

void bzero(void *s, int nbyte);

This function does not return anything.

Parameters

  • s − It specifies the string which has to be filled with null bytes. This will be a point to socket structure variable.

  • nbyte − It specifies the number of bytes to be filled with null values. This will be the size of the socket structure.

The bcmp Function

The bcmp function compares byte string s1 against byte string s2. Both strings are assumed to be nbyte bytes long.

int bcmp(const void *s1, const void *s2, int nbyte);

This function returns 0 if both strings are identical, 1 otherwise. The bcmp() function always returns 0 when nbyte is 0.

Parameters

  • s1 − It specifies the first string to be compared.

  • s2 − It specifies the second string to be compared.

  • nbyte − It specifies the number of bytes to be compared.

The bcopy Function

The bcopy function copies nbyte bytes from string s1 to the string s2. Overlapping strings are handled correctly.

void bcopy(const void *s1, void *s2, int nbyte);

Esta função não retorna nada.

Parâmetros

  • s1 - Especifica a string de origem.

  • s2v - Especifica a string de destino.

  • nbyte - Especifica o número de bytes a serem copiados.

A função memset

A função memset também é usada para definir variáveis ​​de estrutura da mesma forma quebzero. Dê uma olhada em sua sintaxe, fornecida abaixo.

void *memset(void *s, int c, int nbyte);

Esta função retorna um ponteiro para void; na verdade, um ponteiro para a memória do conjunto e você precisa classificá-la de acordo.

Parâmetros

  • s - Especifica a fonte a ser definida.

  • c - Especifica o caractere a ser definido em locais de nbyte.

  • nbyte - Especifica o número de bytes a serem definidos.

Para tornar um processo um servidor TCP, você precisa seguir os passos abaixo -

  • Crie um socket com a chamada de sistema socket () .

  • Vincule o soquete a um endereço usando a chamada de sistema bind () . Para um soquete de servidor na Internet, um endereço consiste em um número de porta na máquina host.

  • Ouça as conexões com a chamada do sistema listen () .

  • Aceite uma conexão com a chamada de sistema accept () . Essa chamada normalmente é bloqueada até que um cliente se conecte ao servidor.

  • Envie e receba dados usando as chamadas de sistema read () e write () .

Agora, vamos colocar essas etapas na forma de código-fonte. Coloque este código no arquivo server.c e compile-o com o compilador gcc .

#include <stdio.h>
#include <stdlib.h>

#include <netdb.h>
#include <netinet/in.h>

#include <string.h>

int main( int argc, char *argv[] ) {
   int sockfd, newsockfd, portno, clilen;
   char buffer[256];
   struct sockaddr_in serv_addr, cli_addr;
   int  n;
   
   /* First call to socket() function */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      perror("ERROR opening socket");
      exit(1);
   }
   
   /* Initialize socket structure */
   bzero((char *) &serv_addr, sizeof(serv_addr));
   portno = 5001;
   
   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = INADDR_ANY;
   serv_addr.sin_port = htons(portno);
   
   /* Now bind the host address using bind() call.*/
   if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
      perror("ERROR on binding");
      exit(1);
   }
      
   /* Now start listening for the clients, here process will
      * go in sleep mode and will wait for the incoming connection
   */
   
   listen(sockfd,5);
   clilen = sizeof(cli_addr);
   
   /* Accept actual connection from the client */
   newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
	
   if (newsockfd < 0) {
      perror("ERROR on accept");
      exit(1);
   }
   
   /* If connection is established then start communicating */
   bzero(buffer,256);
   n = read( newsockfd,buffer,255 );
   
   if (n < 0) {
      perror("ERROR reading from socket");
      exit(1);
   }
   
   printf("Here is the message: %s\n",buffer);
   
   /* Write a response to the client */
   n = write(newsockfd,"I got your message",18);
   
   if (n < 0) {
      perror("ERROR writing to socket");
      exit(1);
   }
      
   return 0;
}

Lidar com conexões múltiplas

Para permitir que o servidor lide com várias conexões simultâneas, fazemos as seguintes alterações no código acima -

  • Coloque a instrução de aceitação e o código a seguir em um loop infinito.

  • Depois que uma conexão for estabelecida, chame fork () para criar um novo processo.

  • O processo filho fechará sockfd e chamará a função doprocessing , passando o novo descritor de arquivo de socket como um argumento. Quando os dois processos concluem sua conversação, conforme indicado pelo retorno de doprocessing () , esse processo simplesmente sai.

  • O processo pai fecha newsockfd . Como todo esse código está em um loop infinito, ele retornará à instrução de aceitação para aguardar a próxima conexão.

#include <stdio.h>
#include <stdlib.h>

#include <netdb.h>
#include <netinet/in.h>

#include <string.h>

void doprocessing (int sock);

int main( int argc, char *argv[] ) {
   int sockfd, newsockfd, portno, clilen;
   char buffer[256];
   struct sockaddr_in serv_addr, cli_addr;
   int n, pid;
   
   /* First call to socket() function */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      perror("ERROR opening socket");
      exit(1);
   }
   
   /* Initialize socket structure */
   bzero((char *) &serv_addr, sizeof(serv_addr));
   portno = 5001;
   
   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = INADDR_ANY;
   serv_addr.sin_port = htons(portno);
   
   /* Now bind the host address using bind() call.*/
   if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
      perror("ERROR on binding");
      exit(1);
   }
   
   /* Now start listening for the clients, here
      * process will go in sleep mode and will wait
      * for the incoming connection
   */
   
   listen(sockfd,5);
   clilen = sizeof(cli_addr);
   
   while (1) {
      newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
		
      if (newsockfd < 0) {
         perror("ERROR on accept");
         exit(1);
      }
      
      /* Create child process */
      pid = fork();
		
      if (pid < 0) {
         perror("ERROR on fork");
         exit(1);
      }
      
      if (pid == 0) {
         /* This is the client process */
         close(sockfd);
         doprocessing(newsockfd);
         exit(0);
      }
      else {
         close(newsockfd);
      }
		
   } /* end of while */
}

O seguinte seqment de código mostra uma implementação simples da função de doprocessamento .

void doprocessing (int sock) {
   int n;
   char buffer[256];
   bzero(buffer,256);
   n = read(sock,buffer,255);
   
   if (n < 0) {
      perror("ERROR reading from socket");
      exit(1);
   }
   
   printf("Here is the message: %s\n",buffer);
   n = write(sock,"I got your message",18);
   
   if (n < 0) {
      perror("ERROR writing to socket");
      exit(1);
   }
	
}

Para tornar um processo um cliente TCP, você precisa seguir os passos abaixo & minus;

  • Crie um socket com a chamada de sistema socket () .

  • Conecte o soquete ao endereço do servidor usando a chamada de sistema connect () .

  • Envie e receba dados. Existem várias maneiras de fazer isso, mas a maneira mais simples é usar as chamadas de sistema read () e write () .

Agora, vamos colocar essas etapas na forma de código-fonte. Coloque este código no arquivoclient.c e compilar com gcc compilador.

Execute este programa e passe o nome do host e o número da porta do servidor, para conectar ao servidor, que você já deve ter executado em outra janela do Unix.

#include <stdio.h>
#include <stdlib.h>

#include <netdb.h>
#include <netinet/in.h>

#include <string.h>

int main(int argc, char *argv[]) {
   int sockfd, portno, n;
   struct sockaddr_in serv_addr;
   struct hostent *server;
   
   char buffer[256];
   
   if (argc < 3) {
      fprintf(stderr,"usage %s hostname port\n", argv[0]);
      exit(0);
   }
	
   portno = atoi(argv[2]);
   
   /* Create a socket point */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      perror("ERROR opening socket");
      exit(1);
   }
	
   server = gethostbyname(argv[1]);
   
   if (server == NULL) {
      fprintf(stderr,"ERROR, no such host\n");
      exit(0);
   }
   
   bzero((char *) &serv_addr, sizeof(serv_addr));
   serv_addr.sin_family = AF_INET;
   bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
   serv_addr.sin_port = htons(portno);
   
   /* Now connect to the server */
   if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
      perror("ERROR connecting");
      exit(1);
   }
   
   /* Now ask for a message from the user, this message
      * will be read by server
   */
	
   printf("Please enter the message: ");
   bzero(buffer,256);
   fgets(buffer,255,stdin);
   
   /* Send message to the server */
   n = write(sockfd, buffer, strlen(buffer));
   
   if (n < 0) {
      perror("ERROR writing to socket");
      exit(1);
   }
   
   /* Now read server response */
   bzero(buffer,256);
   n = read(sockfd, buffer, 255);
   
   if (n < 0) {
      perror("ERROR reading from socket");
      exit(1);
   }
	
   printf("%s\n",buffer);
   return 0;
}

Aqui está uma lista de todas as funções relacionadas à programação de soquetes.

Portas e funções de serviço

O Unix fornece as seguintes funções para buscar o nome do serviço do arquivo / etc / services.

  • struct servent *getservbyname(char *name, char *proto) - Esta chamada pega um nome de serviço e um nome de protocolo e retorna o número da porta correspondente para esse serviço.

  • struct servent *getservbyport(int port, char *proto) - Esta chamada pega um número de porta e um nome de protocolo e retorna o nome do serviço correspondente.

Funções de ordenação de bytes

  • unsigned short htons (unsigned short hostshort) - Esta função converte quantidades de 16 bits (2 bytes) da ordem de bytes do host para a ordem de bytes da rede.

  • unsigned long htonl (unsigned long hostlong) - Esta função converte quantidades de 32 bits (4 bytes) da ordem de bytes do host para a ordem de bytes da rede.

  • unsigned short ntohs (unsigned short netshort) - Esta função converte quantidades de 16 bits (2 bytes) da ordem de bytes da rede para a ordem de bytes do host.

  • unsigned long ntohl (unsigned long netlong) - Esta função converte quantidades de 32 bits da ordem de bytes da rede para a ordem de bytes do host.

Funções de endereço IP

  • int inet_aton (const char *strptr, struct in_addr *addrptr)- Esta chamada de função converte a string especificada, na notação de ponto padrão da Internet, em um endereço de rede e armazena o endereço na estrutura fornecida. O endereço convertido estará em Network Byte Order (bytes ordenados da esquerda para a direita). Retorna 1 se a string for válida e 0 em caso de erro.

  • in_addr_t inet_addr (const char *strptr)- Esta chamada de função converte a string especificada, na notação de ponto padrão da Internet, em um valor inteiro adequado para uso como um endereço da Internet. O endereço convertido estará em Network Byte Order (bytes ordenados da esquerda para a direita). Ele retorna um endereço IPv4 ordenado por byte de rede binário de 32 bits e INADDR_NONE em caso de erro.

  • char *inet_ntoa (struct in_addr inaddr) - Esta chamada de função converte o endereço de host da Internet especificado em uma string na notação de ponto padrão da Internet.

Funções Socket Core

  • int socket (int family, int type, int protocol) - Esta chamada retorna um descritor de socket que você pode usar em chamadas de sistema posteriores ou dá -1 em caso de erro.

  • int connect (int sockfd, struct sockaddr *serv_addr, int addrlen)- A função de conexão é usada por um cliente TCP para estabelecer uma conexão com um servidor TCP. Esta chamada retorna 0 se se conectar com sucesso ao servidor, caso contrário, retorna -1.

  • int bind(int sockfd, struct sockaddr *my_addr,int addrlen)- A função bind atribui um endereço de protocolo local a um socket. Esta chamada retorna 0 se se ligar ao endereço com sucesso, caso contrário, retorna -1.

  • int listen(int sockfd, int backlog)- A função de escuta é chamada apenas por um servidor TCP para escutar a solicitação do cliente. Esta chamada retorna 0 em caso de sucesso, caso contrário, retorna -1.

  • int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen)- A função de aceitação é chamada por um servidor TCP para aceitar solicitações do cliente e estabelecer a conexão real. Esta chamada retorna um descritor não negativo em caso de sucesso, caso contrário, retorna -1.

  • int send(int sockfd, const void *msg, int len, int flags)- A função de envio é usada para enviar dados através de soquetes de fluxo ou soquetes de datagrama CONECTADOS. Esta chamada retorna o número de bytes enviados, caso contrário, retorna -1.

  • int recv (int sockfd, void *buf, int len, unsigned int flags)- A função recv é usada para receber dados por meio de soquetes de fluxo ou soquetes de datagramas CONECTADOS. Esta chamada retorna o número de bytes lidos no buffer, caso contrário, retorna -1 em caso de erro.

  • int sendto (int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen)- A função sendto é usada para enviar dados por soquetes de datagramas UNCONNECTED. Esta chamada retorna o número de bytes enviados, caso contrário, retorna -1 em caso de erro.

  • int recvfrom (int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen)- A função recvfrom é usada para receber dados de soquetes de datagrama UNCONNECTED. Esta chamada retorna o número de bytes lidos no buffer, caso contrário, retorna -1 em caso de erro.

  • int close (int sockfd)- A função close é usada para fechar a comunicação entre o cliente e o servidor. Esta chamada retorna 0 em caso de sucesso, caso contrário, retorna -1.

  • int shutdown (int sockfd, int how)- A função de desligamento é usada para fechar normalmente uma comunicação entre o cliente e o servidor. Esta função oferece mais controle em comparação com a função de fechamento. Ele retorna 0 em caso de sucesso, -1 caso contrário.

  • int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout) - Esta função é usada para ler ou gravar vários sockets.

Funções auxiliares de soquete

  • int write (int fildes, const void *buf, int nbyte)- A função de gravação tenta gravar nbyte bytes do buffer apontado por buf para o arquivo associado ao descritor de arquivo aberto, fildes. Após a conclusão bem-sucedida, write () retorna o número de bytes realmente gravados no arquivo associado a fildes. Este número nunca é maior do que nbyte. Caso contrário, -1 é retornado.

  • int read (int fildes, const void *buf, int nbyte)- A função read tenta ler nbyte bytes do arquivo associado ao descritor de arquivo aberto, fildes, no buffer apontado por buf. Após a conclusão bem-sucedida, write () retorna o número de bytes realmente gravados no arquivo associado a fildes. Este número nunca é maior do que nbyte. Caso contrário, -1 é retornado.

  • int fork (void)- A função fork cria um novo processo. O novo processo, denominado processo filho, será uma cópia exata do processo de chamada (processo pai).

  • void bzero (void *s, int nbyte)- A função bzero coloca nbyte bytes nulos na string s. Esta função será usada para definir todas as estruturas de socket com valores nulos.

  • int bcmp (const void *s1, const void *s2, int nbyte)- A função bcmp compara a string de bytes s1 com a string de bytes s2. Ambas as cadeias são assumidas como tendo nbyte bytes de comprimento.

  • void bcopy (const void *s1, void *s2, int nbyte)- A função bcopy copia nbyte bytes da string s1 para a string s2. Strings sobrepostas são tratadas corretamente.

  • void *memset(void *s, int c, int nbyte) - A função memset também é usada para definir variáveis ​​de estrutura da mesma forma que bzero.