Unconfigured Ad Widget

Collapse

Anúncio

Collapse
No announcement yet.

Tutorial de Winsocks

Collapse
X
 
  • Filter
  • Tempo
  • Show
Clear All
new posts

  • Font Size
    #1

    Tutorial Tutorial de Winsocks

    Conteudo :

    01 Motivacao
    02 Sobre o tutorial
    03 Quem deve ler este tutorial
    04 Oq voce ira aprender
    05 Programacao de sockets
    06 Inicializando a winsock
    07 Inicializando a winsock, exemplo
    08 Socket handles
    09 Sockets
    10 Inicializando um socket
    11 Enderecos locais e remotos
    12 Representacao de enderecos IP para os sockets
    13 Resolvendo de host para endereco IP
    14 Estabelecendo uma conexao
    15 Uso de enderecos(struct sockaddr_in), gethostbyname(), socket(), e connect() para estabelecer uma conexao, exemplo
    16 Obtendo o endereco local de um cliente
    17 Obtendo o endereco local de um cliente, exemplo
    18 Formato de host e formato de rede
    19 Enviando e recebendo dados
    20 Tipos de IO
    21 Cliente de TCP, com IO nao bloqueado, e sincronizado, exemplo
    22 Ligando um endereco local a um socket
    23 Ligando um endereco local a um cliente socket, exemplo
    24 Servidores de TCP
    25 Servidor de TCP com IO bloqueado e sincronizado, exemplo
    26 Sockets UDP
    27 Cliente UDP com IO bloqueado e sincronizado, exemplo
    28 Servidor UDP com IO bloqueado e sincronizado, exemplo
    29 Comentario final


    1)----\
    +--------------------------------------------------------------------------------+
    | Motivacao |
    +--------------------------------------------------------------------------------+

    Estou cansado de tantos tutoriais em ingles, onde os amados leitores de lingua portuguesa, sao obrigados a sofrer, se esforcando a entender algo que nao esta em sua lingua, sem contar que e incrivel a indisponibilidade de tutoriais sobre sockets em portugues, e pro windows na internet, a maioria dos tutoriais sao para sockets no unix, no qual a diferenca e minima.

    2)----\
    +--------------------------------------------------------------------------------+
    | Sobre o tutorial |
    +--------------------------------------------------------------------------------+

    Este tutorial nao deve ser modificado, comercializado, ou publicado, nem em parte, nem em todo, sem autorizacao do autor, ou disponibilizado para download se auteracoes foram feitas no conteudo original. Se vc possuir sugestoes de melhora no conteudo do tutorial, ou se possuir correcoes para alguma informacao distorcida, por favor envie um email para mim o autor, lembrando todos direitos reservados, em relacao a escrita deste tutorial, e bem provavel que exista problemas gramaticais, infelismente ja nao sou tao bem no portugues o quanto antes desde que mudei pra um pais estrangeiro no qual portugues nao e falado, de toda forma, sem preocupacoes, darei o melhor de si. Em relacao a duvidas, mande-as para o email: c0d3r__@hotmail.com
    e as mais elaboradas serao selecionadas para fazer parte da FAQ, deste tutorial.

    3)----\
    +--------------------------------------------------------------------------------+
    | Quem deve ler este tutorial |
    +--------------------------------------------------------------------------------+

    Este tutorial foi escrito para voce, ser motivado, que deseja aprender sobre sockets no sistema windows, antes de comecar gostaria de dizer que conhecimento da linguaguem C, ou C++ e necessario para que se possa absorver o conteudo desse tutorial, tambem uma base em redes e essencial. Se voce nao possui esse conhecimento, o aprendizado nao sera tao facil pq nao estaras acostumado a determinados termos e conceitos, e nao ira entender os exemplos. Os exemplos seram disponibilizados em C particularmente, que nao sera um problema para programadores de C++ que conhecam a "Standard Function Library" do C++ que foi herdada da linguaguem C.

    4)----\
    +--------------------------------------------------------------------------------+
    | O que voce ira aprender |
    +--------------------------------------------------------------------------------+

    Voce ira aprender a base necessaria para se desenvolver aplicacoes para rede no sistema windows para comunicacao remota, onde sockets TCP, e UDP seram explorados.

    5)----\
    +--------------------------------------------------------------------------------+
    | Programacao de sockets |
    +--------------------------------------------------------------------------------+

    A programacao de sockets e uma API(Application programming Interface). Ou interface de programacao criada especialmente para rede, neste tutorial estarei mostrando a winsockets ou winsock, que e a API de sockets para windows, a winsock originou-se do BSD(Berkeley software distribution) que e a primeira API para a rede desenvolvida em sistemas unix, oq siguinifica que a winsock e totalmente compativel com a programacao de sockets em sistemas unix, com algumas alteracoes simples no codigo. Atualmente no momento dessa escrita a versao da winsock e a 2.2, e sera pra essa versao os exemplos nesse tutorial.

    6)----\
    +--------------------------------------------------------------------------------+
    | Inicializando a winsock |
    +--------------------------------------------------------------------------------+

    Para se usar a API winsock e necessario que o programador a inicialize, existe a funcao:

    int WSAStartup(
    WORD wVersionRequested,
    LPWSADATA lpWSAData
    );

    Que esta incluida no header winsock2.h.

    Como vc pode notar acima a funcao retorna um inteiro, e pega 2 argumentos, o primeiro um valor WORD(2 bytes), a versao da winsock que vc deseja utilizar, e o segundo um ponteiro para uma estrutura WSAData, onde informacoes sobre a winsock sao retornadas para essa estrutura; Se vc estiver se perguntando WORD e um tipo "typedef" definido na API, como e o LPWSADATA.

    Agora observaremos a estrutura WSAData, no qual um ponteiro para ela deve ser passado para WSAStartup antes de utilizar sockets.

    typedef struct WSAData
    {

    WORD wVersion;
    WORD wHighVersion;
    char szDescription[WSADESCRIPTION_LEN+1];
    char szSystemStatus[WSASYS_STATUS_LEN+1];
    unsigned short iMaxSockets;
    unsigned short iMaxUdpDg;
    char FAR* lpVendorInfo;

    } WSADATA, *LPWSADATA;

    WORD wVersion;
    A versao da API winsock.

    WORD wHighVersion;
    A versao mais alta que a API suporta.

    char szDescription[WSADESCRIPTION_LEN+1];
    Descricao da winsock.

    char szSystemStatus[WSASYS_STATUS_LEN+1];
    O status.

    Os tres ultimos campos:

    unsigned short iMaxSockets;
    unsigned short iMaxUdpDg;
    char FAR* lpVendorInfo;

    foram inutilizaveis, porem ainda sao suportados para compatibilidade com
    codigo anterior, mas nao devem ser utilizados em codigo que usa a versao 2.2 da winsock pra cima.

    Nosso primeiro programa desse tutorial ira demonstrar o processo supeeeeer simples de se iniciar a livraria do winsocket para uso.

    Antes de mais nada crie um projeto em seu compilador de C/C++ favorito. depois faca como esta abaixo:

    7)----\
    +--------------------------------------------------------------------------------+
    | Inicializando a winsock, exemplo |
    +--------------------------------------------------------------------------------+

    #include <winsock2.h> /*Essa header e super importante
    nela esta as definicoes de dados e proto
    typings de funcoes necessarios. Entre outras
    coisas.*/

    #include <stdio.h> /*Uso de printf()*/
    #include <stdlib.h> /*Uso de system();*/


    int main()
    {
    WORD wVersao = MAKEWORD(2, 2); /*Declaro uma WORD wVersao inicializada usando
    o macro MAKEWORD, no qual retorna uma WORD(2 bytes) onde o primeiro representa a versao, e o segundo a sub-versao, que sao passados pra MAKEWORD.*/



    WSADATA wsData; /*Declaro uma estrutura wsData, onde informacoes de retorno sobre a winsock sao guardadas nela*/

    /*
    Abaixo observemos o uso da funcao WSAStartup(), passando a versao construida com MAKEWORD, e
    depois passamos o endereco da strutura, usando o operador &. A funcao WSAStartup() retorna 0, ou
    NO_ERROR, se tudo ocorreu bem. Entao verificamos usando if, se o valor de retorno e diferente de 0, se
    for entao temos um erro, mostre erro e retorne.
    */

    if(WSAStartup(wVersao, &wsData) != NO_ERROR)
    {
    printf("Error: winsock %hd.%hd nao pode ser inicializado.\n", LOBYTE(wVersao), HIBYTE(wVersao));
    return 0;
    }

    /*
    Agora se nenhum erro ocorreu mostre o que a wsData contem a respeito do winsock lembrando que
    alguns campos estao inutilizaveis, como foi dito anteriormente.
    */

    printf("%s - %s\n", wsData.szDescription, wsData.szSystemStatus);

    printf("Versao sendo usada: %hd.%hd\n",LOBYTE(wsData.wVersion), HIBYTE(wsData.wVersion));

    printf("Versao maxima suportada: %hd.%hd\n", LOBYTE(wsData.wHighVersion), HIBYTE (wsData.wHighVersion));

    /*
    WSACleanup() e necessaria, para liberar recursos que foram alocados pra aplicacao em particular, ela
    retorna NO_ERROR se sucedeu, e nao pega argumentos
    */

    if(WSACleanup() != NO_ERROR)
    fprintf(stderr, "Error: nao foi possivel finalizar o winsock.\n");
    else
    printf("%s finalizada...\n", wsData.szDescription);

    return 0;

    }

    -----------------------------------------------------------------------------------

    Se vc estiver se perguntando quando uso printf pra mostrar a versao e a versao maxima
    o uso do macro LOBYTE, e HIBYTE, e necessario pq os mesmos retornam o primeiro byte da WORD o low, e o segundo byte o high, onde o low representa a versao maior do winsock e o high representa a subversao, assim mostramos os dados corretamente.
    2.2 seria 2 a versao maior, e 0 a menor.

    Agora tente compilar o seu projeto, observe que se voce nao linkou a library do winsock ao projeto havera varios erros no seu projeto que inicialmente pareceram estranhos, nao se assuste, somente linke a library do winsock chamada ws2_32.lib ao seu projeto, para isso bisbilhote o seu compilador de c/c++ na parte de opcoes de projeto, ou configuracoes de projetos e procure por link, depois disso somente link a library ws2_32.lib, como o processo varia de compilador a compilador nao irei generalizar como isso e feito, mas geralmente vc escreve a lib, e clica um butao link, depois aplicar, depois que vc fez isso tente compilar, vc vera que os erros desapareceram, e vera o resultado do programa acima.

    No visual c++ 6, o processo seria,
    clique no menu projects -> settings... -> link -> no final do campo Object/Library modules digite ws2_32.lib e clique OK.

    Como voce pode notar vc linka a ws2_32.lib para se ter acesso as funcoes do winsock, se vc nao linkar a essa library(DLL), vc nao podera compilar o programa acima, e nenhum exemplo desse tutorial.

    Vale apena salientar que depois que o winsock e inicializado atravez da WSAStartup()
    e necessario desallocar os recursos inicializados utilizando WSACleanup(), se vc nao faz isso depois nao se pergunte pq o seu sistema se torna lento depois de um tempo, por mal-pratica de programacao, ou seja alocacao de recursos que nao sao liberados.

    ----\
    +--------------------------------------------------------------------------------+
    | Socket handles |
    +--------------------------------------------------------------------------------+

    Um socket handle e um inteiro, definido no header winsock2.h no qual e dado um numero ao socket handle para identificar um socket particular para cada chamada de funcao, dessa forma, inicializacao, operacoes de leitura de dados, escrita, e dealocacao de recursos podem ser feitos para um particular socket. Quando o programa e finalizado o socket handle deicha de existir, quando o socket handle e feichado usando a funcao closesocket(), recursos do socket sao liberados.

    Pode-se pensar de um socket handle como um ID para identificar um socket.

    Socket handles sao declarados da seguinte forma:

    SOCKET socket;

    Como vc pode observar acima, SOCKET e um inteiro typedefinido como SOCKET, e socket o nome da variavel.

    9)----\
    +--------------------------------------------------------------------------------+
    | Sockets |
    +--------------------------------------------------------------------------------+

    O que realmente siguinifica o termo socket, um socket e relacionado ao programa que acessa recursos na rede, no qual o mesmo, socket, representa um ip e uma porta, que e chamado de endereco de socket geralmente no seguinte formato:

    iporta

    Como por exemplo:

    127.0.0.1:139

    Vc ja deve ter visto o exemplo acima em varios programas de rede, como o netstat, se vc nao viu, abra o netstat, no windows XP va em iniciar -> run -> digite cmd -> no prompt de comando digite netstat -na, vc ira observar, local address, e remote address, e abaixo uma lista com enderecos no formato demonstrado acima, esses enderecos sao enderecos de sockets que por sua vez sao representados por sockets.

    10)----\
    +--------------------------------------------------------------------------------+
    | Inicializando um socket |
    +--------------------------------------------------------------------------------+

    Antes de inicializar um socket, um pouco de conhecimento de rede entra em questao, pois nao quero voltar a esse assunto basico, existem varios tipos de protocolos de rede, protocolos de baixo nivel, a nivel de rede, lembra da camada de rede, na suite TCP/IP e OSI model, entao o protocolo mais usado da camada de rede ou protocolo de rede e com certeza o IPv4, ou internet protocol versao 4, que e a internet que conhecemos hoje em dia, na programacao de sockets e suportado diversos tipos de redes, como IPX/SPX, APPLETALK, entre outras. Porem nesse tutorial irei cobrir somente o protocolo IPv4, que e o mais usado atualmente. Esses protocolos na programacao de sockets sao conhecidos como address family ou endereco de familia, o endereco de familia que vamos usar sera AF_INET que e o do IP.

    Existem 3 tipos de sockets dentro da familia AF_INET, eles sao: SOCK_STREAM, SOCK_DGRAM, e SOCK_RAW.

    E utilizado o SOCK_STREAM, pra formar um socket TCP, client ou servidor, para um socket UDP e utilizado o SOCK_DGRAM, e para um socket RAW, que e um socket puro, onde vc determina o protocolo de transporte, entre outras coisas, e utilizado o SOCK_RAW.

    Nesse tutorial vamos comecar explorando os conceitos de um cliente de TCP, veremos, como o envio e recebimento de dados e feito usando chamadas bloqueadas e nao bloqueadas, veremos tambem oq e sincronizacao e nao-sincroninazao, e entao os conceitos de servidores TCP seram explorados.

    Na ultima parte do tutorial, vamos explorar sockets UDP, como clientes e servidores sao estruturados.

    Raw sockets sera introduzido no proximo tutorial.

    Para se inicializar um socket e necessario chamar a funcao:

    SOCKET socket(
    int af,
    int type,
    int protocol
    );

    Como vc pode observar acima essa funcao retorna um socket handle, que ja vimos anteriormente, e pega 3 argumentos, eles sao:

    int af
    A familia de sockets.

    int type
    O tipo de sockets, em particular o transporte.

    int protocol
    E o protocolo do socket, que irei falar sobre ele agora.

    O protocol, e simplesmente um numero geral, determinado pelos pais da internet, ou toda a comunidade que desenvolveu a internet que unicamente define um protocolo, os mais usados sao, IPPROTO_IP, IPPROTO_TCP, IPPROTO_UDP.

    Como vc pode observar acima, de acordo com o tipo de socket vamos utilizar o seu respectivo protocolo, exemplo: Se uso um tipo SOCK_STREAM para um socket TCP usaremos entao IPPROTO_TCP.

    Se vc estiver se perguntando AF_INET, SOCK_STREAM, e os outros sao numeros especificos definidos no header winsock2.h

    A funcao socket inicializa um socket e retorna o seu handle, ou retorna INVALID_SOCKET em caso de erro.

    Vc irar observar os detalhes no exemplo que esta quase a seguir, o nosso primeiro cliente TCP, mais antes devo voltar para discurcao de enderecos locais e remotos.

    11)----\
    +--------------------------------------------------------------------------------+
    | Enderecos locais e remotos |
    +--------------------------------------------------------------------------------+

    Para que vc possa se conectar a um servidor de TCP remoto, e necessario especificar informacoes sobre o servidor remoto, como o ip e a porta do servidor remoto, conhecido como endereco remoto, ou se vc queira um servidor rodando em uma porta, voce precisara de um endereco local, como um ip e uma porta que representara o processo local, entao e pra isso que e usado a estrutura sockaddr_in que sera mostrada abaixo:

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

    short sin_family;
    A familia do socket. AF_INET neste tutorial.

    u_short sin_port;
    A porta remota ou local depende se vc esta fazendo um cliente ou servidor.

    struct in_addr sin_addr;
    A struct in_addr sera vista na proxima sessao, a mesma e usada para se atribuir um endereco de IP.

    char sin_zero[8];
    Array de 0 bytes.

    Com essa strutura o programador ira atribuir diversos valores a mesma, para criar respectivos enderecos locais, no caso de um servidor; Ou remoto, no caso de um client; Para uso com funcoes da winsock.

    12)----\
    +--------------------------------------------------------------------------------+
    | Representacao de enderecos IP para os sockets |
    +--------------------------------------------------------------------------------+

    Existem distintas representacoes de endereco IP, como por exemplo: existe a dotted decimal(numeros decimais separados por pontos), e existe o numero IP.

    A primeira a dotted decimal e uma string, como vc deve saber uma string em C e uma array de caracteres terminada por 0.

    A segunda e um numero de 4 bytes, em binario que representa o IP.

    Para o uso de sockets e necessario a segunda representacao, ou seja um numero de 4 bytes, como vc deve esta percebendo, nao podemos usar para setar a estrutura de endereco sockaddr_in, ips do tipo "200.222.222.111" e sim IPS de 4 bytes numericos, por isso e introduzida a funcao:
    unsigned long inet_addr( const char* cp);
    A funcao inet_addr que somente pega um IP string, ou dotted decimal, e retorna a sua representacao numerica ou INADDR_NONE para dizer que o IP string esta incorreto.
    O inverso e feito se desejamos converter de IP numerico para IP string, usamos a funcao:
    char * FAR inet_ntoa(struct in_addr in);
    A inet_ntoa() retorna o IP string e pega uma struct in_addr.
    A struct in_addr e a struct que representa o IP em unsigned long que esta declarada dentro de sockaddr_in, vejamos ela a seguir:
    typedef struct in_addr {
    union {
    struct {u_char s_b1,s_b2,s_b3,s_b4;} S_un_b;
    struct {u_short s_w1, s_w2;} S_un_w;
    u_long S_addr;
    } S_un;
    } in_addr;
    Analizando a struct in_addr, e possivel notar, que a mesma e uma estrutura que contem uma union chamada S_un, nessa union existe tb 2 estruturas embutidas, a primeira S_un_b te retorna os bytes de cada parte do IP, a segunda S_un_w te retorna as words do IP, e o terceiro membro S_addr e um numero longo que representa o IP, estaremos utilizando o terceiro membro para atribuir um IP. Mas os outros membros podem muito bem servir em nossos programas.

    13)----\
    +--------------------------------------------------------------------------------+
    | Resolvendo de host para endereco IP |
    +--------------------------------------------------------------------------------+

    Vc ja deve saber que na internet utilizamos numeros IP para a comunicacao, mas muitas vezes o usuario de nossa aplicacao nao sabera o IP e sim, o dominio do IP, como Apenas usuários registrados e ativados podem ver os links., Clique aqui para se cadastrar..., ou localhost, que representam 2 ips diferentes, por esse motivo e necessario se traduzir de nome para numero IP utilizado o DNS(domain name system) atravez da funcao:

    struct hostent* FAR gethostbyname(
    const char* name
    );

    Como vc pode observar a funcao retorna um ponteiro para uma estrutura hostent que sera explorada a seguir, e pega um nome de dominio, dessa forma vc obtem o IP relacionado a um nome.

    A estrutura hostent:

    typedef struct hostent {

    char FAR* h_name;
    char FAR FAR** h_aliases;
    short h_addrtype;
    short h_length;
    char FAR
    FAR** h_addr_list;

    } hostent;

    char FAR* h_name;
    Nome do host sendo retornado

    char FAR FAR** h_aliases;
    Lista de nomes alternativos do host sendo retornado

    short h_addrtype;
    Tipo do endereco, AF_INET.

    short h_length;
    Tamanho do endereco, IPv4, e sempre 4 bytes.

    char FAR FAR** h_addr_list;
    Lista de numeros IP , relacionados ao nome.

    Dentre todos esses campos, utilizaremos o h_addr_list, onde existe um macro h_addr que representa o primeiro IP na lista.

    14)----\
    +--------------------------------------------------------------------------------+
    | Estabelecendo uma conexao |
    +--------------------------------------------------------------------------------+

    Depois que vc especifica o endereco remoto, no caso de um cliente, no qual ira se conectar a um servidor remoto, vc usa a funcao:

    int connect(
    SOCKET s,
    const struct sockaddr* name,
    int namelen
    );

    A mesma retorna um inteiro, SOCKET_ERROR se ocorreu um erro, ou 0 se nao ocorreu nenhum error. Pega um socket handle no primeiro argumento, e um ponteiro para uma estrutura sockaddr no segundo, e o tamanho em bytes dessa estrutura no terceiro.

    Para que vc veja como todo bla, bla, bla, anterior e feito e comece a entender melhor vou disponibilizar um exemplo simples de um cliente de TCP. que somente se conecta ao servidor e mais nada.

    O exemplo abaixo tentara se conectar a um servidor de http, na porta 80, e so isso, e em caso de erro mostrara informacoes sobre esse erro.
    Como um dominio e usado, e necessario se obter o IP utilizando a gethostbyname() que foi vista acima, quando o IP e obtido, o IP esta em formato unsigned long, e nao dotted decimal, pois o mesmo unsigned long e um numero e nao uma string, unsigned long e o formato perfeito para se setar o endereco, ou a struct in_addr que foi vista anteriormente, que esta declarada dentro da struct sockaddr_in pelo nome de sin_addr.

    Se estive-se usando enderecos ip como "200.234.2.1" note que e uma string, em formato dotted decimal teriamos que converter esse IP, para unsigned long, para isso e usado a funcao:

    unsigned long inet_addr(char *Ip);

    que pega um IP e retorna o numero correspondente que e o que interessa em unsigned long.

    15)----\
    +--------------------------------------------------------------------------------+
    | Uso de enderecos(struct sockaddr_in), gethostbyname(), socket(), e connect() para |
    | estabelecer uma conexao, exemplo |
    +--------------------------------------------------------------------------------+


    #include <stdio.h> /*Uso de printf()*/
    #include <stdlib.h> /*Uso de system()*/

    #include <winsock2.h> /*Essa header e super importante
    nela esta as definicoes de dados e proto
    typings de funcoes necessarios. Entre outras
    coisas.*/



    /* Definimos uma porta remota */
    #define PORTA_REMOTA 80

    /* Definimos um host remoto */
    #define HOST_REMOTO "www.ig.com.br"

    int main()
    {
    /* Declaracao de variaveis */

    WORD wVersao = MAKEWORD(2, 2);
    WSADATA wsData;

    /* Abaixo temos o socket handle, ou ID */
    SOCKET Cliente;

    /* Uma estrutura com informacoes sobre o endereco remoto */
    struct sockaddr_in EnderecoRemoto;

    /* Um ponteiro que apontara para informacoes sobre um determinado host */
    struct hostent *h = NULL;

    /* Inicializacao da winsock 2.2 */

    if(WSAStartup(wVersao, &wsData) != NO_ERROR)
    {

    /* a funcao WSAGetLastError() retorna o numero do error ocorrido */
    printf("Error code #%hd: winsock %hd.%hd nao pode ser inicializado.\n",
    WSAGetLastError(), LOBYTE(wVersao), HIBYTE(wVersao));

    return 0;
    }

    printf("%s - %s\n", wsData.szDescription, wsData.szSystemStatus);

    /* Inicializacao do cliente de TCP */

    Cliente = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (Cliente == INVALID_SOCKET)
    {
    printf("Error code #%hd: nao pode obter-se um socket TCP.\n",
    WSAGetLastError());
    return 0;
    }

    /* Preenchemos o EnderecoRemoto com informacao */

    /* A familia do socket */
    EnderecoRemoto.sin_family = AF_INET;
    /* Setamos a porta */
    EnderecoRemoto.sin_port = htons(PORTA_REMOTA);

    /*
    Agora observe que desde que usaremos um dominio, o dominio da IG, que foi
    definido logo no inicio do programa, e desde que a interface de baixo nivel requer um ip
    unsigned long usaremos a gethostbyname pra retornar um ponteiro pra uma estrutura hostent, onde
    apartir dela vamos obter o ip em unsigned long resolvido.
    */

    h = gethostbyname(HOST_REMOTO);
    /* Se h e NULL entao temos um erro. */
    if(!h)
    {
    printf("Error code #%hd: nao pode obter-se o ip atravez do nome %s.\n",

    WSAGetLastError(), HOST_REMOTO);
    return 0;
    }

    /*
    Aqui iremos setar o ip do endereco remoto com o ip dentro da estrutura hostent atravez do ponteiro h,
    sin_addr.s_addr representa o IP dentro da sockaddr_in, e h_addr retorna o ip em unsigned long dentro da
    hostent, s_addr apenas acessa S_addr dentro da struct in_addr.
    */

    EnderecoRemoto.sin_addr.s_addr = *((unsigned long *)h -> h_addr);

    /*
    Agora usamos connect passando o socket handle como primeiro argumento, e o EnderecoRemoto
    como segundo. Como a funcao connect no segundo argumento pega um ponteiro pra uma sockaddr,
    converteremos a nossa sockaddr_in * para sockaddr *, e passamos o tamanho em bytes do
    EnderecoRemoto no terceiro argumento. Dessa forma se tudo ocorrer bem, o valor 0 e retornado, ou
    NO_ERROR, se houver um problema SOCKET_ERROR e retornado.
    */

    if(connect(Cliente, (struct sockaddr *)&EnderecoRemoto, sizeof(EnderecoRemoto)) ==
    SOCKET_ERROR)
    {
    printf("Error code #%hd: nao pode conectar-se ao ip %s e porta %hu.\n",

    /*
    a funcao inet_ntoa() converte um IP unsigned long, para uma string, veremos inet_ntoa a seguir.
    */

    WSAGetLastError(), inet_ntoa(EnderecoRemoto.sin_addr), PORTA_REMOTA);
    return 0;
    }

    printf("Conexao ao servidor %s:%hu estabilizada.\n", inet_ntoa(EnderecoRemoto.sin_addr),
    PORTA_REMOTA);

    closesocket(Cliente); /* Fecha o socket handle */

    /* Finalizacao */
    if(WSACleanup() != NO_ERROR)
    fprintf(stderr, "Error code #%hd: nao foi possivel finalizar o winsock.\n",
    WSAGetLastError());
    else
    printf("%s finalizada...\n", wsData.szDescription);

    return 0;

    }

    -----------------------------------------------------------------------------------
    Se vc estiver se perguntando o por que do uso do htons() como na linha abaixo:

    EnderecoRemoto.sin_port = htons(PORTA_REMOTA);

    htons() e uma funcao de conversao, que e necessario, pq ela converte a porta para o formato de rede, voltarei a falar sobre isso depois.

    Nao se esqueca de linkar o seu projeto a lib ws2_32.
    Tente examinar o exemplo anterior calmamente e vera que nao e tao complicado o quanto parece, de toda forma as coisas ficaram mais claras com os proximos exemplos.

    16)----\
    +--------------------------------------------------------------------------------+
    | Obtendo o endereco local de um cliente |
    +--------------------------------------------------------------------------------+

    No exemplo acima usamos um endereco remoto para um socket no qual o mesmo especifica onde o nosso cliente de TCP ira se conectar, iporta, etc... mas vc pode chegar a se perguntar: e o endereco local do cliente?, ou ate mesmo nao perceber que clientes tem enderecos locais, que nao entra em questao no exemplo acima pq o sistema operacional fica encarregado de fazer um endereco local para o nosso socket, que no caso e formado por:

    Ip local:Porta local

    Exemplo:

    127.0.0.1:5001

    Lembra do netstat -na? que lista os enderecos locais e remotos dos sockets?
    Quando vc conectar o cliente de TCP acima vc podera observar a presenca de um endereco local na lista de coneccoes do netstat, e o endereco remoto que vc especificou no programa.

    Agora, qual a importancia de um endereco local? e logico, quando enviamos dados por TCP precisamos de um endereco remoto, e quando recebemos dados precisamos de um endereco local, por que assim o sistema sabera a qual processo, ou programa representado pela porta local, no sistema local, cliente, a que se deve passar os dados.

    Existe a funcao:

    int getsockname(
    SOCKET s,
    struct sockaddr* name,
    int* namelen
    );


    No qual retorna 0 ou NO_ERROR se a funcao procedeu corretamente, ou SOCKET_ERROR se houve um erro.

    O primeiro argumento e um socket handle, o segundo e um ponteiro para uma struct sockaddr, e o terceiro e um ponteiro para um inteiro que contem o tamanho da sockaddr.

    Vamos incorporar a funcao getsockname no nosso cliente assim saberemos a respeito do endereco local do nosso cliente.

    A primeira coisa a se fazer e declara uma estrutura sockaddr_in para o endereco local, depois um inteiro para guardar o tamanho dessa estrutura e fazer uma chamada a funcao getsockname(), veremos a incorporacao abaixo:

    17)----\
    +--------------------------------------------------------------------------------+
    | Obtendo o endereco local de um cliente, exemplo |
    +--------------------------------------------------------------------------------+

    #include <stdio.h>
    #include <stdlib.h>
    /* winsock header */
    #include <winsock2.h>


    /* Definimos uma porta remota */
    #define PORTA_REMOTA 80

    /* Definimos um host remoto */
    #define HOST_REMOTO "www.ig.com.br"

    int main()
    {

    /* Declaracao de variaveis */

    WORD wVersao = MAKEWORD(2, 2); /* Setamos a versao 2.2 */
    WSADATA wsData; /* Recebe informacoes pela WSAStartup() */

    /* Abaixo temos o socket handle, ou ID */
    SOCKET Cliente;

    /* Endereco remoto, e local */
    struct sockaddr_in EnderecoRemoto, EnderecoLocal;

    /* Um ponteiro que apontara para informacoes sobre um determinado host */
    struct hostent *h = NULL;

    /* Um inteiro que guardara o tamanho da strutura EnderecoLocal */
    int LenEnderecoLocal = sizeof(struct sockaddr_in);

    /* Inicializacao da winsock 2.2 */

    if(WSAStartup(wVersao, &wsData) != NO_ERROR)
    {

    /* a funcao WSAGetLastError() retorna o numero do error ocorrido */
    printf("Error code #%hd: winsock %hd.%hd nao pode ser inicializado.\n",
    WSAGetLastError(), LOBYTE(wVersao), HIBYTE(wVersao));

    return 0;
    }

    printf("%s - %s\n", wsData.szDescription, wsData.szSystemStatus);

    /* Inicializacao do cliente de TCP */

    Cliente = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (Cliente == INVALID_SOCKET)
    {
    printf("Error code #%hd: nao pode obter-se um socket TCP.\n",
    WSAGetLastError());
    return 0;
    }

    /* Preenchemos o EnderecoRemoto com informacao */

    /* A familia do socket */
    EnderecoRemoto.sin_family = AF_INET;
    /* Setamos a porta */
    EnderecoRemoto.sin_port = htons(PORTA_REMOTA);

    /*
    Agora observe que desde que usaremos um dominio, o dominio da IG, que foi
    definido logo no inicio do programa, e desde que a interface de baixo nivel requer um ip
    unsigned long usaremos a gethostbyname pra retornar um ponteiro pra uma estrutura hostent, onde
    apartir dela vamos obter o ip em unsigned long resolvido.
    */

    h = gethostbyname(HOST_REMOTO);
    /* Se h e NULL entao temos um erro. */
    if(!h)
    {
    printf("Error code #%hd: nao pode obter-se o ip atravez do nome %s.\n",

    WSAGetLastError(), HOST_REMOTO);
    return 0;
    }

    /*
    Aqui iremos setar o nosso endereco remoto com o ip de dentro da estrutura hostent
    atravez do ponteiro h
    */

    EnderecoRemoto.sin_addr.s_addr = *((unsigned long *)h -> h_addr);

    /*
    Agora usamos connect passando o socket handle como primeiro argumento, e o EnderecoRemoto
    como segundo. Como a funcao connect no segundo argumento pega um ponteiro pra uma sockaddr,
    converteremos a nossa sockaddr_in * para sockaddr *, e passamos o tamanho em bytes do
    EnderecoRemoto no terceiro argumento. Dessa forma se tudo ocorrer bem, o valor 0 e retornado, ou
    NO_ERROR, se houver um problema SOCKET_ERROR e retornado.
    */

    if(connect(Cliente, (struct sockaddr *)&EnderecoRemoto, sizeof(EnderecoRemoto)) ==
    SOCKET_ERROR)
    {
    printf("Error code #%hd: nao pode conectar-se ao ip %s e porta %hu.\n",

    /*
    a funcao inet_ntoa() converte um IP unsigned long, para uma string, veremos
    inet_ntoa a seguir.
    */
    WSAGetLastError(), inet_ntoa(EnderecoRemoto.sin_addr), PORTA_REMOTA);
    return 0;
    }

    printf("Conexao ao servidor %s:%hu estabilizada.\n", inet_ntoa(EnderecoRemoto.sin_addr),
    PORTA_REMOTA);

    /*
    Utilizamos getsockname() para retornar informacoes sobre o endereco local do socket.
    */

    if (getsockname(Cliente, (struct sockaddr *)&EnderecoLocal, &LenEnderecoLocal) == SOCKET_ERROR)
    {
    printf("Error code #%hd: nao pode obter-se o endereco local.\n",

    WSAGetLastError());
    return 0;
    }

    printf("\n\nLocal address\t\tRemote address\n\n");

    /*
    Mostramos o endereco local abaixo, note que inet_ntoa() converte o ip de unsigned long
    dentro de sin_addr, para uma string dotted decimal. Ja ntohs() converte a porta
    de formato de rede para formato de host.
    */

    printf("%-15s:%hu\t",
    inet_ntoa(EnderecoLocal.sin_addr), ntohs(EnderecoLocal.sin_port));

    printf("%s:%hu\n\n",
    inet_ntoa(EnderecoRemoto.sin_addr), ntohs(EnderecoRemoto.sin_port));

    /* Antes de terminar verifique com netstat -na o endereco local */
    system("pause");

    closesocket(Cliente); /* Fecha o socket handle */

    /* Finalizacao */
    WSACleanup();

    return 0;

    }

    18)----\
    +--------------------------------------------------------------------------------+
    | Formato de host e formato de rede |
    +--------------------------------------------------------------------------------+

    Observe o valor 256 em binario:

    00000001 00000000

    O valor decimal 256 quando e representado em binario, ou seja base 2 possui 2 bytes, como acima, que estao separados por espaco, a primeira parte representa 1 byte, e a segunda outro byte que nos da 2 bytes.

    Quando numeros crescem os mesmos crescem para a nossa esquerda por exemplo: 10, 100, 1000.

    Os numeros decimais acima crescem para nossa esquerda, quando contamos em base 10, quando um numero como 9 chega ate seu limite decimal zeramos esse numero e pulamos uma casa a esquerda e escrevemos um 1 e temos 10. observe: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 de 0 ate 9 ja temos 10 numeros, entao zeramos a casa de 9 pulamos uma casa a esquerda e comecamos a contar novamente ate 9 e repetimos o processo, observe: 99 chegamos ao limite das dezenas, zeramos as casas decimais 99 e acresentamos um 1 a nossa esquerda a assim temos 100, uma centena.

    Numeros binarios crescem dessa forma tambem, para a esquerda, observe:

    0, 1, 10, 11, 100.

    Sabemos que binario e base 2, entao temos 0, e 1, de numeros possiveis, entao como acima quando chegamos a 1 contando de 0, zeramos a casa a direita e crescemos esse numero para a esquerda, logo os numeros a esquerda sao considerados os MAIS SIGUINIFICANTES, e os numeros mais a direita sao os MENOS SIGUINIFICANTES.

    Se temos 200 em decimal sabemos que 2 e o numero mais siguinificante pq e o numero a esquerda se cortamos esse numero 2, diminuimos o valor decimal siguinificativamente para 0.

    Se temos 10001 em binario sabemos que 1 e o numero mais siguinificante pq e o numero a esquerda se cortamos esse numero 1, ficamos com somente 0001 em binario onde os zeros as esquerdas nao valem nada em que resulta em 1 binario.

    Mais o que isso tem haver com redes, sockets?

    Nos computadores dados sao guardados de formas distintas na memoria, a primeira forma e little endian, onde os byte menos siguinificantes sao guardados na memoria no lugar dos bytes mais siguinificantes, maquinas PC, utilizam o little endian.

    entao digamos se tiver-se mos o valor em hexadecimal:

    0xDCBA;

    E notavel que o BA e o byte least significant(menos significante) e que o DC e o byte most significant(mais significant) quero dizer os bits mais a nossa direita sao chamados de least significant bits, e os bits mais a nossa esquerda sao os most significant bits. Entao em little endian os bytes(8 bits) least significant sao guardados primeiro na memoria, ou inversamente do que seria logico.

    O valor acima e guardado na memoria de maquinas que utilizam little endian como o PC da IBM, da sequinte forma:

    0xBADC

    Onde o byte menos significante BA vem antes e depois (o ou os) byte(s) mais significante como o DC.

    O valor:

    0xDCBA

    Em big endian, ficaria na memoria da mesma forma logica. Uma maquina que usa big endian seria o MAC da Apple.

    Desque que existem diversas maneiras de se guardar bytes na memoria em diversas maquinas, na programacao de sockets os nossos bytes sao convertidos para big endian que tambem e conhecido como formato de rede, onde quando esses bytes chegam em seu destino, podendo ou nao ser uma maquina de endian diferente, os mesmos bytes sao convertidos para o formato de host que pode ser big endian ou little endian, dessa forma e evitado que bytes em um endian venha a representar valores diferente em outro endian.

    Lembra da linha:

    EnderecoRemoto.sin_port = htons(PORTA_REMOTA);

    Observe a funcao htons, a mesma siguinifica: host to network short, que em portugues quer dizer short(2 bytes) de formato de host para formato de rede, dessa forma a compatibilidade e garantida independente do endian, abaixo a descricao da htons():

    unsigned short htons(unsigned short Host);

    A funcao pega um u_short em formato host e retorna o seu formato de rede.

    E obrigatorio sempre apartir de um u_short em formato de rede retornar o seu respectivo formato de host, para manipulacao local, entao temos a funcao

    unsigned short ntohs(unsigned short Network);

    A funcao ntohs() pega uma word em formato de rede e a retorna em formato de host. ntohs siguinifica: network to host short.

    E se vc estiver manipulando 4 bytes, longs, ou longos? pra isso existe as funcoes:

    unsigned long htonl(unsigned long Host);

    Host to network long, que retorna o formato de rede apartir do formato de host, o l simboliza que e para longos.

    tambem tem a:

    unsigned long ntohl(unsigned long Network);

    Network to host long, que retorna o formato de host apartir do formato de rede, o l simboliza que e para longos.

    Resumindo, quando nossos bytes sao enviados na rede convertemos os mesmos para big endian, o formato de rede, utilizando as funcoes: htons() ou htonl(), que foram vistas acima, e quando recebemos esses bytes os convertemos para formato de host, que pode ser little endian ou big endian, utilizando ntohs() ou ntohl(), para manipulacao local desses bytes, dessa forma foi resolvido o problema de endians diferentes.

    19)----\
    +--------------------------------------------------------------------------------+
    | Enviando e recebendo dados |
    +--------------------------------------------------------------------------------+

    Na programacao de sockets e usada a seguinte funcao para envio de dados:

    int send(
    SOCKET s,
    const char *buf,
    int len,
    int flags
    );

    SOCKET s
    Socket handle que ira enviar os dados.

    const char *buf
    Um buffer de bytes no qual os dados a serem enviados estao guardados.

    len
    Tamanho do buffer.

    flags
    Flags afetam como o envio de dados sera feito, sao elas: MSG_DONTROUTE, e MSG_OOB.

    MSG_DONTROUTE
    Especifica que os dados nao devem ser roteados.

    MSG_OOB
    Especifica o envio de dados urgentes, somente para TCP.

    Se nenhum error ocorre send() retorna o numero de bytes enviados, se houve um error SOCKET_ERROR e retornado.

    -----------------------------------------------------------------------------------

    Para receber dados e usado a funcao:

    int recv(SOCKET s, char* buf, int len, int flags);

    SOCKET s
    Socket handle que ira receber os dados.

    const char *buf
    Um buffer de bytes no qual os dados a serem recebidos seram guardados.

    len
    Tamanho do buffer.

    flags
    Flags afetam como o recebimento de dados sera feito, sao elas: MSG_PEEK, e MSG_OOB.

    MSG_PEEK
    Copia os dados recebidos para o buffer, mas nao os retira do buffer interno, serve pra se observar os dados e nao realmente receber-los.

    MSG_OOB
    Lida com o recebimento de dados urgentes, somente para TCP.

    Se nenhum error ocorre recv() retorna a quantidade de bytes lidos, se a conexao foi fechada recv() retorna o valor 0, ou se um error ocorreu recv() retorna SOCKET_ERROR.

    20)----\
    +--------------------------------------------------------------------------------+
    | Tipos de IO |
    +--------------------------------------------------------------------------------+

    Antes de comecar a falar dos tipos de IO, deiche-me explicar oq e IO.

    IO e o mesmo que INPUT, e OUTPUT, ou em portugues: ENTRADA, e SAIDA, ou tambem siguinifica: LEITURA, e ESCRITA.

    Na programacao de sockets quando estamos fazendo um input estamos recebendo dados, estamos lendo, tambem quando estamos fazendo um output estamos enviando dados, estamos escrevendo.

    Os tipos de IO sao:

    Bloqueado

    No IO bloqueado, as funcoes de IO como send() e recv() sao bloqueadas, oq siguinifica que a funcao nao retorna instantaniamente, mas sim espera que algo interno ocorra para com oq a mesma retorne, exemplo:

    No input com recv() bloqueado, recv() so retorna quando dados sao recebidos, ou quando houver um erro, ou quando a conexao for fechada, ou se um intervalo de tempo passar.

    No output com send() bloqueado, se nehum buffer esta disponivel dentro do sistema de transporte send() e bloqueada.

    Nao-bloqueado

    No IO nao-bloqueado, as funcoes de IO como send() e recv() nao sao bloqueadas, oq siguinifica que as mesmas retornam instantaneamente.

    No input com recv() nao-bloqueado, recv() retorna quantos bytes sao recebidos, ou um erro, ou quando a conexao for fechada, instantaneamente. Tambem se nao houverem dados pendentes que foram recebidos pela interface de sockets, recv retornara SOCKET_ERROR.

    No output com send() nao-bloqueado, send() retorna a quantidade de bytes que o sistema tentara enviar, ou um error.

    Para se setar o tipo de IO, bloqueado ou nao-bloqueado, e necessario usar a seguinte funcao:

    int ioctlsocket(
    SOCKET s,
    long cmd,
    u_long* argp
    );

    SOCKET s,
    Um socket handle a qual se deseja modificar o modo de IO do socket.

    long cmd
    Um comando de modo de IO.

    u_long* argp
    Um ponteiro para um longo, que e o argumento para o cmd


    A funcao ioctlsocket(), modifica o modo do socket, existem diversos comandos, dentre eles o que iremos cobrir sera o de colocar o socket em estado bloqueado ou nao-bloqueado de um socket.

    O comando FIONBIO representa o modo bloqueado ou nao-bloqueado, onde o argp devera apontar para um longo que contenha um valor boolean(verdadeiro ou falso).

    Se argp for 1, o modo do socket e nao-bloqueado, ou se for 0 o modo do socket e bloqueado.

    No exemplo quase a seguir sera demonstrado o uso da ioctlsocket() com FIONBIO.

    Sincronizado

    No IO sincronizado o IO(leitura/escrita) e feita de uma forma sincronizada, como por exemplo:


    recv()
    send()

    Primeiro e recebido dados de um socket, depois e enviado dados, essa forma e sincronizada pois vc nao pode modificar ou controlar a forma com oq o IO acontece uma vez que vc ja a programou. Em outras palavras vc nao pode enviar dados sem que vc nao tenha primeiro recebido dados.

    Um outro exemplo:

    send()
    recv()

    Essa forma tambem e sincronizada, pois vc nao pode receber dados, mas somente depois que vc envia dados.

    Nao-sincronizado

    No IO nao-sincronizado e possivel fazer qualquer operacao IO ao mesmo tempo, o IO nao-sincronizado e implementado com threads de execucao, onde threads podem ficar fazendo input(lendo) onde outros podem ficar fazendo output(escrevendo), e vice-versa onde o processo ocorre "simultaneamente".


    21)----\
    +--------------------------------------------------------------------------------+
    | Cliente de TCP com IO nao-bloqueado e sincronizado, exemplo |
    +--------------------------------------------------------------------------------+

    O exemplo abaixo, recebe um IP, ou um domain-name no seu 2 argumento, lembrando que o primeiro argumento e sempre a path do exe; Depois sera verificado se esse argumento e um possivel dominio, se for, o dominio e resolvido para o seu respectivo IP numerico e o endereco remoto e configurado, senao for, entao temos um IP string, no qual e usado inet_addr() pra se obter o IP numerico e entao configurar o endereco remoto, depois uma conexao a porta 80(HTTP) e estabilizada e entao uma request HTTP simples e enviada ao servidor no qual ira responder enviando a pagina indice, a pagina indice e recebida ao mesmo tempo copiada para dentro de um arguivo em grupo de bytes.

    -----------------------------------------------------------------------------------

    #include <stdio.h>

    /* memset(), system() */
    #include <stdlib.h>

    /* isalpha() */
    #include <ctype.h>

    /* winsock header */
    #include <winsock2.h>

    #define PORTA_REMOTA 80

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

    /* Declaracao de variaveis */

    WORD wVersao = MAKEWORD(2, 2); /* Setamos a versao 2.2 */
    WSADATA wsData; /* Recebe informacoes pela WSAStartup() */

    /* Abaixo temos o socket handle, ou ID */
    SOCKET Cliente;

    /* Endereco remoto */
    struct sockaddr_in EnderecoRemoto;

    /* Um ponteiro que apontara para informacoes sobre um determinado host */
    struct hostent *h = NULL;

    /*
    Essa variavel representa o tipo de IO do socket,
    se for 1 e nao-bloqueado e se for 0 e bloqueado
    */
    long nao_bloqueado = 1;

    /* Declara 2 buffers */
    char SendBuf[1024];
    char ReadBuf[5000];

    /*
    Declara 2 inteiros que representaram o valor de retorno das
    funcoes de IO, send() e recv
    */
    int BytesSent = 0, BytesRecved = 0;

    /* O ip ou hostname que sera passado no primeiro argumento do programa */
    char *IpRemotoOuHost = NULL;

    /* O file pointer para manipulacao de files */
    FILE *fp;

    /* Guardara a quantidade de bytes que foram escritos para um arquivo*/
    int BytesEscritos = 0;

    /* Guardara o ultimo erro retornado */
    int ErrorCode = 0;

    /*
    Se nao tem argumentos suficientes, no minimo 2,
    mostre o uso do programa
    */

    if (argc != 2)
    {
    puts("Uso: [Ip Remoto]");
    return 0;
    }

    /*
    Atribui a IpRemotoOuHost o segundo argumento
    da lista de argumentos que e o possivel IP ou host que queremos */
    IpRemotoOuHost = argv[1];


    /* Deixa o ReadBuf NULL-terminated */
    memset(ReadBuf, 0, 1024);

    /* Inicializacao da winsock 2.2 */
    if(WSAStartup(wVersao, &wsData) != NO_ERROR)
    {

    /* a funcao WSAGetLastError() retorna o numero do error ocorrido */
    printf("Error code #%hd: winsock %hd.%hd nao pode ser inicializado.\n",
    WSAGetLastError(), LOBYTE(wVersao), HIBYTE(wVersao));

    return 0;
    }

    /* Inicializacao do cliente de TCP */
    Cliente = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (Cliente == INVALID_SOCKET)
    {
    printf("Error code #%hd: nao pode obter-se um socket TCP.\n",
    WSAGetLastError());
    return 0;
    }

    /* Preenchemos o EnderecoRemoto com informacao */

    /* A familia do socket */
    EnderecoRemoto.sin_family = AF_INET;
    /* Setamos a porta em formato de rede */
    EnderecoRemoto.sin_port = htons(PORTA_REMOTA);

    /*
    Aqui e verificado se o IpRemotoOuHost contem 1 charactere alpha
    no primeiro byte da string, se conter e pq e um hostname ou domain-name
    se nao conter e pq IpRemotoOuHost e uma Ip string, onde se for um host
    e resolvido o seu IP, para dentro da struct hostent.
    */
    if (isalpha(*IpRemotoOuHost))
    {
    printf("Resolvendo host %s...\n", IpRemotoOuHost);
    /* como e um host obtenha o seu IP numerico apartir do host */
    h = gethostbyname(IpRemotoOuHost);
    /* Se h e NULL entao temos um erro. */
    if(!h)
    {
    printf("Error code #%hd: nao pode obter-se o ip atravez do nome %s.\n",
    WSAGetLastError(), IpRemotoOuHost);
    return 0;
    }

    /*
    Aqui iremos setar o nosso endereco remoto com o ip de dentro
    da estrutura hostent atravez do ponteiro h
    */
    EnderecoRemoto.sin_addr.s_addr = *((unsigned long *)h -> h_addr);
    printf("Host %s resolvido para %s\n", IpRemotoOuHost, inet_ntoa(EnderecoRemoto.sin_addr));

    }
    else
    /*
    Aqui o endereco remoto e setado apartir do IP string
    convertido para IP numerico se uma Ip string e passada
    no argumento
    */
    EnderecoRemoto.sin_addr.s_addr = inet_addr(IpRemotoOuHost);

    /* Use connect() para obter um endereco local para o cliente e se conectar */
    if(connect(Cliente, (struct sockaddr *)&EnderecoRemoto, sizeof(EnderecoRemoto)) ==
    SOCKET_ERROR)
    {
    printf("Error code #%hd: nao pode conectar-se ao ip %s e porta %hu.\n",

    /*
    a funcao inet_ntoa() converte um IP in_addr, para uma string.
    */
    WSAGetLastError(), inet_ntoa(EnderecoRemoto.sin_addr), PORTA_REMOTA);
    return 0;
    }

    printf("Conexao ao servidor %s na porta %hu estabilizada.\n\n", inet_ntoa(EnderecoRemoto.sin_addr),
    PORTA_REMOTA);

    /* Usamos ioctlsocket() para setar o modo do socket para nao_bloqueado */
    ioctlsocket(Cliente, FIONBIO, (unsigned long *)&nao_bloqueado);

    /* Request http para o servidor para pedir a pagina indice */
    strcpy(SendBuf, "GET /index.html HTTP/1.1\r\n"
    "host: localhost\r\n"
    "\r\n"
    "\r\n");

    /* E enviado uma request http para o servidor */
    BytesSent = send(Cliente, SendBuf, strlen(SendBuf), 0);

    /* Pode enviar? */
    if (BytesSent == SOCKET_ERROR)
    {
    printf("Error code #%hd: nao pode enviar a request http.\n", WSAGetLastError());
    return 0;
    }

    /* Cria um arquivo indice.txt */
    fp = fopen("indice.txt", "w");
    if (ferror(fp))
    {
    perror("Nao pode criar indice.txt\n");
    return 0;
    }
    puts("Indice.txt criado.");
    puts("Recebendo dados aguarde...");
    /*
    BytesRecved ira receber o valor de retorno da funcao recv, entao oq ira acontecer, se o retorno for 0, o
    loop nao ira executar, lembrando que o valor de retorno 0, siguinifica que a conexao foi finalizada,
    tambem se o valor de retorno for SOCKET_ERROR ou a quantidade de bytes lida, o loop executa e
    logo apos o erro e manipulado dentro do loop onde se o erro for WSAEWOULDBLOCK que siguinifica
    que "deveriamos" bloquear o socket, e feito com oq o loop continue. Se erro nenhum e retornado
    os bytes sao lidos para dentro do ReadBuf, e copiados para dentro de um arquivo, onde depois da
    trasmissao o servidor "obrigatoriamente" finaliza a conexao e o loop finaliza.
    */
    while(BytesRecved = recv(Cliente, ReadBuf, sizeof(ReadBuf), 0))
    {
    /* se houve um erro */
    if (BytesRecved == SOCKET_ERROR)
    {
    /* Pega o numero do erro */
    ErrorCode = WSAGetLastError();
    /*
    Se o erro for diferente de WSAEWOULDBLOCK que siguinifica que o IO deveria ser
    bloqueado, entao houve um erro fatal, retorne.
    */
    if (ErrorCode != WSAEWOULDBLOCK)
    {
    printf("Fatal error #%d\n", ErrorCode);
    return 0;
    }
    /* Se o error for simplesmente WSAEWOULDBLOCK continue */
    else
    continue;
    }
    else
    {

    if (fwrite(ReadBuf, BytesRecved, 1, fp) != 0)
    BytesEscritos += BytesRecved;
    else
    {
    puts("Nao pode escrever dados recebidos em indice.txt.");
    return 0;
    }
    memset(ReadBuf, 0, sizeof(ReadBuf));
    }
    }
    printf("%d bytes foram escritos dentro de indice.txt.\n", BytesEscritos);
    fclose(fp);

    puts("Conexao ao servidor foi finalizada.\n");

    closesocket(Cliente);

    /* Finalizacao */
    WSACleanup();

    return 0;

    }

    -----------------------------------------------------------------------------------

    Em nenhuma forma o exemplo acima se aproxima de um cliente de http, no qual a complexidade do protocolo e imensa, no entanto o programa acima cumpri com o seu proposito que e mostrar o uso de send(), e recv() nao-bloqueado, e sincronizado.

    22)----\
    +--------------------------------------------------------------------------------+
    | Ligando um endereco local a um socket |
    +--------------------------------------------------------------------------------+

    Como vc pode esta imaginando e possivel ligar um endereco local a um socket, onde essa rotina e geralmente utilizada para servidores, mas e possivel se utilizar para clientes. Nos servidores o endereco local especifica a porta em que o servidor estara aceitando conexoes, e nos clientes o endereco local especifica a porta que sera associada ao processo, a que aparecera no netstat -na.

    Para se ligar um endereco local a um socket e usada a seguinte funcao:

    int bind( SOCKET s, const struct sockaddr* name, int namelen );

    SOCKET s
    O socket handle que deseja associar com um endereco local.
    const struct sockaddr* name
    Um ponteiro para uma estrutura sockaddr no qual tera informacoes sobre o enderelo local.
    int namelen
    O tamanho em bytes da sockaddr.

    A funcao bind retorna NO_ERROR se tudo ocorreu bem, ou retorna SOCKET_ERROR se um problema ocorreu.


    23)----\
    +--------------------------------------------------------------------------------+
    | Ligando um endereco local a um cliente socket, exemplo |
    +--------------------------------------------------------------------------------+

    No exemplo a seguir iremos repetir o nosso cliente de tcp demonstrado anteriormente, onde iremos usar bind para definir um endereco local para o nosso cliente.

    -----------------------------------------------------------------------------------

    #include <stdio.h>

    /* memset(), system() */
    #include <stdlib.h>

    /* isalpha() */
    #include <ctype.h>

    /* winsock header */
    #include <winsock2.h>

    #define PORTA_REMOTA 80

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

    /* Declaracao de variaveis */

    WORD wVersao = MAKEWORD(2, 2); /* Setamos a versao 2.2 */
    WSADATA wsData; /* Recebe informacoes pela WSAStartup() */

    /* Abaixo temos o socket handle, ou ID */
    SOCKET Cliente;

    /* Endereco remoto e local */
    struct sockaddr_in EnderecoRemoto, EnderecoLocal;

    /* Um ponteiro que apontara para informacoes sobre um determinado host */
    struct hostent *h = NULL;

    /*
    Essa variavel representa o tipo de IO do socket,
    se for 1 e nao-bloqueado e se for 0 e bloqueado
    */
    long nao_bloqueado = 1;

    /* Declara 2 buffers */
    char SendBuf[1024];
    char ReadBuf[5000];

    /*
    Declara 2 inteiros que representaram o valor de retorno das
    funcoes de IO, send() e recv
    */
    int BytesSent = 0, BytesRecved = 0;

    /* O ip ou hostname que sera passado no primeiro argumento do programa */
    char *IpRemotoOuHost = NULL;

    /* O file pointer para manipulacao de files */
    FILE *fp;

    /* Guardara a quantidade de bytes que foram escritos para um arquivo*/
    int BytesEscritos = 0;

    /* Guardara o ultimo erro retornado */
    int ErrorCode = 0;

    /*
    Se nao tem argumentos suficientes, no minimo 2,
    mostre o uso do programa
    */

    if (argc != 2)
    {
    puts("Uso: [Ip Remoto]");
    return 0;
    }

    /*
    Atribui a IpRemotoOuHost o segundo argumento
    da lista de argumentos que e o possivel IP ou host que queremos */
    IpRemotoOuHost = argv[1];


    /* Deixa o ReadBuf NULL-terminated */
    memset(ReadBuf, 0, 1024);

    /* Inicializacao da winsock 2.2 */
    if(WSAStartup(wVersao, &wsData) != NO_ERROR)
    {

    /* a funcao WSAGetLastError() retorna o numero do error ocorrido */
    printf("Error code #%hd: winsock %hd.%hd nao pode ser inicializado.\n",
    WSAGetLastError(), LOBYTE(wVersao), HIBYTE(wVersao));

    return 0;
    }

    /* Inicializacao do cliente de TCP */
    Cliente = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (Cliente == INVALID_SOCKET)
    {
    printf("Error code #%hd: nao pode obter-se um socket TCP.\n",
    WSAGetLastError());
    return 0;
    }

    /* Preenchemos o EnderecoRemoto com informacao */

    /* A familia do socket */
    EnderecoRemoto.sin_family = AF_INET;
    /* Setamos a porta em formato de rede */
    EnderecoRemoto.sin_port = htons(PORTA_REMOTA);

    /*
    Aqui e verificado se o IpRemotoOuHost contem 1 charactere alpha
    no primeiro byte da string, se conter e pq e um hostname ou domain-name
    se nao conter e pq IpRemotoOuHost e uma Ip string, onde se for um host
    e resolvido o seu IP, para dentro da struct hostent.
    */
    if (isalpha(*IpRemotoOuHost))
    {
    printf("Resolvendo host %s...\n", IpRemotoOuHost);
    /* como e um host obtenha o seu IP numerico apartir do host */
    h = gethostbyname(IpRemotoOuHost);
    /* Se h e NULL entao temos um erro. */
    if(!h)
    {
    printf("Error code #%hd: nao pode obter-se o ip atravez do nome %s.\n",
    WSAGetLastError(), IpRemotoOuHost);
    return 0;
    }

    /*
    Aqui iremos setar o nosso endereco remoto com o ip de dentro
    da estrutura hostent atravez do ponteiro h
    */
    EnderecoRemoto.sin_addr.s_addr = *((unsigned long *)h -> h_addr);
    printf("Host %s resolvido para %s\n", IpRemotoOuHost, inet_ntoa(EnderecoRemoto.sin_addr));

    }
    else
    /*
    Aqui o endereco remoto e setado apartir do IP string
    convertido para IP numerico se uma Ip string e passada
    no argumento
    */
    EnderecoRemoto.sin_addr.s_addr = inet_addr(IpRemotoOuHost);


    /*
    Aqui iremos especificar o nosso propio endereco local para um cliente, que nao e obrigatorio pq o
    sistema automaticamente em uma chamada a funcao connect() se encarrega de nos disponibilizar um
    endereco local para clientes.
    */

    /* Network internet */
    EnderecoLocal.sin_family = AF_INET;
    /* A porta e 5000, e tem que esta no range 1024 - 5000 */
    EnderecoLocal.sin_port = htons(5000);
    /* INADDR_ANY simboliza que o sistema irar usa um endereco ip numerico apropiado */
    EnderecoLocal.sin_addr.s_addr = htonl(INADDR_ANY);

    /* bind() faz a associacao */
    if (bind(Cliente, (struct sockaddr *)&EnderecoLocal, sizeof(EnderecoLocal)) == SOCKET_ERROR)
    {
    printf("Error code #%hd: nao pode ligar um endereco local a um cliente.\n",
    WSAGetLastError());
    return 0;
    }

    /* Use connect() para obter um endereco local para o cliente e se conectar */
    if(connect(Cliente, (struct sockaddr *)&EnderecoRemoto, sizeof(EnderecoRemoto)) ==
    SOCKET_ERROR)
    {
    printf("Error code #%hd: nao pode conectar-se ao ip %s e porta %hu.\n",

    /*
    a funcao inet_ntoa() converte um IP in_addr, para uma string.
    */
    WSAGetLastError(), inet_ntoa(EnderecoRemoto.sin_addr), PORTA_REMOTA);
    return 0;
    }

    printf("Conexao ao servidor %s na porta %hu estabilizada.\n\n", inet_ntoa(EnderecoRemoto.sin_addr),
    PORTA_REMOTA);

    system("pause");
    /* Usamos ioctlsocket() para setar o modo do socket para nao_bloqueado */
    ioctlsocket(Cliente, FIONBIO, (unsigned long *)&nao_bloqueado);

    /* Request http para o servidor para pedir a pagina indice */
    strcpy(SendBuf, "GET /index.html HTTP/1.1\r\n"
    "host: localhost\r\n"
    "\r\n"
    "\r\n");

    /* E enviado uma request http para o servidor */
    BytesSent = send(Cliente, SendBuf, strlen(SendBuf), 0);

    /* Pode enviar? */
    if (BytesSent == SOCKET_ERROR)
    {
    printf("Error code #%hd: nao pode enviar a request http.\n", WSAGetLastError());
    return 0;
    }

    /* Cria um arquivo indice.html */
    fp = fopen("indice.html", "w");
    if (ferror(fp))
    {
    perror("Nao pode criar indice.html\n");
    return 0;
    }
    puts("Indice.html criado.");
    puts("Recebendo dados aguarde...");
    /*
    BytesRecved ira receber o valor de retorno da funcao recv, entao oq ira acontecer, se o retorno for 0, o
    loop nao ira executar, lembrando que o valor de retorno 0, siguinifica que a conexao foi finalizada,
    tambem se o valor de retorno for SOCKET_ERROR ou a quantidade de bytes lida, o loop executa e
    logo apos o erro e manipulado dentro do loop onde se o erro for WSAEWOULDBLOCK que siguinifica
    que "deveriamos" bloquear o socket, e feito com oq o loop continue. Se erro nenhum e retornado
    os bytes sao lidos para dentro do ReadBuf, e copiados para dentro de um arquivo, onde depois da
    trasmissao o servidor "obrigatoriamente" finaliza a conexao e o loop finaliza.
    */
    while(BytesRecved = recv(Cliente, ReadBuf, sizeof(ReadBuf), 0))
    {
    /* se houve um erro */
    if (BytesRecved == SOCKET_ERROR)
    {
    /* Pega o numero do erro */
    ErrorCode = WSAGetLastError();
    /*
    Se o erro for diferente de WSAEWOULDBLOCK que siguinifica que o IO deveria ser
X
Working...
X