Cliente-Servidor
Vivemos em mundo cliente-servidor. Quase tudo relacionado
a rede lida com processos no cliente falando com processos no
servidor e vice-versa. Pegue o telnet por exemplo. Quando
você conecta na porta 23 com o telnet(cliente), um
programa no outro host (chamado telnetd, o servidor) aparece.
Ele gerencia a conexão, mostra um prompt de login,
etc.
O par cliente-servidor pode se comunicar através de
"SOCK_STREAM","SOCK_DGRAM", ou qualquer outra coisa (contanto
que estejam falando a mesma coisa).
Alguns exemplos de pares cliente-servidor são:
telnet/telned, ftp/ftpd ou bootp/bootpd. Toda vez que
você usa o FTP, existe um programa remoto, ftpd, que
atende suas conexões.
Geralmente haverá apenas um servidor na máquina,
e este servidor vai controlar múltiplos clientes usando
fork(). A rotina básica é: o servidor espera a
conexão, aceita (accept()) , e então cria um
processo filho para gerenciar a conexão (através de
fork()). Isto é o que nosso servidor faz na próxima
seção.
Um Simples Servidor Stream
Tudo o que este servidor faz é enviar a string
"Olá , Beleza!\n" através de uma conexão
stream. Tudo o que você precisa para testar este
servidor e rodá-lo em uma janela, e usar o telnet em
outra.
// a porta na qual clientes
estarão se conectando
// quantas conexões
pendentes serão permitidas
void sigchld_handler(int s)
{
while(wait(NULL) > 0);
}
int main(void)
{
int sockfd, nova_fd; // escutar em fd, novas
conexões em new_fd
struct sockaddr_in meu_end; // minha
informação de endereço
struct sockaddr_in outro_end; // informação
de endereço externo
int sin_size;
struct sigaction sa;
int yes=1;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
exit(1);
}
if
(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) ==
-1) {
perror("setsockopt");
exit(1);
}
meu_end.sin_family = AF_INET; // host byte
order
meu_end.sin_port = htons(3490 ); // converte para
short, network byte order
meu_end.sin_addr.s_addr = INADDR_ANY; //
automaticamente preenche com meu endereço IP
bzero(&(meu_end.sin_zero), 8); // zera o
resto da estrutura
if (bind(sockfd, (struct sockaddr *)&meu_end,
sizeof(struct sockaddr))
== -1) {
perror("bind");
exit(1);
}
if (listen(sockfd, 10 ) == -1) {
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // retira todos os
processos ociosos
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}
while(1) { // loop principal accept()
sin_size = sizeof(struct sockaddr_in);
if ((nova_fd = accept(sockfd, (struct sockaddr
*)&outro_end,
&sin_size)) == -1) {
perror("accept");
continue;
}
printf("servidor: recebeu conexão de
%s\n",
inet_ntoa(outro_end.sin_addr));
if (!fork()) { // este é o processo-filho
close(sockfd); // o filho não precisa
de escutar conexões
if (send(nova_fd, "Olá , Beleza!\n", 14,
0) == -1)
perror("send");
close(nova_fd);
exit(0);
}
close(nova_fd); // o processo principal não
precisa disso
}
return 0;
}