Los sockets son mecanismos para el intercambio de datos entre procesos . Cualquiera de esos procesos puede existir sobre la misma máquina o en diferentes máquinas conectadas via red . Una vez que las conexiónes de socket se han establecido , los datos pueden enviarse en ambas direcciones hasta que una de las conexiones sea cerrada.
Yo necesito utilizar sockets para un proyecto en el que estoy trabajando , por lo que desarrollé y redefiní clases para encapsular las llamadas a la API referente a los sockets . Generalmente , la aplicación que solicita datos es llamada cliente , y la aplicación que sirve datos es llamada servidora. Yo implemente dos clases primarias , ClientSocket y ServerSocket ,por lo tanto el cliente y el servidor podrían utilizarse para el intercambio de datos .
La meta de este artículo es enseñar como utilizar las clases ClientSocket y ServerSocket en nuestras propias aplicaciones . Primero hablaremos brevemente de la comunicación entre el cliente y es servidor y a contianuación desarrollaremos un ejemplo simple de cliente y servidor que usen esas dos clases .
Antes de entrar dentro del código , deberíamos explicar brevemente los pasos que hay que realizar en una conexión tíica entre el cliente y el servidor . La siguiente tabla muestra estos pasos .
| Servidor | Cliente |
| 1.Establecer el socket de escucha y esperar a recibir conexiones desde la parte cliente. | |
| 2.Crear un socket en el cliente e intentar conectar con el servidor. | |
| 3.Aceptar la conexión del cliente . | |
| 4.Enviar y recibir datos. | 4.Enviar y recibir datos. |
| 5. Cerrar la conexión. | 5. Cerrar la conexión. |
Esto es basicamente todo . Primero , el servidor crea un socket de escucha , y espera por intentos de conexiones desde los clientes . El cliente crea un socket e intenta conectar con el servidor . El servidor a continuación acepta la conexión , y los datos comenzarán a intercambiarse . Una vez que todos los datos han sido pasados a través de los sockets , cualquiera de ellos cerrará el punto de conexión.
Ahora es el momento de profundizar en el código .En la siguiente sección crearemos ambos cliente y servidor que desarrollen todos los pasos subrallados en la sección de visión general . Implementaremos esas operaciones en el orden tipico de ocurrencia - primero crearemos la parte del servidor que se encarga de la escucha , seguidamente crearemos en la parte cliente los conectores al servidor ,etc .Todo el código puede encontrarse en simple_server_main.cpp y simple_client_main.cpp.
Si prefieres experimentar y examinar el código fuente por tí mismo , salta a esta sección. Aquí está la lista de los fichero dentro de un proyecto , y aborda como como compilar y probar programas.
Lo primero que necesitamos realizar para crear un simple servidor de escucha es saber cuando entra un cliente .Este es el código requerido para establecer un socket en el servidor :
listado 1 : creando un socket de servidor ( parte de simple_server_main.cpp )
#include "ServerSocket.h"
#include "SocketException.h"
#include <string>
int main ( int argc, int argv[] )
{
try
{
// Create the server socket
ServerSocket server ( 30000 );
// rest of code -
// accept connection, handle request, etc...
}
catch ( SocketException& e )
{
std::cout << "Exception was caught:" << e.description() << "\nExiting.\n";
}
return 0;
}
Esto es todo lo que hay que hacer . El constructor para la clase ServerSocket llama a la API para establecer el socket de escucha . Esto esconde los detalles , por lo que tendrás que crear una instancia de esta clase para comenzar escuchando sobre el puerto local del computador.
Aviso del bloque try/catch . Las clases ServerSocket y ClientSocket utilizan un manejador de excepciones desarrollado en C++. Si esta clase de métodos falla por cualquier razón , esto generará una excepción del tipo SocketException , el cuál se encuentra definido en SocketException.h. No controlar los resultados de esta excepción provocan la terminación del programa , por lo que es mejor manejarlo . Puedes conseguir el texto del error causado llamando al método description() de la clase SocketException.
El segundo paso dentro de una conexión típica entre el cliente y el servidor es responsabilidad del cliente para intentar conectar con el servidor . Este código que mostramos a continuación es similar al visto anteriormente.
Listado 2 . Creando un socket en el cliente. (parte de simple_client_main.cpp )
#include "ClientSocket.h"
#include "SocketException.h"
#include <iostream>
#include <string>
int main ( int argc, int argv[] )
{
try
{
// Create the client socket
ClientSocket client_socket ( "localhost", 30000 );
// rest of code -
// send request, retrieve reply, etc...
}
catch ( SocketException& e )
{
std::cout << "Exception was caught:" << e.description() << "\n";
}
return 0;
}
Simplemente por crear una instancia de la clase ClientSocket , estas creando un socket en Linux , y conectarás al servidor y al puerto pasado como parámetro en el constructor . Al igual que la clase ServerSocket , si el constructor falla por cualquier razón , se lanzará una excepción .
El siguiente paso de la conexión cliente servidor ocurre junto al servidor . Es responsabilidad del servidor aceptar la tentativa de la conexión del cliente , el cuál abre un canal de comunicación entre dos sockets.
Hemos añadido funcionalidad a nuestro servidor . Aquí se encuentra la versión actualizada.
Listado 3 : aceptando una conexión desde el cliente ( parte de simple_server_main.cpp )
#include "ServerSocket.h"
#include "SocketException.h"
#include <string>
int main ( int argc, int argv[] )
{
try
{
// Create the socket
ServerSocket server ( 30000 );
while ( true )
{
ServerSocket new_sock;
server.accept ( new_sock );
// rest of code -
// read request, send reply, etc...
}
}
catch ( SocketException& e )
{
std::cout << "Exception was caught:" << e.description() << "\nExiting.\n";
}
return 0;
}
Para aceptar una conexión solo se requiere una llamada al método accept. Este método acepta la tentativa de conexión , y rellena new_sock con la información acerca de la conexión . Veremos como se utiliza new_sock en la siguiente sección .
Ahora que el servidor ha aceptado la solicitud de conexión del cliente , llega el momento del envio de un lado a otro sobre el socket.
Una de las características avanzadas de C++ es la habilidad para sobrecargar operadores - o simplementa hacer que un operador desarrolle una cierta operación . Dentro de las clases ClientSocket y ServerSocket he sobrecargado los operadores << y >> ,así pués cuando los usemos estos escribirán datos y recibirán datos desde el socket . Aquí se encuentra la versión actualizada de un servidor simple :
Listado 4 : Una simple implementación de un servidor ( simple_server_main.cpp )#include "ServerSocket.h"
#include "SocketException.h"
#include <string>
int main ( int argc, int argv[] )
{
try
{
// Create the socket
ServerSocket server ( 30000 );
while ( true )
{
ServerSocket new_sock;
server.accept ( new_sock );
try
{
while ( true )
{
std::string data;
new_sock >> data;
new_sock << data;
}
}
catch ( SocketException& ) {}
}
}
catch ( SocketException& e )
{
std::cout << "Exception was caught:" << e.description() << "\nExiting.\n";
}
return 0;
}
La variable new_sock contiene toda la información sobre el socket , por lo que la usaremos para el intercambio de datos con el cliente . La línea "new_sock >> data ;" debería leerse como "leer datos desde new_sock , y colocar estos datos dentro de una variable de tipo string 'data' ".Similarmente , la siguiente línea envia los datos que están en 'data' en el socket hacia el cliente .
Si has prestado atención , notarás que hemos creado un servidor de eco. Cada pedazo de dato que es enviado desde el cliente al servidor es enviado hacia atrás .Ya podemos escribir el cliente por lo que este enviarà datos y a continuación escribirá la respuesta del servidor .
Listado 5 : una simple implementación de un cliente ( simple_client_main.cpp )
#include "ClientSocket.h"
#include "SocketException.h"
#include <iostream>
#include <string>
int main ( int argc, int argv[] )
{
try
{
ClientSocket client_socket ( "localhost", 30000 );
std::string reply;
try
{
client_socket << "Test message.";
client_socket >> reply;
}
catch ( SocketException& ) {}
std::cout << "We received this response from the server:\n\"" << reply << "\"\n";;
}
catch ( SocketException& e )
{
std::cout << "Exception was caught:" << e.description() << "\n";
}
return 0;
}
Cuando envies el string "Test Message" hacia el servidor , leerá la respuesta del servidor y imprimira la respuesta por la salida std.
Ahora que nos hemos acercado al uso básico de las clases ClientSocket y ServerSocket , podremos construir el proyecto entero y probarlo .
Los siguientes ficheros ejecutan nuestro ejemplo :
Miscelanea::
Compilar es simle . Primero guardamos todos los ficheros del proyecto dentro de un subdirectorio , luego tecleamos en nuestro terminal de comandos :
prompt$ cd directory_you_just_created prompt$ make
Esto compilará todos los ficheros que están dentro del proyecto , y crea un servidor y un cliente . Para probar esos dos ficheros , ejecuta el servidor en un terminal y el cliente en otro terminal :
first prompt: prompt$ ./simple_server running.... second prompt: prompt$ ./simple_client We received this response from the server: "Test message." prompt$
El cliente enviará datos al servidor , leerá las respuestas e imprimirá en la salida std . Puedes ejecutar el cliente todas las veces que quieras - el servidor responderá cada colicitud .
Los sockets son un simple y eficiente camino para enviar datos entre procesos . En este artículo hemos realizado comunicaciones bajo sockets y y hemos desarrollado un ejemplo de un cliente y un servidor .¡ Ya estas preparado para añadir sockets de comunicaciones en tu aplicación !