by Stiod Desenvolvimento Web
Assine nosso RSS

Sockets em C (WinSock)

Yoshio
No Gravatar

Um socket é um mecanismo de comunicação que permite que dois ou mais aplicativos troquem informações, seja no mesmo computador ou em computadores distintos, como os programas de chats, games on-line, navegadores, etc. No geral, programas que utilizam Network Communication ou Interprocess Communication utilizam sockets.

Sockets são importantes, por isso eu tinha que tomar vergonha na cara e aprender a usá-los, comecei a ler sobre o assunto e consegui fazer uma pequena aplicação que envia mensagens para outra.

Primeiramente vou mostrar o código e depois ir (tentando) descrever as funções. Não vou me aprofundar, é só para ter uma idéia de como a coisa funciona, mesmo porque eu acho que aprofundei demais na explicação (mas quanto mais se sabe, mas se sabe que não se sabe nada XD).

O código foi feito para Windows, mas a implementação no Linux é bem parecida. No Windows você deve usar a WinSock API para trabalhar com sockets, não se esqueça de linkar a lib na hora de compilar. Eu uso o Dev-C++ e a lib é a libwsock32.a.

Criando o servidor que recebe mensagens

[c]/*
SERVIDOR
*/

#include
#include
#include
#include

#define BACKLOG_MAX 5
#define BUFFER_SIZE 128
#define EXIT_CALL_STRING "#quit"

int local_socket = 0;
int remote_socket = 0;

int remote_length = 0;
int message_length = 0;

unsigned short local_port = 0;
unsigned short remote_port = 0;

char message[BUFFER_SIZE];

struct sockaddr_in local_address;
struct sockaddr_in remote_address;

WSADATA wsa_data;

/* Exibe uma mensagem de erro e termina o programa */
void msg_err_exit(char *msg)
{
fprintf(stderr, msg);
system("PAUSE");
exit(EXIT_FAILURE);
}

int main(int argc, char **argv)
{
// inicia o Winsock 2.0 (DLL), Only for Windows
if (WSAStartup(MAKEWORD(2, 0), &wsa_data) != 0)
msg_err_exit("WSAStartup() failed\n");

// criando o socket local para o servidor
local_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (local_socket == INVALID_SOCKET)
{
WSACleanup();
msg_err_exit("socket() failed\n");
}

printf("Porta local: ");
scanf("%d", &local_port);
fflush(stdin);

// zera a estrutura local_address
memset(&local_address, 0, sizeof(local_address));

// internet address family
local_address.sin_family = AF_INET;

// porta local
local_address.sin_port = htons(local_port);

// endereco
local_address.sin_addr.s_addr = htonl(INADDR_ANY); // inet_addr("127.0.0.1")

// interligando o socket com o endereço (local)
if (bind(local_socket, (struct sockaddr *) &local_address, sizeof(local_address)) == SOCKET_ERROR)
{
WSACleanup();
closesocket(local_socket);
msg_err_exit("bind() failed\n");
}

// coloca o socket para escutar as conexoes
if (listen(local_socket, BACKLOG_MAX) == SOCKET_ERROR)
{
WSACleanup();
closesocket(local_socket);
msg_err_exit("listen() failed\n");
}

remote_length = sizeof(remote_address);

printf("aguardando alguma conexao...\n");
remote_socket = accept(local_socket, (struct sockaddr *) &remote_address, &remote_length);
if(remote_socket == INVALID_SOCKET)
{
WSACleanup();
closesocket(local_socket);
msg_err_exit("accept() failed\n");
}

printf("conexao estabelecida com %s\n", inet_ntoa(remote_address.sin_addr));
printf("aguardando mensagens...\n");
do
{
// limpa o buffer
memset(&message, 0, BUFFER_SIZE);

// recebe a mensagem do cliente
message_length = recv(remote_socket, message, BUFFER_SIZE, 0);
if(message_length == SOCKET_ERROR)
msg_err_exit("recv() failed\n");

// exibe a mensagem na tela
printf("%s: %s\n", inet_ntoa(remote_address.sin_addr), message);
}
while(strcmp(message, EXIT_CALL_STRING)); // sai quando receber um "#quit" do cliente

printf("encerrando\n");
WSACleanup();
closesocket(local_socket);
closesocket(remote_socket);

system("PAUSE");
return 0;
}[/c]

WSAStartup()

A função WSAStartup() inicia o Windows Sockets Dynamic Link (WinSock DLL). Ela também é usada para confirma a versão do WinSock DLL. Estamos utilizando a 2.0.

Declaração:
[c]int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);[/c]

O parâmetro wVersionRequired é número da maior versão que a aplicação pode usar. Repare que ele é uma WORD onde o byte de maior ordem especifica a número da minor version e o byte de ordem menor indica a major version. Por isso eu utilizo a função MAKEWORD para retornar uma WORD (e facilitar minha vida), por exemplo, se quiser usar a versão do WinSock 1.1 é só alterar para MAKEWORD(1, 1). Fácil demais...

O parâmetro lpWSAData é um ponteiro para um estrutura WSADATA que receberá os detalhes da implementação do WinSock.

Se der tudo certo a função retornará 0. Se ocorrer um erro você pode usar a função WSAGetLastError() para saber mais detalhes.

socket()

Use a função socket() parar criar o socket \o/.

Declaração:
[c]SOCKET WSAAPI socket(int af, int type, int protocol);[/c]

O parâmetro af especifica o "address family" que este socket usa. Nós iremos usar o AF_INET. A versão 1.1 do WinSock só suporta o formato AF_INET.

Alguns formatos possíveis:
AF_INET: Arpa Internet Protocols
AF_UNIX: Unix Internet Protocols
AF_ISSO: Iso Protocols
AF_NS: Xerox Network System Protocols

Se você olhar no header (winsock.h) vai encontrar outros como o AF_FIREFOX, mas é melhor ver quais realmente podem ser usados no MSDN.

O parâmetro type especifica o tipo do socket, no nosso caso é SOCK_STREAM (utilizado na maioria dos programas), mas você pode usar o SOCK_DGRAM (UDP), mas não vou abordar as diferenças entre eles.

Em protocol deixe 0 (zero), isto indica que o socket irá usar o valor padrão. O protocol pode ser o padrão porque a combinação do AF_INET+SOCK_STREAM já indica que o protocolo é TCP. Eu coloquei IPPROTO_TCP no código, mas não precisa, é só deixar como 0.

Alguns valores possíveis para o protocol:
IPPROTO_IP: Internet Protocol (0)
IPPROTO_ICMP: Internet Control Message Protocol (1)
IPPROTO_IGMP: Internet Group Multicast Protocol (2)
IPPROTO_GGP: Gateway-Gateway Protocol (3)
IPPROTO_TCP: Transmission Control Protocol (6)
IPPROTO_UDP: User Datagrama Protocol (17)

Se der tudo certo a função irá retornar o socket descriptor, que é um número que identifica o socket criado. Se falhar irá retorna INVALID_SOCKET, você pode usar a função WSAGetLastError() para saber mais detalhes do erro.

bind()

Agora precisamos dar nomes aos bois, precisamos dizer que o nosso socket se chama "192.168.0.1", por exemplo. Usaremos a função bind() para isso.

Declaração:
[c]int bind(SOCKET s, const struct sockaddr *addr, int namelen);[/c]

O parâmetro s é o socket descriptor retornado pela função socket(). O parâmetro addr é um ponteiro para uma estrutura do tipo sockaddr. O namelen é o tamanho em bytes da estrutura, use a função sizeof() para isto.

Esta parte pode confundir, antes de usar a função bind() nós precisamos conhecer as estruturas sockaddr, sockaddr_in e in_addr, mas a que iremos utilizar é a sockaddr_in.

Definições da estrutura sockaddr:
[c]struct sockaddr
{
u_short sa_family; // address family
char sa_data[14]; // address
};[/c]

O item sa_data da estrutura vai depende do "address family". No WinSock 1.1, como eu disse, apenas o AF_INET é suportado, logo somente um "endereçamento de internet" é suportado no sa_data. Lembrando que você não vai utilizar esta estrutura, mas eu coloquei porque ela aparece em um "cast" de um dos parâmetros da função bind() que estamos utilizando. Você deve utilizar uma outra estrutura no lugar da sockaddr quando chamar a função bind(), use a sockaddr_in.

Definições da estrutura sockaddr_in:
[c]struct sockaddr_in
{
short sin_family; // address family
u_short sin_port; // port
struct in_addr sin_addr; // internet address
char sin_zero[8]; // to make a beautiful cast
};[/c]

Primeiro você deve zerar a estrutura e depois preencher alguns itens. No código que eu fiz está assim:
[c]struct sockaddr_in local_address;
/* ... */
memset(&local_address, 0, sizeof(local_address)); // zera a estrutura

local_address.sin_family = AF_INET;
local_address.sin_port = htons(local_port);
local_address.sin_addr.s_addr = htonl(INADDR_ANY);[/c]

Importante!
Nem todos os computadores armazenam os dados na mesma ordem na memória. Existem computadores que trabalham no formato "Big Endian", onde byte menos significativo fica no endereço de memória de maior valor. Por exemplo, para armazenar o número 0x01234567 ficaria assim:
endereço 0x101: 01
endereço 0x102: 23
endereço 0x103: 45
endereço 0x104: 67

O maior endereço é o 0x104 e o byte menos significativo de 0x01234567 é o último da direita (67).

Já nos computadores "Little Endian", ocorre o inverso, o byte menos significativo fica no endereço de memória de menor valor:
endereço 0x101: 67
endereço 0x102: 45
endereço 0x103: 23
endereço 0x104: 01

O menor endereço é o 0x101 e o byte menos significativo de 0x01234567 é o último da direita (67).

Os computadores com processadores baseados no Intel x86 são "Little Endian", mas a ordem dos bytes na rede (network) é "Big Endian". Precisamos converter os dados antes de enviá-los usando as funções htons() e htonl():
htons(): converte um unsigned short (host-to-network)
htonl(): converte um unsigned long (host-to-network)

Existem outras funções para esta converter os dados, mas nós iremos utilizar apenas esses.

Voltando a estrutura sockaddr_in, vamos falar do item sin_addr. Veja na declaração que este item é uma estrutura do tipo in_ddr.

Definições da estrutura in_addr:
[c]struct in_addr
{
union
{
struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { u_short s_w1,s_w2; } S_un_w;
u_long S_addr;
} S_un;
#define s_addr S_un.S_addr
#define s_host S_un.S_un_b.s_b2
#define s_net S_un.S_un_b.s_b1
#define s_imp S_un.S_un_w.s_w2
#define s_impno S_un.S_un_b.s_b4
#define s_lh S_un.S_un_b.s_b3
};[/c]

Repare nos defines que ela possui, você pode acessar o dados da estrutura através deles. Quando você acessa local_address.sin_addr.s_addr na verdade esta acessando local_address.sin_addr.S_un.S_addr. Mas sempre use os defines para manter compatibilidade, não acesse os dados diretamente.

O valor indicado para local_address.sin_addr.s_addr é htonl(INADDR_ANY), isto indica que usaremos todos os endereços locais designados ao nosso servidor. Por exemplo, se sua máquina possui duas placas de rede (192.168.0.1 e 192.168.0.2), você pode usar os dois endereços para o seu socket. Mas se você quiser especificar apenas uma placa, use a função inet_addr(). Nós usaremos esta função para fazer o programa cliente.

Sobre o item sin_zero da estrutura sockaddr_in, ele server apenas para ocupar espaço, de modo que a estrutura tenha o mesmo tamanho (em bytes) da estrutura sockaddr. Assim podemos fazer "casts" de uma estrutura para outra (utilizado no parâmetro da função bind()). Por isso a estrutura deve ser preenchida com zeros antes de ser utilizada.

Se der tudo certo a função bind() retornará 0, caso contrário retornará SOCKET_ERROR, use o WSAGetLastError() para analisar o erro.

listen()

A função listen() coloca o socket para escutar as conexões. Seria como ligar o socket, agora ele pode receber por conexões dos clientes.

Declaração:
[c]int listen(SOCKET s, int backlog);[/c]

O parâmetro s é o socket descriptor que você já conhece. O backlog indica quantas conexões pendentes o socket pode deixar na fila para serem processadas, quando as conexões são aceitas elas são removidas da fila. O mínimo para o backlog é 1 e o máximo 5, mas não achei alguma coisa no site do MSDN que indicasse que o limite é sempre 5.

Se tudo der certo ele vai retornar 0, caso contrário ele retornará um SOCKET_ERROR. Novamente você pode usar a função WSAGetLastError() para analisar o erro se quiser.

accept()

O accept() aceita a conexão quando ela é detectada.

Declaração:
[c]SOCKET accept(SOCKET s, struct sockaddr* addr, int* addrlen);[/c]

O parâmetro s é o socket descriptor. O parâmetro addr é um ponteiro para uma estrutura do tipo sockaddr (igual ao do função bind()), nela a função irá armazenar a o endereço (estrutura) da entidade (cliente) que requisitou a conexão. Você não precisa armazenar este valor se não quiser obter informações do cliente, pode colocar apenas um NULL no lugar.

O parâmetro addrlen é um ponteiro onde a função irá colocar o tamanho em bytes da estrutura addr recebida do cliente. Se você passar um NULL para o parâmetro addr então o addrlen retornará NULL para o ponteiro. Você também pode apenas colocar um NULL no parâmetro do addrlen.

Repare que o programa ficará esperando por uma conexão, ele ficará meio travado enquanto isso. Você terá que fazer ele rodar de forma assíncrona, mas isso não é importante agora, primeiro temos que fazer o servidor e o cliente funcionarem.

A função accept() irá retornar um socket descriptor do cliente. Se der algum problema será retornado um INVALID_SOCKET. Novamente use o WSAGetLastError() para verificar os erros.

recv()

O recv() recebe os dados de uma conexão.

Declaração:
[c]int recv(SOCKET s, char* buf, int len, int flags);[/c]

O parâmetro s é o socket descriptor do cliente. O parâmetro buf é o buffer onde serão armazenadas as informações recebidas. O len é o tamanho deste buffer.

O parâmetro flags indica o modo como serão recebidos os dados, eu deixei como 0 (zero) assim ele não fará nenhuma ação especial. Para mais detalhes sobre outras opções olhe no site MSDN.

O recv() irá retornar o total de bytes recebidos, mas você não irá receber mais bytes do que especificou em len. Ele retorna 0 (zero) quando a conexão é fechada normalmente. Se der algum erro ele retorna um SOCKET_ERROR. Use o WSAGetLastError() para saber mais do erro.

Repare que eu também usei a função inet_ntoa() para exibir junto com a mensagem. Essa função converte um endereço de Internet (IPv4) em uma string do endereço formatado nos padrões de Internet com pontos decimais. Ela já retorna em "Little Endian".

O recv() está dentro de um loop, antes de receber uma mensagem o buffer (variável message no código) deverá ser limpo. Após receber a mensagem, ela é exibida. O loop só pára quando o cliente enviar uma mensagem "#quit". Não é a melhor maneira de ser terminar o programa, mas serve por enquanto.

Depois de tudo pronto, nós devemos finalizar a aplicação com as funções WSACleanup() e closesocket(). Você só precisa olhar o código para entender como usa-las. E é isso, o micro-nano-humilde-servidor tá pronto agora só falta fazer o cliente.

Criando cliente que envia as mensagens

[c]/*
CLIENTE
*/

#include
#include
#include
#include

#define BUFFER_SIZE 128
#define EXIT_CALL_STRING "#quit"

int remote_socket = 0;
int message_length = 0;

unsigned short remote_port = 0;

char remote_ip[32];
char message[BUFFER_SIZE];

struct sockaddr_in remote_address;

WSADATA wsa_data;

/* Exibe uma mensagem de erro e termina o programa */
void msg_err_exit(char *msg)
{
fprintf(stderr, msg);
system("PAUSE");
exit(EXIT_FAILURE);
}

int main(int argc, char **argv)
{
if (WSAStartup(MAKEWORD(2, 0), &wsa_data) != 0)
msg_err_exit("WSAStartup() failed\n");

printf("IP do servidor: ");
scanf("%s", remote_ip);
fflush(stdin);

printf("Porta do servidor: ");
scanf("%d", &remote_port);
fflush(stdin);

remote_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (remote_socket == INVALID_SOCKET)
{
WSACleanup();
msg_err_exit("socket() failed\n");
}

// preenchendo o remote_address (servidor)
memset(&remote_address, 0, sizeof(remote_address));
remote_address.sin_family = AF_INET;
remote_address.sin_addr.s_addr = inet_addr(remote_ip);
remote_address.sin_port = htons(remote_port);

printf("conectando ao servidor %s...\n", remote_ip);
if (connect(remote_socket, (struct sockaddr *) &remote_address, sizeof(remote_address)) == SOCKET_ERROR)
{
WSACleanup();
msg_err_exit("connect() failed\n");
}

printf("digite as mensagens\n");
do
{
// limpa o buffer
memset(&message, 0, BUFFER_SIZE);

printf("msg: ");
gets(message);
fflush(stdin);

message_length = strlen(message);

// envia a mensagem para o servidor
if (send(remote_socket, message, message_length, 0) == SOCKET_ERROR)
{
WSACleanup();
closesocket(remote_socket);
msg_err_exit("send() failed\n");
}
}
while(strcmp(message, EXIT_CALL_STRING)); // sai quando enviar um "#quit" para o servidor

printf("encerrando\n");
WSACleanup();
closesocket(remote_socket);

system("PAUSE");
return 0;
}[/c]

Essa parte vai ser mais rápida, pois já vimos o básico sobre sockets no servidor.

Você deve criar um socket no cliente para poder conectar ao servidor. Ele precisa preencher uma estrutura do tipo socketaddr_in (que nós já vimos) com as informações do servidor, olhe o código para entender melhor.

connect()

A função connect() irá conectar o cliente com o servidor.

Declaração:
[c]int connect(SOCKET s, const struct sockaddr* name, int namelen);[/c]

Ele recebe parâmetros iguais ao da função bind(). Se der algum erro ele retorna um SOCKET_ERROR.

A função connect() tem o mesmo problema da função accept(), enquanto ele estiver tentando conectar ficará travado (precisa ser assíncrono), mas não vou mostra como se faz isso, fica como lição de casa XD.

send()

E o send() é a função responsável por enviar informações ao servidor.

Declaração:
[c]int send(SOCKET s, const char* buf, int len, int flags);[/c]

Os parâmetro que ele recebe também são iguais ao da função recv(). O send() retonará o número de bytes enviados, esse número não será maior do len (e ele não enviará mais o que o especificado em len). Se der um erro ele retornará SOCKET_ERROR. Use o WSAGetLastError() para mais informações.

Lembre-se de limpar o buffer de mensagens, senão você irá enviar "lixo" para o servidor.

Pronto!

Compile os dois programas, depois execute primeiro o servidor depois o cliente. Se estiver rodando na mesma máquina você pode conectar o cliente no IP "127.0.0.1" que dará no mesmo. E é só isso, fácil demais não?

Para saber mais informações sobre o WinSock visite a página de referência da MSDN em Winsock Reference.

14 Responses to “Sockets em C (WinSock)”

  1. DarK_PiJaMiNhANo Gravataron 05 Nov 2007 at 2:21 am

    hey, muito bom o tutorial pra quem está realmente começando!

    Eu não sabia nada sobre sockets, e dei os primeiros passos com o seu tutorial, me ajudou bastante,

    vlwz
    ;)

  2. Jedidias Nunes DiasNo Gravataron 12 Nov 2007 at 11:08 am

    CARA…

    Parabéns mesmo pelo tutorial.

    Estou tentando faz uma cara estabelecer conexão entre dois pcs.

    Só acho que faltou uma observaçãozinha de nada neste tutorial…

    Não sei se é o correto a fazer… só sei que aqui só funcionou quando eu fiz assim… Então veja se será útil e me avise depois, ok?

    CONFIGURANDO O DEV C PARA COMPILAR SEM ERROS

    Se você - assim como eu - estiver com alguns erros incompreensíveis ao compilar, tente fazer assim:
    1 - abra o DEV
    2 - Vá em ARQUIVO/NOVO/PROJETO… (ao invés de arquivo/novo/arquivo fonte)
    3 - escolha projeto em branco… (emptyProject) e dê OK
    4 - clique com o botão direito sobre o projeto novo, no canto esquerdo da tela, e solicite um novo arquivo.
    5 - Dê um ALT P e adicione a tal lib citada pelo autor, que é a libwsock32.a.
    Agora sim… funfa que é uma beleza.

    Obs. Faça um projeto para o server e outro projeto para o Client. se tentar os dois ao mesmo tempo, não rola. (Pelo menos comigo não deu certo)

  3. Yoshio IwamotoNo Gravataron 15 Nov 2007 at 10:42 pm

    O modo de linkar a lib vai depender de como você está compilando, se é pela interface gráfica ou linha de comando. Não entrei em detalhes de como fazer isto pois varia de um ambiente de desenvolvimento para outro.
    No caso de se utilizar um ou dois projetos também depende. Uma idéia seria ter tanto o cliente quanto o servidor em um único programa, dando a opção de escolha antes de executa-lo.

  4. PauloNo Gravataron 29 Nov 2007 at 5:05 pm

    Cara, salvou uma vida e uma boa nota, tava precisando mesmo de um soft de chat que realmente prestasse….

    vlw….

  5. Luis FelipeNo Gravataron 08 Dec 2007 at 4:29 pm

    Gostei muito do seu trabalho.
    mais não tenho muita habilidade em C ou C++ ainda
    ai ta tendo um errinho na hora q eu compilo
    seguir os passo do Jedidias Nunes Dias e mesmo assim nada =[
    é triste
    tá dano erro na "Makefile.win [Build Error] ["Sem Título2.o"] Error 1 ”
    “Sem titulo2″ foi o nome q eu dei para arquivo
    faço a mínima noção o seria isso oh
    se alguém me dé uma luz serei grato
    e um material mais aprofundado sobre esses tipo de erros e C/C++

  6. Yoshio IwamotoNo Gravataron 20 Dec 2007 at 4:25 am

    Bom, o seu erro é muito, muito, genérico. Pode ser 1 milhão de coisas. Pode ser o espaço no nome, a declaração da função Main não está como “int main(int argc, char** argv)”, o devc++ pode não estar conseguindo criar esse “Sem Título.o” no diretório onde está sendo compilado por causa de alguma permissão, pode estar faltando uma linha em branco no final do arquivo, etc, etc, etc…
    Estou sem o DevC++ aqui, mas dê uma pesquisada no Google para achar a fonte deste erro, com certeza alguém já passou pelo mesmo problema que você.

  7. MuriloNo Gravataron 20 Dec 2007 at 6:10 pm

    fiz tudo certo e não consegui compilar, fiz soh o client, pq o server eh em outro programa, preciso soh do client em C…
    =/

  8. MuriloNo Gravataron 20 Dec 2007 at 10:08 pm

    Consegui, e consegui fazer o cliente se comunicar com meu server feito em delphi, agora o que não vi ai e estou precisando e de enviar retorno para o client, como faria isso?

  9. KaflubesNo Gravataron 22 Feb 2008 at 5:20 pm

    cara, você tem que mudar o nome do seu arquivo fonte, porque não pode ter acento. isso é uma grande mancada do dev, o nome default deles em português não funciona porque eles colocaram acento em “Título”.

    5
    Autor: Luis Felipe | Data: December 8, 2007 | Hora: 4:29 pm

    Gostei muito do seu trabalho.
    mais não tenho muita habilidade em C ou C++ ainda
    ai ta tendo um errinho na hora q eu compilo
    seguir os passo do Jedidias Nunes Dias e mesmo assim nada =[
    é triste
    tá dano erro na “Makefile.win [Build Error] [”Sem Título2.o”] Error 1 ”
    “Sem titulo2″ foi o nome q eu dei para arquivo
    faço a mínima noção o seria isso oh
    se alguém me dé uma luz serei grato
    e um material mais aprofundado sobre esses tipo de erros e C/C++

  10. RocirNo Gravataron 03 Mar 2008 at 4:28 pm

    Alguém saberia configurar para o Visual Studio? Estou tentando aqui, mas sem sucesso. Agradeço quem puder ajudar.

  11. EDUARDONo Gravataron 22 Mar 2008 at 5:12 pm

    AI GALERA AQUI EU TO USANDO O DEV C++ PARA QUE LE NAO DE ERRO
    NA HORA DE COMPILAR .

    VC QUE COPIOU E COLOU DIRETO NO DEV C++ ESSE CÓDIGO
    BASTA TIRAR O ASTERISCO OU O NUMERO COPIADO (#*1,2,3,4,5 DO LADO CODIGO
    EO DEV C++ IRA COMPILAR SEM ERROS !!!!!!

  12. BrunoNo Gravataron 24 May 2008 at 10:56 am

    Excelente tutorial. Aqui funcionou tranquilo. Vou usar as rotinas para implementar meu jogo on-line. Valeu.

  13. diogoNo Gravataron 05 Nov 2008 at 7:54 pm

    “A função connect() tem o mesmo problema da função accept(), enquanto ele estiver tentando conectar ficará travado (precisa ser assíncrono), mas não vou mostra como se faz isso, fica como lição de casa XD”

    cara se vc pudesse passar algo sobre o modo assíncrono agradeceria, não encontrei nada relevante sobre isso =[

  14. Silas dos SantosNo Gravataron 21 Nov 2008 at 12:24 am

    Parabéns pela matéria cara, ajudou bastante =D!

Trackback URI | Comments RSS

Leave a Reply