lunes, 20 de mayo de 2024

Redes con Python: Programación de Sockets para la Comunicación. 1.- Usando Sockets con Python.

Python tiene una API que nos permite comunicarnos con otras programas a través de una red. Vamos a empezar viendo como usar esta API para crear un servidor y usar tu navegador para conectarnos a él. Lo primero que vamos a hacer es crear un nuevo programa de Python al que llamaremos server.py

Para poder usar el API tenemos que importar el módulo socket, de esta forma:

server.py

import socket
Esta línea importa el módulo "socket" que nos proporciona acceso a la interfaz de sockets de red de bajo nivel. Nos va a permitir realizar operaciones de red, como establecer conexiones y trasferir datos entre sistemas.

Luego crearemos el socket. Lo llamaré server_socket y tendrá unos parámetros que van a determinar el tipo de protocolo utilizado.

server.py

import socket

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

  • socket.socket(): Es la función que crea un nuevo socket.
  • socket.AF_INET: Especifica la familia de direcciones que el socket puede usar. AF_INET significa que el socket utilizará direcciones IPv4. Si quisieras usar direcciones IPv6, usarías AF_INET6.
  • socket.SOCK_STREAM: Especifica el tipo de socket. SOCK_STREAM indica que se va a usar un socket de flujo, lo que significa que se trata de un socket orientado a la conexión y basado en TCP (Transmission Control Protocol). TCP es un protocolo que proporciona una conexión confiable y orientada a la conexión para el intercambio de datos.

En conclusión, hemos creado un socket TCP/IP. A continuación tenemos que vincular ese socket con una dirección y un puerto especifico. 

server.py

import socket

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM
server_socket.bind(('0.0.0.0', 8081))
Usaremos la dirección 0.0.0.0 y el puerto 8081. 

La dirección 0.0.0.0 se utiliza en la función bind() de los sockets para indicar que el socket debe aceptar conexiones en todas las interfaces de red disponibles del sistema. Es una dirección especial que no se refiere a una sola dirección IP específica, sino a todas las direcciones IP configuradas en el dispositivo. Vamos a explorar esto con más detalle:

Contexto de Uso de 0.0.0.0

  • 0.0.0.0: Acepta conexiones en todas las interfaces de red del sistema. Esto significa que el servidor será accesible desde cualquier dirección IP que tenga el sistema (por ejemplo, desde localhost, direcciones privadas, y direcciones públicas, si están configuradas). También es equivalente a dejar este argumento en blanco "".
  • 127.0.0.1: Acepta conexiones solo desde la interfaz de loopback (localhost). Esto significa que el servidor solo será accesible desde el mismo sistema u ordenador donde está ejecutándose y no desde otras máquinas.
  • Dirección IP específica (como 192.168.1.10): Acepta conexiones solo en esa dirección IP específica. Esto puede ser útil si solo quieres que tu servidor esté accesible a través de una interfaz de red particular.
Es decir cualquier conexión que se solicite a nuestro ordenador activará el socket en el puerto 8081.

Por otra parte, los puertos se utilizan normalmente para identificar el propósito de la conexión. Se puede utilizar cualquier número entre el 0 y el 65535. No obstante ten en cuenta que los puertos entre el 0 y el 1023 se les llama "Puertos Bien Conocidos" y suelen estar restringidos a usos específicos. Por ejemplo:

puertos en un ordenador


Podríamos haber usado cualquier otro puerto, pero hemos optado por el 8081 que se suele utilizar para testear.

Preparemos al servidor para que se ponga a escuchar las peticiones entrantes. Vamos también a poner un mensaje en la pantalla, que indique que el servidor está esperando la conexión y podemos también limitar el número de conexiones entrantes. (5 en este caso)

server.py

import socket

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Vincula el socket a una dirección y puerto específicos
server_socket.bind(('0.0.0.0', 8081))

# Escucha conexiones entrantes (el argumento especifica el tamaño máximo de la cola de conexiones)
server_socket.listen(5)

print("Servidor escuchando en el puerto 8080...")
Ya tenemos al servidor esperando a las conexiones. Una vez que tenga una petición tiene que aceptarlas. Así que vamos a prepararlo todo:

server.py

import socket

# Creamos el socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Vincula el socket a una dirección y puerto específicos
server_socket.bind(('0.0.0.0', 8081))

# Escucha conexiones entrantes (el argumento especifica el 
# número máximo de solicitudes de conexión en cola)
server_socket.listen(5)

print("Servidor escuchando en el puerto 8081...")

# Aceptar una conexión
client_socket, client_address = server_socket.accept()
print(f"Conexión aceptada de {client_address}")

El programa está esperando que se realice una conexión y cuando esto se produce, se devuelven dos parámetros. 

El primero "client_socketes un nuevo objeto de socket que representa la conexión específica con el cliente que ha hecho la solicitud de conexión. Este socket se usa para enviar y recibir datos a y desde el cliente. Cada vez que un cliente se conecta al servidor, el método accept() crea un nuevo socket para manejar esa conexión individual, permitiendo que el servidor continúe escuchando nuevas conexiones en el socket original.

Para que quede claro. Un socket_server no manda ningún dato. No recibe ningún dato. Solo produce sockets clientes. Cada client_socket es creado en respuesta a algún otro socket cliente que hace connect al host y puerto al que estamos vinculado. Tan pronto como hemos creado ese cliente_socket volvemos a escuchar por más conexiones. Los dos "clientes" son libres de conversar entre ellos - están usando algún puerto asignado dinámicamente que será reciclado cuando la conversación termine.

Cualquier software que sea capaz de conectarse mediante TCP podrá establecer una conexión con nuestro programa de servidor, como un navegador web o un terminal. Ahora, en el siguiente paso, vamos a crear tu propio cliente, pero antes vamos a probar que nuestro programa funciona usando tu navegador web para conectarse. Así que ejecutemos nuestro programa.

El segundo "client_address" es una tupla que contiene la dirección IP y el número de puerto del cliente que ha establecido la conexión con el servidor. Este parámetro proporciona información sobre la ubicación de red del cliente.

Pondremos un mensaje en pantalla para indicar que se ha realizado la conexión.


>>> python3 server.py
Servidor escuchando en el puerto 8081...

Podemos ver que está esperando una conexión. Abre un navegador web y ve a la dirección 127.0.0.1, que es la dirección local de tu ordenador. Usa el puerto 8081 porque ese es el puerto en el que programa está escuchando. (http://127.0.0.1:8081/)

>>> python3 server.py
Servidor escuchando en el puerto 8081...
Conexión aceptada de ('127.0.0.1', 45438)
Aunque el navegador web no muestra ninguna información, podemos ver que está conectado. ¿Por que el navegador no muestra nada? ¿Qué tendrías que hacer para que el navegador responda?  Lo veremos en el próximo post.

Puedes encontrar el código de este post en el siguiente enlace.

No hay comentarios:

Publicar un comentario