Unconfigured Ad Widget

Collapse

Anúncio

Collapse
No announcement yet.

TCP Estouro da Manada

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

  • Font Size
    #1

    C / C# TCP Estouro da Manada

    Fonte: E.A.D (Elgio A Distância) Sistema desenvolvido em SW Livre



    /*
    TITULO: TCP Concorrencia Estouro da Manada
    DATA: 06/Maio/2008
    */

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdlib.h>

    #define TAM 5000 // Tamanho do buffer
    #define NFILHOS 400 // quantidade de filhos pre criadas

    int main(int argc, char *argv[])
    {
    struct sockaddr_in serv, cli;
    char buf[TAM + 1];
    int listenfd, s1, erro, conexao, i, size, pid;
    int total;
    int PORTA;

    /* Obtem a porta que pode vir por parametro do main. Se nao vier
    solicita que seja digitada
    */
    if (argc < 2) {
    printf("Em qual porta TCP devo escutar? ");
    scanf("%i", &PORTA);
    } else {
    PORTA = atoi(argv[1]);
    }

    /* ZERA todos os bytes da variavel serv */
    memset(&serv, 0, sizeof(serv));

    /* Cria um socket Ipv4 e SOCK_STREAM, ou seja, TCP */
    s1 = socket(PF_INET, SOCK_STREAM, 0);
    if (s1 < 0) {
    printf("Erro na criacao do socket\n");
    return (1);
    }

    /* um socket vai ganhar uma entrada na tabela de descritores de arquivos,
    onde ja existem TRES abertos:

    0: entrada padrao
    1: saida padrao
    2: saida padrao de erro

    Qualquer arquivo aberto com fopen ou open ocupara esta tabela tambem.
    Sockets IGUALMENTE usam posicoes desta tabela (isto facilida pois se pode
    usar as mesmas funcoes de leitura/escrita em arquivo).

    Como nenhum arquivo foi aberto, eh certo que s1 sera 3 (proximo disponivel)
    Imprimindo isto para verificacao:
    */
    printf("s1 ganhou posicao %i na tabela de descritores\n", s1);


    /* Configura a estrutura serv que sera ligada ao socket */
    serv.sin_family = AF_INET; // AF_INET: Ipv4
    serv.sin_port = htons(PORTA); // Numero da porta
    serv.sin_addr.s_addr = htons(INADDR_ANY);

    /* liga o socket s1 com os dados em serv. No caso PEDE a porta especifica
    armazenada em PORTA (anteriormente copiada para serv). Pode nao conseguir
    */
    erro = bind(s1, (struct sockaddr *) &serv, sizeof(serv));
    if (erro < 0) {
    printf("Erro no bind\n");
    return (1);
    }

    /* Servidor seta a porta em modo listen (modo PASSIVO)
    Isto identifica para a pilha que conexoes serao esperadas nesta
    porta e a pilha prepara estrutura de dados para elas
    */
    erro = listen(s1, SOMAXCONN);
    if (erro < 0) {
    printf("Erro Listen\n");
    return (1);
    }
    printf("Servidor operando na porta %d.\n", PORTA);
    size = sizeof(cli);

    /* Pre criar os filhos
    Pai entra em um laco de NFILHOS. Dentro do laco executa um fork.
    Pai continua no laco e filho sai do laco por break indo ficar bloqueado
    no accept.

    Depois de criar todos os filhos, pa tambem sai do laco (por i nao menor
    que NFILHOS e tambem fica no accept. TODOS PRESOS NO ACCEPT ao mesmo
    tempo (ESTOURO DA MANADA)
    */
    for (i = 0; i < NFILHOS; i++) {
    pid = fork();
    if (pid < 0) {
    fprintf(stderr, "ERRO NO FORK\n");
    exit(0);
    }
    if (pid == 0) { // SOU O FILHO!!!
    break; // sai do laco
    }
    }

    /* A logica a seguir eh exatamente igual ao servidor SEM CONCORRENCIA
    (apenas com uns printfs ilustrativos para depuracao)
    */
    for (; {
    total = 0;
    printf("A espera de uma conexao de algum cliente...\n");

    conexao = accept(s1, (struct sockaddr *) &cli, &size);
    /* Servidor executou accept. Fica preso ate que algum cliente se conecte.
    A propria pilha se encarrega de liberar este programa quando houver um
    cliente, devolvendo um socket de conexao (diferente do socket de
    porta). Continuando com a tabela de descritores, eh provavel que o
    primeiro cliente tenha o socket 4. Como eh apenas um cliente por vez,
    todos terao 4
    */

    printf("Filho %i ACORDOU DO ACCEPT\n", getpid());
    if (conexao < 0) {
    printf("%d: Erro no accept\n", conexao);
    return (1);
    }
    /* retornou do accept sem erro. Houve uma conexao
    A PILHA TCP constroi um novo socket, chamado de socket de conexao,
    para cada cliente. Para "conversar" com este cliente basta escrever
    ou ler no socket recebido */


    printf("\n\033[1;31mServidor conectado em um cliente\033[m\n");
    printf("Cliente esta na posicao %i da tabela descritores\n",
    conexao);
    printf("Recebendo caracteres (Maximo de %d)... \n", TAM);
    printf("Irei imprimir o que receber\n\n");
    buf[0] = 0;

    /* Abaixo eh apenas logica de programacao em C.
    Este servidor entra em um laco infinito de CLIENTES (for acima)
    Para cada cliente ele fica conversando ate receber 'F' como primeiro caractere
    de uma frase. Ai volta para pegar outro cliente. Contudo se o cliente enviar
    'Q' como primeiro caractere da frase, ai o servidor ENCERRA A SUA EXECUCAO.
    */
    while ((buf[0] != 'F') && (buf[0] != 'Q')) {
    erro = read(conexao, buf, TAM);
    /* O comando read tambem pode ser usado para ler em arquivos!
    read (0,buf, TAM) leria do teclado por exemplo. Observe que as chamadas
    read e write sao as UNICAS para fazer IO. printf, scanf, etc, sao
    FUNCOES DA LIB STDIO.H e internamente elas usam read e write.

    A estrutura FILE * tambem eh uma abstracao de alto nivel para
    facilitar a operacao. Por ser uma ESTRUTURA (FILE *) um de seus
    campos eh justamente a posicao desta tabela. Veja para o caso de
    stdin (teclado):

    printf("stdin = %i\n", stdin->_fileno);

    Isto devera imprimir 0. Imprimira 1 para stdout e 2 para stderr!

    No caso de sockets eu nao recebo um FILE * como no fopen, mas sim
    um descritor como no open. Contudo eu posso converter um descritor
    em FILE * usando a chamada fdopen e usar fprintf ou fscanf normalmente
    caso me sinta mais a vontade com estas funcoes:

    FILE *cliente;

    ... (apos o accept)
    cliente = fdopen(conexao,"r+w");
    ...
    scanf("%s", cliente);
    */

    if (erro == 0) {
    break;
    } // a chamada read retorna quantidade e bytes lidos

    buf[erro] = 0; // para finalizar string
    printf("%s", buf);
    fflush(stdout);
    }
    /* Saiu do while de conversa com cliente. Pode ter saido por varios motivos:
    a) Recebeu F como primeiro caractere (cliente encerrou)
    b) Recebeu Q como primeiro caractere (cliente ordenou finalizacao do servidor)
    c) Houve um erro no read (se ele retornou ZERO)
    */
    if (!erro) {
    printf("Erro ao tentar receber dados\n");
    } else {
    printf("\033[1;32mCliente finalizou\033[m\n");
    }

    shutdown(conexao, SHUT_RDWR);
    sleep(1);
    close(conexao);

    if (buf[0] == 'Q') {
    break;
    }
    } // fecha o laco INFINITO que sempre faz accept

    /* Fim do laco infinito. O laco infinito soh termina que algum cliente enviou
    'Q' como primeiro caractere
    */
    printf("OK. Missao cumprida :-D Bye!!\n");
    close(s1); /* fechando o socket da porta, ou seja, liberando tudo que
    fora a alocado em termos de socket (liberaria mesmo sem o close,
    pois o SO libera tudo quando um programa encerra) */
    }

  • Font Size
    #2
    Estou vendo que é cada vez mais importante aprender sobre winsock.h e socket.h, valeu cara abraço.

    Comment

    X
    Working...
    X