Python Language
Enchufes
Buscar..
Introducción
Muchos lenguajes de programación usan sockets para comunicarse a través de procesos o entre dispositivos. Este tema explica el uso correcto del módulo de sockets en Python para facilitar el envío y la recepción de datos a través de protocolos de red comunes.
Parámetros
Parámetro | Descripción |
---|---|
socket.AF_UNIX | Unix Socket |
socket.AF_INET | IPv4 |
socket.AF_INET6 | IPv6 |
socket.SOCK_STREAM | TCP |
socket.SOCK_DGRAM | UDP |
Envío de datos a través de UDP
UDP es un protocolo sin conexión. Los mensajes a otros procesos o computadoras se envían sin establecer ningún tipo de conexión. No hay confirmación automática si su mensaje ha sido recibido. UDP se utiliza generalmente en aplicaciones sensibles a la latencia o en aplicaciones que envían transmisiones de toda la red.
El siguiente código envía un mensaje a un proceso que escucha en el puerto de host local 6667 usando UDP
Tenga en cuenta que no es necesario "cerrar" el socket después del envío, ya que UDP no tiene conexión .
from socket import socket, AF_INET, SOCK_DGRAM
s = socket(AF_INET, SOCK_DGRAM)
msg = ("Hello you there!").encode('utf-8') # socket.sendto() takes bytes as input, hence we must encode the string first.
s.sendto(msg, ('localhost', 6667))
Recepción de datos a través de UDP
UDP es un protocolo sin conexión. Esto significa que los pares que envían mensajes no requieren establecer una conexión antes de enviar mensajes. socket.recvfrom
devuelve una tupla ( msg
[el mensaje que recibió el socket], addr
[la dirección del remitente])
Un servidor UDP que utiliza únicamente el módulo de socket
:
from socket import socket, AF_INET, SOCK_DGRAM
sock = socket(AF_INET, SOCK_DGRAM)
sock.bind(('localhost', 6667))
while True:
msg, addr = sock.recvfrom(8192) # This is the amount of bytes to read at maximum
print("Got message from %s: %s" % (addr, msg))
A continuación se muestra una implementación alternativa utilizando socketserver.UDPServer
:
from socketserver import BaseRequestHandler, UDPServer
class MyHandler(BaseRequestHandler):
def handle(self):
print("Got connection from: %s" % self.client_address)
msg, sock = self.request
print("It said: %s" % msg)
sock.sendto("Got your message!".encode(), self.client_address) # Send reply
serv = UDPServer(('localhost', 6667), MyHandler)
serv.serve_forever()
Por defecto, los sockets
bloquean. Esto significa que la ejecución del script esperará hasta que el socket reciba datos.
Envío de datos a través de TCP
El envío de datos a través de Internet es posible mediante múltiples módulos. El módulo de sockets proporciona acceso de bajo nivel a las operaciones subyacentes del sistema operativo responsables de enviar o recibir datos de otras computadoras o procesos.
El siguiente código envía la cadena de bytes b'Hello'
a un servidor TCP que escucha en el puerto 6667 en el host local y cierra la conexión cuando termina:
from socket import socket, AF_INET, SOCK_STREAM
s = socket(AF_INET, SOCK_STREAM)
s.connect(('localhost', 6667)) # The address of the TCP server listening
s.send(b'Hello')
s.close()
La salida del zócalo está bloqueando de forma predeterminada, lo que significa que el programa esperará en la conexión y enviará las llamadas hasta que la acción se complete. Para la conexión eso significa que el servidor realmente acepta la conexión. Para enviar, solo significa que el sistema operativo tiene suficiente espacio de almacenamiento para poner en cola los datos que se enviarán más tarde.
Los enchufes siempre deben estar cerrados después de su uso.
Servidor de socket TCP multihilo
Cuando se ejecuta sin argumentos, este programa inicia un servidor de socket TCP que escucha las conexiones a 127.0.0.1
en el puerto 5000
. El servidor maneja cada conexión en un hilo separado.
Cuando se ejecuta con el argumento -c
, este programa se conecta al servidor, lee la lista de clientes y la imprime. La lista de clientes se transfiere como una cadena JSON. El nombre del cliente puede especificarse pasando el argumento -n
. Al pasar nombres diferentes, se puede observar el efecto en la lista de clientes.
client_list.py
import argparse
import json
import socket
import threading
def handle_client(client_list, conn, address):
name = conn.recv(1024)
entry = dict(zip(['name', 'address', 'port'], [name, address[0], address[1]]))
client_list[name] = entry
conn.sendall(json.dumps(client_list))
conn.shutdown(socket.SHUT_RDWR)
conn.close()
def server(client_list):
print "Starting server..."
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('127.0.0.1', 5000))
s.listen(5)
while True:
(conn, address) = s.accept()
t = threading.Thread(target=handle_client, args=(client_list, conn, address))
t.daemon = True
t.start()
def client(name):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 5000))
s.send(name)
data = s.recv(1024)
result = json.loads(data)
print json.dumps(result, indent=4)
def parse_arguments():
parser = argparse.ArgumentParser()
parser.add_argument('-c', dest='client', action='store_true')
parser.add_argument('-n', dest='name', type=str, default='name')
result = parser.parse_args()
return result
def main():
client_list = dict()
args = parse_arguments()
if args.client:
client(args.name)
else:
try:
server(client_list)
except KeyboardInterrupt:
print "Keyboard interrupt"
if __name__ == '__main__':
main()
Salida del servidor
$ python client_list.py
Starting server...
Salida de cliente
$ python client_list.py -c -n name1
{
"name1": {
"address": "127.0.0.1",
"port": 62210,
"name": "name1"
}
}
Los buffers de recepción están limitados a 1024 bytes. Si la representación de la cadena JSON de la lista de clientes supera este tamaño, se truncará. Esto hará que se genere la siguiente excepción:
ValueError: Unterminated string starting at: line 1 column 1023 (char 1022)
Raw Sockets en Linux
Primero desactivas la suma de comprobación automática de tu tarjeta de red:
sudo ethtool -K eth1 tx off
Luego envíe su paquete, utilizando un socket SOCK_RAW:
#!/usr/bin/env python
from socket import socket, AF_PACKET, SOCK_RAW
s = socket(AF_PACKET, SOCK_RAW)
s.bind(("eth1", 0))
# We're putting together an ethernet frame here,
# but you could have anything you want instead
# Have a look at the 'struct' module for more
# flexible packing/unpacking of binary data
# and 'binascii' for 32 bit CRC
src_addr = "\x01\x02\x03\x04\x05\x06"
dst_addr = "\x01\x02\x03\x04\x05\x06"
payload = ("["*30)+"PAYLOAD"+("]"*30)
checksum = "\x1a\x2b\x3c\x4d"
ethertype = "\x08\x01"
s.send(dst_addr+src_addr+ethertype+payload+checksum)