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 :).
Conteúdos que serão abordados:
1) Introdução.
2) Preparando e criando Sockets.
3) Sockets - Criando um programa Servidor.
4) Sockets - Criando um programa Cliente.
5) Enviando dados e Recebendo dados.
6) Finalizando uso do socket.
7) Código de exemplo - simples chat.
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:
Explicando o código:Código:#include <stdio> #include <winsock> #include <conio> 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; }
#include <winsock>
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:
O que vemos de novidade no código?Código:#include <stdio> #include <conio> #include <winsock> #include <windows> //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; }
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).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; }
listen(winsock,1) -> Coloca o socket em modo de espera, aceitando 1 conexão.
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.Código:while((winsock = accept(winsock,0,0))==SOCKET_ERROR) { Sleep(1); } printf("Cliente conectado!"); getch(); closesocket(winsock); WSACleanup(); return 0; }
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:
Analisando:Código:#include <stdio> #include <winsock> #include <conio> 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; }
Esta função é uma das mais utilizadas na programação em SOCKET's. :)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().
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:
Analisando a funcão temos:Código:#include <string> char buffer[1024]; send(winsock,buffer,strlen(buffer),0)
send(SOCKET,BUFFER,TAMANHO,0);
SOCKET -> nosso socket que foi criado;
BUFFER -> dados a enviar;
TAMANHO -> tamanho dos dados;
Recebendo dados:
Analisando a funcão temos:Código:#include <string> char buffer[1024]; int byes; while(1){ memset(buffer,0,1024); bytes = recv(winsock,buffer,strlen(buffer),0) if(bytes==-1) return 0; }
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 de exemplo - simples chatCódigo:HEADERS [...] int main(){ [...] desenvolvimento do programa [...] closesocket(winsock); WSACleanup() return 0; }
CLIENTE.C
SERVIDOR.CCódigo:#include <stdio> #include <winsock> #include <conio> #include <windows> #include <string> 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; }
Considerações finaisCódigo:#include <stdio> #include <conio> #include <winsock> #include <windows> //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; }
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.



Responder com Citação
vo la no win pra testar.
