Escribir Servidor Web en Python - SOCKETS
Escribir Servidor Web en Python - SOCKETS
sockets
// C-ish pseudocode
close(fd);
int fd = socket(SOCK_TYPE_TCP);
char buffer[2048];
read(fd, buffer, 2048);
write(fd, "some data", 10);
close(fd);
Imagine que hay una aplicación que quiere enviar un texto relativamente
largo a través de la red. Supongamos que el socket ya se ha abierto y el
programa está a punto de write(o, en lenguaje de redes, send) estos
datos en el socket. ¿Cómo se transmitirán estos datos?
Las computadoras viven en un mundo discreto. Las tarjetas de interfaz
de red (NIC) transmiten datos en pequeñas porciones: unos pocos
cientos de bytes a la vez. Al mismo tiempo, en general, el tamaño de los
datos que un programa puede querer enviar no está limitado de ninguna
manera y puede exceder los cientos de gigabytes. Para transmitir un dato
grande arbitrario a través de la red, es necesario fragmentarlo y cada
fragmento debe enviarse por separado. Lógicamente, el tamaño máximo
del fragmento no debe exceder la limitación del adaptador de red.
Por último, pero no menos importante ... Una dirección IP define un host
de red como un todo. Sin embargo, entre dos hosts cualesquiera, puede
haber muchas conexiones TCP simultáneas. Si la única información de
direccionamiento en nuestros fragmentos fueran las direcciones IP, sería
prácticamente imposible determinar la afiliación de los fragmentos con
las conexiones. Por lo tanto, se requiere cierta información de
direccionamiento adicional. Para eso, TCP introduce el concepto
de puertos . Cada conexión obtiene un par de números de puerto (uno
para el remitente, otro para el receptor) que identifica de forma única la
conexión TCP entre un par de IP. Por lo tanto, cualquier conexión TCP
puede ser plenamente identificado por la siguiente tupla: (source IP,
source port, destination IP, destination port).
En el caso de TCP, los flujos de trabajo de socket del lado del servidor y
del lado del cliente son diferentes. Un servidor espera pasivamente a que
los clientes se conecten. A priori, la dirección IP y el puerto TCP del
servidor son conocidos por todos sus potenciales clientes. Por el
contrario, el servidor no conoce las direcciones de sus clientes hasta el
momento en que se conectan. Es decir, los clientes desempeñan el papel
de iniciadores de la comunicación al conectarse activamente a los
servidores.
Sin embargo, hay más que eso. En el lado del servidor, en realidad hay
dos tipos de sockets involucrados: el socket del servidor antes
mencionado esperando conexiones y, sorpresa, sorpresa, ¡los sockets
del cliente! Por cada conexión establecida, hay un socket más creado en
el lado del servidor, simétrico a su contraparte del lado del cliente. Por lo
tanto, para N clientes conectados, siempre habrá N + 1 sockets en el
lado del servidor.
# python3
import socket
serv_sock = socket.socket(
socket.AF_INET, # set protocol family to 'Internet' (INET)
socket.SOCK_STREAM, # set socket type to 'stream' (i.e. TCP)
proto=0 # set the default protocol (for TCP it's IP)
)
def fileno(self):
return self._fd
Dado que, en general, una sola máquina servidor puede tener más de un
adaptador de red, debería haber una forma de vincular el socket del
servidor a una interfaz en particular asignando una dirección local de esta
interfaz al socket:
serv_sock.bind(('127.0.0.1', 6543))
serv_sock:
laddr (ip=<server_ip>, port=6543)
raddr (ip=0.0.0.0, port=*)
client_sock: # peer
laddr (ip=<client_ip>, port=51573) # 51573 is a random port
assigned by the OS
raddr (ip=<server_ip>, port=6543) # it's a server's listening port
Aquí hay un ejemplo simple de recibir algunos datos del cliente y luego
enviarlos de vuelta (el llamado servidor de eco):
# echo-server
data = client_sock.recv(2048)
client_sock.send(data)
Tenga en cuenta que las tres primeras opciones aún pueden conducir a
una situación en la que el socket del lado del servidor estará esperando
que la recv() llamada regrese por tiempo indefinido. Puede suceder si el
servidor quisiera recibir K mensajes del cliente mientras que el cliente
solo quisiera enviar M mensajes, donde M <K. Por lo tanto, depende de
los diseñadores de protocolo de nivel superior decidir las reglas de
comunicación.
chunks = []
while True:
data = client_sock.recv(2048)
if not data:
break
chunks.append(data)
Cerrar sockets
socket.close()
# python3
import socket
while True:
# Accept new connections in an infinite loop.
client_sock, client_addr = serv_sock.accept()
print('New connection from', client_addr)
chunks = []
while True:
# Keep reading while the client is writing.
data = client_sock.recv(2048)
if not data:
# Client is done with sending.
break
chunks.append(data)
client_sock.sendall(b''.join(chunks))
client_sock.close()
Las cosas son mucho más sencillas del lado del cliente. No existe un
conector de escucha en el lado del cliente. Solo necesitamos crear un
único punto final de socket y connect() enviarlo al servidor antes de
enviar algunos datos:
# python3
import socket