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) */
}
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) */
}
Comment