Pular para o conteúdo

BSD Sockets em linguagem C

Venho neste artigo explicar como funciona sockets em ANSi C, explicar portabilidade e exemplos reais e diferentes de artigos semelhantes. Enfim, aqui você aprenderá a usar sockets na prática.
C00L3R_ Cooler_
Hits: 123.631 Categoria: C/C++ Subcategoria: Rede
  • Indicar
  • Impressora
  • Denunciar

Parte 7: Servidor e cliente com fork

Continuando nossa odisseia, já enfrentamos um ciclope entendendo STREAMS, ou seja, comunicação TCP ficou faltando RAW_SOCK, famoso raw sockets, DGRAM - UDP. Mesmo tendo 3 exemplos eu quero passar mais alguns problemas, bora fazer um servidor tipo QuestBook, que é um cliente que entra, deixa uma mensagem e se o cliente quiser sair ele digita "exit".

O nosso servidor vai poder ter no máximo 5 conexões ao mesmo tempo, para tal feito vamos usar fork, veja:

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "unistd.h"
#include "errno.h"
#include "sys/socket.h"
#include "sys/types.h"
#include "arpa/inet.h"
#include "netdb.h"
#include "netinet/in.h"
#include "sys/wait.h"

#define PORTA 6666
#define ERRO -1
#define TAMMAX 250     //tamanho maximo da string

int main () {

struct sockaddr_in network,local;
int sock,
    newSock,
    resp,
    strucsize,pros;

char msgbuffer [TAMMAX];

if(fork() == 0){
  sock = socket (AF_INET, SOCK_STREAM, 0);
  if (sock == ERRO) {
   perror ("Socket");
   exit (0);
  }
  bzero ((char *)&local, sizeof (local));
  local.sin_family = AF_INET;
  local.sin_port = htons (PORTA);
  local.sin_addr.s_addr = INADDR_ANY;
  strucsize = sizeof (local);
  resp = bind (sock, (struct sockaddr *)&local, strucsize);

  if (resp == ERRO) {
   perror ("Bind");
   exit (0);
  }
//numero maximo de conexões agente definiu aqui
  listen (sock, 5);
for(;;) {        
  if((newSock = accept (sock, (struct sockaddr *)&network, &strucsize))==1) {
    perror("accept");
    exit(1);
  }

  if (newSock == ERRO) {
   perror ("Accept");
   exit (0);
  }
  if(!fork()) {
  printf ("Recebendo conexao de: %s\n", inet_ntoa (network.sin_addr));
  //permite o cliente da uma entrada e a mostra, se a entrada for exit bula o laço "for" infinito
   for (;;) {
    recv (newSock, msgbuffer, TAMMAX, 0);
    fprintf (stdout, "\nMensagem Recebida: %s\n", msgbuffer);
    if (!strcmp (msgbuffer, "exit")) break;
   }
  }
}
}
}

Explicando:

Função recv - usada para receber mensagens de um socket. Sua sintaxe é:

int recv(int Meusocket, void *buf, int len, unsigned int flags);

Onde:
  • Meusocket -> é o socket para ler de outro, no caso um socket local.
  • buf -> aqui é o endereço da área do buffer.
  • len -> é o tamanho do buffer.
  • flags -> são formados por MSG_OOB e MSG_PEEK, permitindo receber dados out-of-band e permitindo espiar dados que entram, consequentemente. Esta chamada deve ser usada somente com sockets do tipo SOCK_STREAM.
  • MSG_OOB -> para dados oob (out-of-band).
  • MSG_WAITALL -> este argumento faz com que seja bloqueada a chegada de dados até que a requisição seja satisfeita. Flags podem ser: MSG_OOB, MSG_PEEK, MSG_WAITALL, MSG_ERRQUEUE, MSG_NOSIGNAL.

Agora o cliente para nosso servidor, papel dele é simples, ele pega a entrada do usuário, manda para o servidor e continua na conexão, enquanto não digitar "exit".

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "errno.h"
#include "sys/socket.h"
#include "sys/types.h"
#include "arpa/inet.h"
#include "netdb.h"
#include "netinet/in.h"
#include "sys/wait.h"

#define PORTA 6666
#define ERRO -1
#define TAMMAX 250 //tamanho maximo da string

main (int argc, char * * argv) {

struct sockaddr_in network;

int sock,
    newSock,
    resp,
    strucsize;

char msg [TAMMAX];

if (argc < 2) {
  printf ("Use %s <host>\n\n", argv [0]);
  exit (0);
  }

sock = socket (AF_INET, SOCK_STREAM, 0);

if (sock == ERRO) {
  perror ("Socket");
  exit (0);
  }

bzero ((char *)&network, sizeof (network));
network.sin_family = AF_INET;
network.sin_port = htons (PORTA);
network.sin_addr.s_addr = inet_addr (argv [1]);

strucsize = sizeof (network);

resp = connect (sock, (struct sockaddr *)&network, strucsize);

if (resp == ERRO) {
  perror ("Connect");
  exit (0);
  }

fprintf (stdout, "Conectado em %s\n", argv [1]);

for (;;) {
  printf ("\nMensagem: ");
   fgets(msg, sizeof(msg), stdin);
   msg[strlen(msg)-1] = '\0';
  send (sock, msg, sizeof (msg), 0);
  if (!strcmp (msg, "exit"))
     {
     exit (0);
     }
   }
}

Rode o servidor num terminal e abra dois terminais. Rode:

./client ip_do_server

Mande mensagem pelos os dois e veja por si o servidor rolando no terminal em que você executou ele. As dúvidas geralmente vão com a prática, então não fique triste se não rolar, apenas não desista. Se ajudar, baixe uma música aí do Raul Seixas chamada "tente outra vez", sempre que estou desanimado escuto esta música, abro o GDB e depuro o código, leio um bom livro como Ansi C do K&R, Dragon Books, entre outros aí, entrar no IRC e conversar com alguém que tem mais experiência também ajuda.

   1. Introdução
   2. Explanação ao TCP/IP
   3. O que é socket
   4. Funções read(), send() e exemplo cliente HTTP
   5. Funções listen(), bind(), accept() e exemplo de servidor HTTP
   6. Na prática fazendo um FUZZER TCP
   7. Servidor e cliente com fork
   8. Servidor de comandos e scanner de portas
   9. Simple socket library
   10. Explanação ao UDP e exemplo de servidor e cliente
   11. Exemplo UDP Flood
   12. Portabilidade
   13. Conclusão

Banco de dados orientados a documentos

Bind: Explorando e evitando falhas

Buffer Overflow: Entendendo e explorando

Ponteiros - Saindo de Pesadelos

Módulos de Web no Perl

Tutorial - Aplicação em C para transferência de arquivo usando socket TCP e Thread

Controlando UPLOAD com o CBQ

O Modelo de Referência OSI

Monitorando o consumo de banda com Bwbar

Manipulação de arquivos em C++

#1 Comentário enviado por VonNaturAustreVe em 06/07/2010 - 03:24h
Excelente cara vou ler tudo :)
#2 Comentário enviado por removido em 06/07/2010 - 05:29h
Hey C00L3R,
Parabéns, é um ótimo artigo. Minhas dúvidas surgiram após meus testes.
Um abraço.
#3 Comentário enviado por andrezc em 06/07/2010 - 08:12h
Cara, realmente esse é um dos melhores artigos que eu já li por aqui. Parabéns.
#4 Comentário enviado por werneral em 06/07/2010 - 11:18h
Muito bom! Obrigado!
#5 Comentário enviado por uberalles em 06/07/2010 - 11:43h
verdadeira aula, velho. parabéns!
muito bom "resumão" do Unix Network programming. Nunca consegui fazer nada decente em sockets e esta tua aula deverá me ajudar muito.
#7 Comentário enviado por andrezc em 07/07/2010 - 15:43h
Opa stremer, eu cheguei a ver este seu script, realmente fabuloso.

Um abraço.
#8 Comentário enviado por fernandopinheiro em 07/07/2010 - 20:23h
Parabens, muito bom!!
#9 Comentário enviado por brunosolar em 08/07/2010 - 09:48h
Parabens realmente muito bom. So queria fazer um comentario sobra a parte do UDP flood. Sim hoje em dia qualquer firewal simples pode recusar este tipo de pacote.

No entanto dependendo da quantidade de pacotes UDP enviados (leia-se DDOS) você poderá ser "derrubado" pois o firewall irá gastar muito processador para descatar todos os pacotes. A melhor solução (AINDA) para ataques DOS / DDOS é diretamente no ISP onde eles irão setar no roteador o IP do atacante para /dev/null (exemplo). claro que voce corre o risco de perder algum cliente que faça parte da rede redirecionada.

No mais excelente trabalho.
#10 Comentário enviado por shazaum em 24/09/2010 - 11:44h
opa, no fuzzer faltou uma lib...

#include <netinet/in.h>
#11 Comentário enviado por thomasawrd em 18/07/2014 - 12:41h
parabéns cara excelente artigo,me ajudou muito.

Contribuir com comentário

Entre na sua conta para comentar.