Hi,
Um bom começo para quem está começando a programar sockets é desenvolver um programa de chat cliente-servidor.Algo que reparamos é que, enquanto um computador envia uma mensagem, o outro está aguardando por esta, só depois de recebê-la terá sua vez de enviar uma mensagem também. Isso ocorre devido ao fato de que uma instrução é executada por vez. É possível, entretanto, executar duas ou mais funções simultaneamente, conseqüentemente podemos executar várias instruções ao mesmo tempo. O programa primeiramente define o socket como non-blocking - permite que o socket possa executar várias tarefas ao mesmo tempo - porém não é suficiente para que possamos ler e enviar dados ao mesmo tempo no programa. Para esta última função, utilizaremos Threads - que são subprocessos de um processo maior e que dividem tarefas, permitindo que estas sejam executadas simultaneamente.
O código abaixo ilustra o funcionamento:
chat.cpp#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <windows.h> // Dados e funções da API do Windows
#include <winsock.h> // Biblioteca do winsock
#define ERRO -1 // Define de erro
#define SERVER_PORTA 2222 // Porta utilizada no chat
using namespace std;
WSADATA data;
SOCKET sock,newsock; // Sockets
struct sockaddr_in sin,newsin; // SIN ADDR's
static int sin_size = sizeof(sockaddr_in); // Tamanho da estrutura sockaddr_in
struct hostent * host; // Estrutura hostent
u_long nonblocking = 1; // Non-Blocking mode
HANDLE thread1,thread2; // Handles para os threads
DWORD id1,id2; //ID's
char buffer[0x200],entrada[0x200]; // Buffers para enviar e receber dados
void erro(const char*),servidor(void),cliente(void),threads(void); // Funções em geral
DWORD _stdcall enviar(LPVOID); // Função do thread1
DWORD _stdcall receber(LPVOID); // Função do thread2
int main()
{
init:
system("cls");
cout << "################################################" << endl;
cout << "### Exemplo - Chat estilo Non-Blocking ###" << endl;
cout << "### by Dark Side ###" << endl;
cout << "################################################" << endl;
char modo;
cout << "Modos:\n(1) Servidor\n(2) Cliente\nEscolha um modo:";
modo = cin.get(); // Obtém a opção
switch(modo) //Verifica-a
{
case '1':
servidor(); // Modo servidor -> opção 1
break;
case '2':
cliente(); // Modo cliente -> opção 2
break;
default:
goto init; // Nenhuma das opções acima -> retorna ao início
break;
}
return 0;
}
void erro(const char * erro) // Função que exibe erros
{
cout << erro;
exit(1);
}
void servidor()
{
if(WSAStartup(MAKEWORD(1,1),&data)==ERRO) //Tenta inicializar o winsock
erro("\n>> Erro ao inicializar winsock.");
if((sock = socket(AF_INET,SOCK_STREAM,0))==ERRO) //Tenta criar o socket
erro("\n>> Erro ao criar socket.");
sin.sin_family=AF_INET; // Família do socket
sin.sin_port = htons(SERVER_PORTA); // Porta
// Tenta configurar o socket na porta local
if((bind(sock,(struct sockaddr*)&sin,sizeof(sin)))==ERRO)
erro("\n>> Erro ao configurar socket.");
if(listen(sock,1) == ERRO) // Tenta colocar o socket em modo listen
erro("\n>> Erro ao colocar socket no modo listen.");
// Define o socket como Non-Blocking => O socket pode realizar diversas tarefas ao mesmo tempo
ioctlsocket(sock,FIONBIO,&nonblocking);
cout << "\nAguardando conexao...\n";
// Enquanto não houver conexão com cliente
while((newsock = accept(sock,(struct sockaddr*)&newsin,&sin_size))==ERRO)
Sleep(10); // Aguarda
// Mostra o IP do cliente conectado, quando há uma conexão -> fim do loop
cout << "Cliente conectado: " << inet_ntoa(newsin.sin_addr) << endl;
threads(); // Chama função responsável por criar os threads
}
void cliente()
{
if(WSAStartup(MAKEWORD(1,1),&data)==ERRO) //Tenta inicializar o winsock
erro("\n>> Erro ao inicializar winsock.");
if((sock = socket(AF_INET,SOCK_STREAM,0))==ERRO) //Tenta criar o socket
erro("\n>> Erro ao criar socket.");
sin.sin_family=AF_INET; // Família do socket
sin.sin_port = htons(SERVER_PORTA); // Porta
// Solicita o host do servidor
char nome[0x32];
cin.get();
cout << "Servidor: ";
cin.getline(nome,0x32); // Obtém host do servidor digitdo
host = gethostbyname(nome); // Tenta obter o IP do servidor
if(host == NULL)
erro("\n>> Erro ao obter o host.");
memcpy(&sin.sin_addr,host->h_addr_list[0],host->h_length); // Copia o IP para a estrutura sockaddr_in
newsock = sock; // Ativa um novo socket;
// Tenta conectar-se
if(connect(newsock,(struct sockaddr*)&sin,sizeof(sin))==ERRO)
erro("\n>> Erro ao se conectar no host.");
cout << "Conectado com o servidor" << endl;
ioctlsocket(sock,FIONBIO,&nonblocking); // Define modo Non-Blocking
threads(); // Cria Threads
}
void threads(){
thread1 = CreateThread(0,0,enviar,NULL,0,&id1); // Cria thread para enviar dados
thread2 = CreateThread(0,0,receber,NULL,0,&id2); // Cria thread para receber dados
WaitForSingleObject(thread1,INFINITE); // Aguarda o encerramento da função do thread1
WaitForSingleObject(thread2,INFINITE); // Aguarda o encerramento da função do thread2
// Fecha os handles
CloseHandle(thread1);
CloseHandle(thread2);
// Encerra
cout << "\n>> A conexao foi finalizada.";
closesocket(newsock); // Fecha o socket
WSACleanup(); // Finaliza Winsock
}
DWORD _stdcall enviar(LPVOID p){
while(WSAGetLastError() != WSAECONNRESET) // Enquanto estiver conectado.
{ Sleep(10);
ZeroMemory(entrada,0x200); // Zera buffer
cin.getline(entrada,0x200); // Obtém dados digitados
send(newsock,entrada,strlen(entrada),0); // Envia-os
send(newsock,"\n",1,0);
}
return 0;
}
DWORD _stdcall receber(LPVOID p){
while(WSAGetLastError() != WSAECONNRESET) // Enquanto estiver conectado.
{
Sleep(10);
ZeroMemory(buffer,0x200); // Zera buffer
recv(newsock,buffer,0x200,0); // Obtém dados recebidos
if(strlen(buffer) > 0) // Se tiverem pelo menos 1 byte
cout << endl << buffer; // Mostra-os na tela.
}
return 0;
}
Obs:: O objetivo maior do programa é ilustrar o funcionamento da técnica, não dando muita prioridade à parte do chat em si.
Bye...