// Estas leyendo...

C \\ C++

Tutorial Programacion de Sockets en C Parte II

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

Hola a todos nuevamente, después de unos días me alegra informarles que ya esta lista la segunda entrega de este pequeño tutorial para incursionar en la programación con sockets.

En la entrega anterior aprendimos los principales tipos de sockets y como programar nuestro primer cliente, es decir el programa que conecta al servidor.

En esta ocasión vamos a aprender a programar el servidor, que es el programa que atenderá al cliente.

Pero antes de empezar a tocar la programación es bueno comenzar explicando brevemente que es el protocolo TCP y como trabaja de la mano del protocolo IP

TCP (Transmission Control Protocol, en español Protocolo de Control de Transmisión) es uno de los protocolos fundamentales en Internet. Fue creado entre los años 1973 - 1974 por Vint Cerf y Robert Kahn. Muchos programas dentro de una red de datos compuesta por ordenadores pueden usar TCP para crear conexiones entre ellos a través de las cuales puede enviarse un flujo de datos. El protocolo garantiza que los datos serán entregados en su destino sin errores y en el mismo orden en que se transmitieron. También proporciona un mecanismo para distinguir distintas aplicaciones dentro de una misma máquina, a través del concepto de puerto. TCP da soporte a muchas de las aplicaciones más populares de Internet, incluidas HTTP, SMTP, SSH y FTP.

Una dirección IP es un número que identifica de manera lógica y jerárquicamente a una interfaz de un dispositivo (habitualmente una computadora) dentro de una red que utilice el protocolo de Internet (Internet Protocol), que corresponde al nivel de red o nivel 3 del modelo de referencia OSI. Dicho número no se ha de confundir con la dirección MAC que es un número físico que es asignado a la tarjeta o dispositivo de red (viene impuesta por el fabricante), mientras que la dirección IP se puede cambiar.
Ahora que sabemos lo necesario sobre TCP \ IP entendemos que TCP es el protocolo que transportara nuestros datos a través del socket, mientras que IP sirve para saber de donde y a donde enviar dichos datos a través de la red.
Ambos protocolos trabajan en conjunto para poder establecer una conexión entre Cliente y Servidor.

Esto lo podemos entender mejor apreciando el siguiente diagrama.

Si ya se que esto no se acerca ni tantito a un diagrama de TCP / IP three-way handshake pero para darnos idea de cómo funciona la interacción (conmutación de datos) entre ambos equipos esta bien.

Puesto que ya tenemos algo de teoría y un entendimiento un poco mas amplio de cómo funciona una conexión en TCP ahora procederemos a Programar Nuestro servidor.

Dado que es muy similar la programación del servidor a la forma en la que programamos el cliente, a excepción de de la forma en la que se declara la estructura del socket y el hecho de que ahora nos pondremos en escucha por un puerto determinado envés de conectar a una IP, solo listare esa sección del código para ahorrar espacio.

char Buffer[1023];
Declaramos el tamaño del Buffer

conexloc.sin_family = AF_INET;
Definimos la version 4 de IP

conexloc.sin_addr.s_addr = INADDR_ANY;
Definimos IP local

conexloc.sin_port = htons(9999);
Definimos puerto (9999) por el que escuchara el 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”.

bind(locsock, (sockaddr*)&conexloc, sizeof(conexloc);
Una ves creado el socket asociamos el descriptor de fichero “locsock” a un puerto, para eso utilizamos la función “bind”, después asignamos la dirección IP pasándola como puntero a “sockaddr”.

listen(locsock, 5);
La función listen sirve para poner el socket en escucha por un puerto determinado, en nuestro caso el puerto “9999”que fue definido al socket en la función “bind”, utilizando el descriptor de fichero “locsock” que “socket()” nos dio. El último número es la cantidad de conexiones que podemos tener como máximo en la cola de espera.

conm=sizeof(struct sockaddr);
Definimos longitud de “sockaddr”

locsock=accept(locsock,(sockaddr*)&conexloc,&conm);
Aceptamos la conexión con la función “accept” utilizando el descriptor de fichero “locsock” para transmitir los datos entre ambas computadoras mediante nuestra estructura “SOCKADDR_IN” definida anteriormente en “conexloc”.

conm=recv(locsock,Buffer,sizeof(Buffer),0);
La función “recv” se encarga de recibir los datos a través del descriptor de fichero “locsock”, mientras que “Buffer” es donde se va almacenar la información recibida y posteriormente con “sizeof” medimos la longitud total del buffer recibido.

Dado que “recv” devuelve un valor de cero en caso de existir un error se utiliza un bucle while para verificar que seguimos conectados con el equipo remoto.

Código fuente completo del servidor:

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

using namespace std;

WSADATA wsadata; //Declaramos WSADATA

struct hostent *host;
//Declaramos estructura hostent donde almacenaremos la IP que nos devuelva gethostbyname

SOCKADDR_IN conexloc;
/*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

char Buffer[1023]; // Declaramos el tamaño del Buffer

int WSAInicio() { //declaramos procedimiento
         int wasa = WSAStartup(MAKEWORD(2,0),&amp;wsadata); //Indicamos versión 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
     conexloc.sin_family = AF_INET; // Ordenación de Maquina
      /*
         Definimos la version 4 de IP
      */

     conexloc.sin_addr.s_addr = INADDR_ANY;
     /*
         Definimos IP local
     */

     conexloc.sin_port = htons(9999);
     /* Definimos puerto (9999) por el que escuchara el 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”.
     */

         if (bind(locsock, (sockaddr*)&amp;conexloc, sizeof(conexloc)) == SOCKET_ERROR) { // Si existen errores…
         /* Una ves creado el socket asociamos el descriptor de ficher "locsock" a un puerto, para eso utilizamos la función "bind"
            Después asignamos la dirección IP pasándola como puntero a "sockaddr".
         */

                printf("%s","Error definiendo socket \n"); //Mostramos un mensaje
                WSACleanup(); //Limpiamos WSADATA
                return 1; // Retornamos 1 dado que la funcion fallo
         }  else {
             if (listen(locsock, 5) == SOCKET_ERROR) { // Si existen errores…
             /* La función listen sirve para poner el socket en escucha por un puerto determinado, en nuestro caso el puerto 9999
                que fue definido al socket en la función "bind", utilizando el descriptor de fichero "locsock" que "socket()" nos dio
                El ultimo numero es la cantidad de conexiones que podemos tener como máximo en la cola de espera.
             */

                       printf("%s","Error Al ponerse en escucha \n"); //Mostramos un mensaje
                   WSACleanup(); //Limpiamos WSADATA
                           return 1; // Retornamos 1 dado que la función fallo
                 } else {
                     printf("%s","Esperando conexiones por puerto 9999 \n"); //Mostramos un mensaje
                     return 0; // Si se inicio todo bien retornamos 0
                    }

     }
}

void conexion(){

     int conm; //Declaramos variable para definir longitud de la estructura "sockaddr"
     conm=sizeof(struct sockaddr); // definimos longitud de "sockaddr"
     locsock=accept(locsock,(sockaddr*)&amp;conexloc,&amp;conm); // Conexion establecida
     /* Aceptamos la conexión con la funcion "accept" utilizando el descriptor de fichero "locsock" para transmitir los datos entre ambas computadoras
        mediante nuestra estructura "SOCKADDR_IN" definida anteriormente en "conexloc"
     */

     printf("%s","Conexion establecida \n"); //Mostramos un mensaje
         while (conm!=0){ //mientras estemos conectados..
                conm=recv(locsock,Buffer,sizeof(Buffer),0); //recibimos los datos que envíe
                /* La función "recv" se encarga de recibir los datos a través del descriptor de fichero "locsock"
                   Buffer es donde se va almacenar la información recibida y posteriormente con "sizeof" medimos la longitud total del buffer recibido
                   Dado que "recv" devuelve un valor de cero en caso de existir un error se utiliza un bucle while para verificar
                   que seguimos conectados con el equipo remoto
                */

                if (conm>0){  //si seguimos conectados al cliente
                printf("Datos recibidos:%s",Buffer); //imprimimos los datos recibidos
                }
         }

     }

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…
                 conexion(); // Iniciamos el procedimiento "conexion"
              }
           }
        }
     }

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

Bien ahora que ya tienes El cliente y el Servidor, llego el momento de ejecutar el Servidor seguido del cliente y voila!!!

Felicidades ya tienes tu primer aplicación con sockets programada en C.
Hasta aquí termina este tutorial, espero que te haya gustado y te sea de utilidad, cualquier sugerencia puedes contactarme vía E-Mail: octalh@gmail.com

Solo me queda decirte que ahora te toca perfeccionar el cliente para que interactué de igual forma que lo hace el servidor ;)

Tutorial elaborado para: www.aztekmindz.org

Descargar Projecto Fuentes y Compilado

Comentarios

One comment para “Tutorial Programacion de Sockets en C Parte II”

  1. esta bueno!!

    Posted by Carlos Sing | Agosto 7, 2008, 21:32

Deja un comentario