Programar com Sockets windows e linux

Iniciado por Judeu, 20 de Fevereiro de 2009, 14:34

tópico anterior - próximo tópico

Judeu

Pessoal fiz um chat bem humilde com sockets, que funciona em linux e windows, e com instruções para compilar no dev e no codeblocks.
Estou postando aqui porque é muito difícil achar na net uns exemplos bacanas e comentados.



*Obs: Os comentários explicativos das ações das funções foi baseado em um livro.



------------------------------------------------------------------------------
Servidor: servidor.cpp


/*
Intruções de Compilação:
    DevC++: Tools > Compiler Options > Add linker -lws2_32.
    Codeblocks: Menu Settings > Compiler and Debugger > Linker Settings > Other Linker Options > -lws2_32.
------------------------------------------------------------------------------------------------------------
Dependências:
    windows -> winsock2.h, WSDATA wsa_data.
    unix/linux -> <sys/types.h>, <sys/socket.h>, <arpa/inet.h>.
    comuns -> <iostream>, <unistd.h>, <string.h>
*/
#include <iostream>
#include <unistd.h>
#include <string.h>
#ifdef unix
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#else
#include <winsock2.h>
WSADATA wsa_data;
#endif
#define TAMANHO_MESG 501
int main(){
    //Variável para receber conexão.
    int conexao = -1;
    //Variável para receber e enviar mensagem
    char mensagem[TAMANHO_MESG];
    //Variável para guardar o tamanho da mensagem recebida
    int tam_mesg_receb=0;
    //Declarando estrutura Servidor e Cliente
    struct sockaddr_in servidor;
    struct sockaddr_in cliente;
    #ifndef unix
    //Startando sockets
    WSAStartup(MAKEWORD(2, 0), &wsa_data);
    //Tamanho do estrutura sockaddr_in
    int tamanho  = sizeof(struct sockaddr_in);
    #else
    //Tamanho do estrutura sockaddr_in
    socklen_t tamanho  = sizeof(struct sockaddr_in);
    #endif
    /*
    Criando Socket.
    função socket(int domain,int type,int protocol);
    domain: domínio específico. Ex: AF_INET -> suporta UPD,TCP, etc.
    type: tipo definido. Ex: SOCK_STREAM -> scoket de fluxo.
    protocol: protocolo definido. Ex: IPPROTO_TCP -> suporta TCP, IPPROTO_UDP -> UDP, 0 -> protocolo padrão.
    */
    int meusocket = socket(AF_INET,SOCK_STREAM,0);
    /*
    Definindo endereçamento do Servidor
    sin_family: familia do scoket. Ex: AF_INET -> UDP, TCP, etc.
    sin_addr.s_addr: ip do servidor. Ex: INADDR_ANY -> declara como 0.0.0.0 para funcionar com qualquer ip disponível.
    sin_port: porta de escuta. Ex: htons(80)-> htons transforma a porta 80 em digitos específicos para redes.
    */
    servidor.sin_family = AF_INET;
    servidor.sin_addr.s_addr = htonl(INADDR_ANY);
    servidor.sin_port = htons(8767);
    //Completando a estrutura socktaddr_in com valores nulo para que fique do tamanho igual ao da sockaddr padrão
    memset(&(servidor.sin_zero),'\0', sizeof(servidor.sin_zero));
    /*
    Comunicando o Socket com endereço de rede.
    função bind(int s, const struct sockaddr *name, int namelen);
    s: meusocket ou variável que foi criada recebendo o retorno da função scoket().
    *name: apontador para estrutura padrão do AF_INET(family).
    name_len: tamanho da estrutura.
    */
    bind(meusocket, (struct sockaddr *)&servidor, sizeof(struct sockaddr));
    /*
    Preprando para receber conexões
    função listen(int s, int backlog);
    s: meusocket ou variável que foi criada recebendo o retorno da função scoket().
    backlog: limite máximo de clientes para conectar.
    */
    listen(meusocket,1);
    std::cout << "Aguardando Conexao ... \n";
    //Verificando se há conexões
    while(1){
        /*
        Criando socket a partir de conexões pendentes
        função accept(int s, struct sockaddr *addr, socklen_t *addrlen);
        s: meusocket ou variável que foi criada recebendo o retorno da função scoket().
        *addr: apontador para estrutura padrão do AF_INET (family).
        *addrlen: tamanho da estrutura.
        */
        conexao = accept(meusocket, (struct sockaddr *)&cliente, &tamanho);
        //Avisando sobre a conexão estabelecida com um ip
        std::cout << "Conexão Estabelecida com o ip: " << inet_ntoa(cliente.sin_addr) << "\n";
        //Comunicando com cliente
        do{
            //Solicitando dados de envio e finalizando vetor
            std::cout << "\nDigite uma mensagem ou (0) - para sair:";
            fgets(mensagem, TAMANHO_MESG, stdin);
            mensagem[TAMANHO_MESG] = '\0';
            //verificando se a mensagem vai ser enviada
            if(mensagem[0] != '0'){
                /*
                Transmitindo Pacotes
                função send(int c, const void *msg, size_t len, int flags);
                c: conexão ou variável que foi criada recebendo o retorno da função accept().
                *msg: mensagem a ser enviada.
                len: tamanho da mensagem a ser enviada.
                flag: tipos de comportamento no envio da mensagem. Ex: 0 -> padrão.
                */
                send(conexao, mensagem, TAMANHO_MESG, 0);
                //aguardando resposta
                do{
                    /*
                    Recebendo pacotes
                    função recv(int s, void *buf, size_t len, int flags);
                    s: meusocket ou variável que foi criada recebendo o retorno da função scoket().
                    *buf: mensagem recebida
                    *len: tamanho máximo para receber mensagem.
                    flag: tipos de comportamento no envio da mensagem. Ex: 0 -> padrão.
                    */
                    tam_mesg_receb = recv(conexao, mensagem, TAMANHO_MESG, 0);
                    //verificando se houve mensagem recebida
                    if(tam_mesg_receb > 0){
                        //finalizando mensagem
                        mensagem[tam_mesg_receb] = '\0';
                        //imprimindo mensagem
                        if(mensagem[0] != '0')
                            std::cout << "\nCliente diz: " << mensagem;
                    }//fim if()
                }while(tam_mesg_receb <= 0);//fim do{}
            }//fim if()
        }while(mensagem[0] != '0');//fim do{}
        /*
        Terminando conexão
        função close(int c);
        c: conexão ou variável que foi criada recebendo o retorno da função accept().
        */
        close(conexao);
        std::cout << "\nConexao Finalizada";
    } //fim while()
    /*
    Terminando socket
    função close(int s);
    s: meusocket ou variável que foi criada recebendo o retorno da função scoket().
    */
    close(meusocket);
    //retorno main()
    return 0;
}//fim main()


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


Cliente: cliente.cpp


/*
Intruções de Compilação:
    DevC++: Tools > Compiler Options > Add linker -lws2_32.
    Codeblocks: Settings > Compiler and Debugger > Linker Settings > Other Linker Options > -lws2_32.
------------------------------------------------------------------------------------------------------------
Dependências:
    windows -> winsock2.h, WSDATA wsa_data.
    unix/linux -> <sys/types.h>, <sys/socket.h>, <arpa/inet.h>.
    comuns -> <iostream>, <unistd.h>, <string.h>
*/
#include <iostream>
#include <unistd.h>
#include <string.h>
#ifdef unix
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#else
#include <winsock2.h>
WSADATA wsa_data;
#endif
#define TAMANHO_MESG 501
int main(){
    //Variável para receber conexão.
    int conexao = -1;
    //Variável para receber e enviar mensagem
    char mensagem[TAMANHO_MESG];
    //Variável para guardar o tamanho da mensagem recebida
    int tam_mesg_receb=0;
    //Declarando estrutura Servidor
    struct sockaddr_in servidor;
    #ifndef unix
    //Startando sockets
    WSAStartup(MAKEWORD(2, 0), &wsa_data);
    #endif
    /*
    Criando Socket.
    função socket(int domain,int type,int protocol);
    domain: domínio específico. Ex: AF_INET -> suporta UPD,TCP, etc.
    type: tipo definido. Ex: SOCK_STREAM -> scoket de fluxo.
    protocol: protocolo definido. Ex: IPPROTO_TCP -> suporta TCP, IPPROTO_UDP -> UDP, 0 -> protocolo padrão.
    */
    int meusocket = socket(AF_INET,SOCK_STREAM,0);
    /*
    Definindo endereçamento do Servidor
    sin_family: familia do scoket. Ex: AF_INET -> UDP, TCP, etc.
    sin_addr.s_addr: ip do servidor. Ex: 192.168.1.61 -> Ip do servidor.
    sin_port: porta de escuta. Ex: htons(80)-> htons transforma a porta 80 em digitos específicos para redes.
    */
    servidor.sin_family = AF_INET;
    servidor.sin_addr.s_addr = inet_addr("189.13.133.30");
    servidor.sin_port = htons(8767);
    //Completando a estrutura socktaddr_in com valores nulo para que fique do tamanho igual ao da sockaddr padrão
    memset(&(servidor.sin_zero),'\0', sizeof(servidor.sin_zero));
    /*
    Conectando ao servidor
    função connect(int s, const struct sockaddr *serv_addr, socklen_t addrlen)
    s: meusocket ou variável que foi criada recebendo o retorno da função scoket().
    *serv_addr: apontador para servidor.
    addrlen: tamanho do endereço do servidor
    */
    conexao = connect(meusocket, (struct sockaddr *)&servidor,sizeof(struct sockaddr));
    std::cout << "\nConectando ao servidor: " << inet_ntoa(servidor.sin_addr) << "\n";
    //comunicando com servidor
    do{
        /*
        Recebendo pacotes
        função recv(int s, void *buf, size_t len, int flags);
        s: meusocket ou variável que foi criada recebendo o retorno da função scoket().
        *buf: mensagem recebida
        *len: tamanho máximo para receber mensagem.
        flag: tipos de comportamento no envio da mensagem. Ex: 0 -> padrão.
        */
        tam_mesg_receb = recv(meusocket, mensagem, TAMANHO_MESG, 0);
        //verificando se houve mensagem recebida
        if(tam_mesg_receb > 0 && mensagem[0] != '0'){
            //finalizando mensagem
            mensagem[tam_mesg_receb] = '\0';
            //imprimindo mensagem
            std::cout << "\nServidor diz: " << mensagem;
            //Solicitando dados de envio e finalizando vetor
            std::cout << "\nDigite uma mensagem ou (0) - para sair:";
            fgets(mensagem, TAMANHO_MESG, stdin);
            mensagem[TAMANHO_MESG] = '\0';
            //verificando se a mensagem vai ser enviada
            if(mensagem[0] != '0'){
                /*
                Transmitindo Pacotes
                função send(int c, const void *msg, size_t len, int flags);
                c: conexão ou variável que foi criada recebendo o retorno da função connect().
                *msg: mensagem a ser enviada.
                len: tamanho da mensagem a ser enviada.
                flag: tipos de comportamento no envio da mensagem. Ex: 0 -> padrão.
                */
                send(meusocket, mensagem, TAMANHO_MESG, 0);
                tam_mesg_receb = 0;
            }//fim if()
        }//fim if()
    }while(mensagem[0] != '0' && tam_mesg_receb == 0);//fim do{}
    /*
    Terminando conexão
    função close(int c);
    c: conexão ou variável que foi criada recebendo o retorno da função accept().
    */
    close(conexao);
    std::cout << "\nConexao Finalizada";
    /*
    Terminando socket
    função close(int s);
    s: meusocket ou variável que foi criada recebendo o retorno da função scoket().
    */
    close(meusocket);
    //retorno main()
    return 0;
}//fim main()


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

ushi7781

Cara, muito obrigado pelo post. Ajudou mesmo!

Gostaria de lhe fazer uma pergunta: "Uma aplicação que deseje ser portável pode funcionar com estes sockets? Quero dizer posso criar uma aplicação Cliente/Servidor com um Cliente em um SO e o Servidor em outro?"

Obrigado.