// Estas leyendo...

C \\ C++

Tutorial Programacion de Sockets en C Parte I

Tutorial Programacion de Sockets en C
por Octalh
www.aztekmindz.org

Antes que otra cosa aclarar que este texto se a escrito simplemente como un tutorial y no como una guía de sockets, si bien el tema es demasiado extenso me pareció bien adjuntar lo necesario para dar nuestros primeros pasos en la programación con sockets.
Ya veras que no eran tan complicados como pensabas que eran. ;)

Que es un socket.

Un socket no es mas que una forma de comunicarce con otras computadoras utilizando un descriptor de fichero que la funcion socket() nos da, y posteriormente nos comunicamos con el para interactuar entre ambas computadoras usando send() y recv().

Tipos de sockets.

Existen principalmente dos tipos de sockets:

  • SOCK_STREAM Sockets de Flujo
  • SOCK_DGRAM Sockets de Datagramas

Los sockets de Flujo establecen una conexión y son bastante seguros, ya que el protocolo que utilizan es TCP, y como bien saben TCP se encarga de que todo lo que se envía llegue en el mismo orden y sin errores.

Los sockets de Datagramas se caracterizan por utilizar el protocolo UDP, por lo que se pueden enviar paquetes sin necesidad de una conexión, es decir. No es necesaria una serie de saludos previos para garantizar que la conexión sea segura y no exista perdida de datos.

Quiere decir que puedes enviar una serie de paquetes y no se recibirá confirmación alguna si fue recibido o no.

A Programar!!

Bien ahora que ya tenemos un poco de teoría base procederemos a programar nuestro primer Cliente. Es decir será el programa que conecte al Servidor.

Como compilador para los ejemplos utilizaremos Dev-C++
Requerimientos:
Primero tenemos que hacer una referencia a la librería <winsock2.h> y configurar el Linker de nuestro Dev-C++ para que adjunte la librería libws2_32.a, para eso nos vamos a Project –- Project options –- Parameters y en la opcion Linker damos clic en “Add Library or Object” y seleccionamos libws2_32.a, después damos en OK y ya esta.

#include <winsock2.h>
Referencia a la librería

WSADATA wsadata;
Declaramos WSADATA. En Windows antes de poder tocar la librería winsock tenemos que declarar primero WSADATA.

struct hostent *host;
Declaramos estructura “hostent” donde almacenaremos la IP que nos devuelva “gethostbyname”.

SOCKADDR_IN conexrem;
Declaramos una estructura SOCKADDR_IN para no tener que definir una IP y un puerto en cada paquete que enviemos. De esa forma todo viajara encapsulado utilizando la estructura SOCKADDR_IN.

SOCKET locsock;
Declaramos el descriptor de fichero que nos de el socket

int wasa = WSAStartup(MAKEWORD(2,0),&wsadata);
Indicamos versión 2.0 del socket

locsock = socket(AF_INET, SOCK_STREAM, 0);
Indicamos que usaremos un socket Stream(TCP).

host=gethostbyname(“localhost”);
Definimos Host con la IP que devuelva “gethostbyname” y resolvemos la dirección IP del Dominio “localhost”, con esto conseguimos traducir nombres de dominio sin utilizar una dirección IP directamente. (esta será la IP a donde nuestro Cliente conectara).

conexrem.sin_port = htons(9999);
Definimos puerto (9999) del socket utilizando un “short de máquina a short de la red” (htons)
Esto lo hacemos para ordenar la forma en la que enviaremos y recibiremos los datos por el puerto del socket, mas información buscar en google “Big-Endian”.

conexrem.sin_addr = *((struct in_addr *)host->h_addr);
Definimos la IP a donde conectaremos, como teníamos la dirección IP almacenada en “host” y la función solo admite “in_addr” para almacenar la IP en “h_addr”, tenemos que hacer un Casting de modo que “host” que es ahora un “in_addr” (debido a el casting (struct in_addr *) ) sea un puntero a “h_addr”.

conexrem.sin_family = AF_INET;
Definimos la versión 4 de IP

memset(conexrem.sin_zero,0,8);
Ponemos en 0 la cadena “sin_zero” en sus 8 espacios

connect(locsock, (sockaddr*)&conexrem, sizeof(conexrem);
Ahora procedemos a realizar la conexión con la IP remota que definimos en la estructura “conexrem.sin_addr” y el puerto definido en “conexrem.sin_port”. Dado que “connect” solo acepta “sockaddr” envés de nuestra estructura “SOCKADDR_IN” tenemos que realizar un casting para convertir “conexrem” en un “sockaddr”.
Después medimos la longitud de la estructura con “sizeof.

char msj[] = “Hola Mundo desde www.aztekmindz.org”;
Definimos el mensaje a enviar

send(locsock,msj,sizeof(msj),0);
Enviamos mensaje
Utilizamos la función “send” para enviar datos a través del descriptor de fichero “locsock” que nos dio el socket, después medimos la cantidad de caracteres a enviar en “msj” utilizando “sizeof”

Código del cliente.

#include <cstdlib>
#include <stdio.h>
#include <winsock2.h> // Referencia a la librería

using namespace std;

WSADATA wsadata; //Declaramos WSADATA

struct hostent *host;
//Delcaramos estrucutra hostent donde almacenaremos la IP que nos devuelva gethostbyname

SOCKADDR_IN conexrem;
/*Declaramos una estructura SOCKADDR_IN para no tener que definir una IP y un puerto
en cada paquete que enviemos. De esa forma todo viajara encapsulado utilizando la estructura
SOCKADDR_IN.
*/

SOCKET locsock; // Declaramos el descriptor de fichero que nos de el socket

int WSAInicio() { //declaramos procedimiento
         int wasa = WSAStartup(MAKEWORD(2,0),&amp;wsadata); //Indicamos version 2.0 del socket
         if (wasa != 0) { // Si existen errores…
     printf("%s","Error iniciando WSAStartup \n"); //Mostramos un mensaje
                WSACleanup(); //Limpiamos WSADATA
                return 1; // Retornamos 1 dado que la funcion fallo
         }
         return 0; // Si se inicio todo bien retornamos 0
}

int definirsocket() {
         locsock = socket(AF_INET/* IP V4 */, SOCK_STREAM, 0); // Indicamos que usaremos un socket Stream(TCP)
         if (locsock == INVALID_SOCKET) { // Si existen errores…
         printf("%s","Error definiendo socket \n"); //Mostramos un mensaje
                WSACleanup(); //Limpiamos WSADATA
                return 1; // Retornamos 1 dado que la funcion fallo
         }
         return 0; // Si se inicio todo bien retornamos 0
}

int estructsocket() { // Definimos procedimiento
         host=gethostbyname("localhost"); //Definimos Host con la IP que devuelva gethostbyname
         /* Resolvemos la direccion IP del Dominio localhost, con esto conseguimos traducir nombres de dominio
         sin utilizar direccion IP directamente.
         Esta sera la IP a donde nuestro Cliente conectara.
         */

         conexrem.sin_port = htons(9999); //Ordenacion de Red.
     /*  Definimos puerto (9999) del socket utilizando un "short de máquina a short de la red" (htons)
     Esto lo hacemos para ordenar la forma en la que enviaremos y recibiremos los datos por el puerto del socket
     Mas informacion buscar en google "Big-Endian".
     */

         conexrem.sin_addr = *((struct in_addr *)host->h_addr);
         /* Definimos la IP a donde conectaremos, como teniamos la direccion IP almacenada en "host"
     y la funcion solo admite "in_addr" para almacenar la IP en "h_addr" tenemos que hacer un Casting de modo que
     "host" que es ahora un "in_addr" (debido a el casting (struct in_addr *) ) sea un puntero a "h_addr".
         */

         conexrem.sin_family = AF_INET; // Ordenacion de Maquina
         /*
         Definimos la version 4 de IP
         */

         memset(conexrem.sin_zero,0,8); // Ponemos en 0 la cadena sin_zero en sus 8 espacios

         if (connect(locsock, (sockaddr*)&amp;conexrem, sizeof(conexrem)) == SOCKET_ERROR) { // Si existe un error…
     /* Ahora procedemos a realizar la conexion con la IP remota que definimos en la estructura "conexrem.sin_addr"
     y el puerto definido en "conexrem.sin_port". Dado que connect solo acepta "sockaddr" enves de nuestra
     estructura "SOCKADDR_IN" tenemos que realizar un casting para convertir "conexrem" en un "sockaddr".
     Despues medimos la longitud de la estructura con "sizeof" y por ultimo verificamos si se produjo un error
     tratando de conectar ya que por ejemplo la direccion IP remota podria no existir o estar detras de un FireWall
    */

         printf("%s","Error conectando al servidor remoto \n"); // En caso de error mostramos msj
                WSACleanup(); //Limpiamos WSADATA
                return 1; // Retornamos 1 dado que la funcion fallo
         }  else // De lo contrario…
        printf("%s","Coneccion realizada con exito \n"); // Mostramos msj
                return 0; // Si se inicio todo bien retornamos 0
}

void enviarmsj(){

     char msj[] = "Hola Mundo desde www.aztekmindz.org"; // Definimos el mensaje a enviar
     send(locsock,msj,sizeof(msj),0); // Enviamos mensaje
     /* Utilizamos la funcion "send" para enviar datos atraves del descriptor de fichero "locsock" que nos dio el socket
     Despues medimos la cantidad de caracteres a enviar en "msj" utilizando "sizeof"
     */

     }

void sockets(){ // Procedimiento que iniciara el socket secuencialmente.
        if((WSAInicio()) == 0) { // Si se inicio WSAInicio sin errores…
           if((definirsocket()) == 0) { // Si se inicio definirsocket sin errores…
              if((estructsocket()) == 0) { // Si se inicio estructsocket sin errores…
                 enviarmsj(); // Iniciamos el procedimiento "enviarmsj"
              }  else { // Si no conecto..
                      Sleep(500); // Esperamos 500 Milisegundos y…
                              sockets(); // Repetimos proceso
                      }
           }
        }
     }

int main(int argc, char *argv[])
{
    sockets(); // Iniciamos el Socket
}

Bien hasta aquí termino esta primera parte, espero que te sea de gran utilidad y si compartes este manual por favor da la fuente de donde lo tomaste, para la próxima entrega programaremos el servidor :D , por ahora tienes suficiente para entretenerte bastantes horas ;)
octalh@gmail.com
www.aztekmindz.org

Descargar Projecto Fuentes y Compilado

Comentarios

3 comments para “Tutorial Programacion de Sockets en C Parte I”

  1. Era justo lo que buscaba, un tuto que me diga las cosas justas para no tenerme que leer 100 paginas de sockets :P

    Se agradece

    Posted by Lorena | agosto 8, 2008, 21:16
  2. Muy bueno. Aunque creo que haría falta una tercera parte, y que tratara del mismo tema (los sockets) pero sin desconectar a un cliente al tiempo que aceptamos otra conexion. Como un servidor pero más real.

    Posted by Cristian | febrero 26, 2009, 3:59
  3. TENGO UNA DUDa EN TU CODIGO DE SOCKETS HAY UNA VARIABLE QUE ES “amp” y me marca error tanto en cliente como en el servidor. no se como corregirlo

    Posted by ANA | octubre 5, 2011, 1:41

Deja un comentario