Programación de socket Python

1. Zócalo

1. ¿Qué es un zócalo?

  Socket es una capa de abstracción de software intermedia para la comunicación entre la capa de aplicación y la familia de protocolos TCP / IP. Es un conjunto de interfaces. En el modo de diseño, Socket es en realidad un modo de fachada. Oculta la compleja familia de protocolos TCP / IP detrás de la interfaz Socket. Para los usuarios, un simple conjunto de interfaces es todo. Deje que Socket organice los datos para cumplir con la especificada Acuerdo

  Por lo tanto, no necesitamos entender profundamente el protocolo tcp / udp. El socket ha sido encapsulado para nosotros. Solo necesitamos seguir las regulaciones del socket para programar, y el programa escrito sigue naturalmente el estándar tcp / udp.

img

  La capa de aplicación es en realidad un protocolo TCP o UDP, porque el socket puede considerarse inexistente, ya que es solo un proceso de encapsulación, desde la perspectiva del protocolo no pertenece a la capa de transporte, por lo que generalmente se basan en protocolos de comunicación de red. .

2. Historia del desarrollo del zócalo

  Los zócalos se originaron en la década de 1970 en la versión de Unix de Berkeley o BSD Unix de la Universidad de California. Por lo tanto, a veces las personas también se refieren a los enchufes como "enchufes Berkeley" o "enchufes BSD". Al principio, los sockets fueron diseñados para comunicarse entre múltiples aplicaciones en el mismo host. Esto también se conoce como comunicación entre procesos o IPC. Hay dos tipos de sockets (o llamados dos razas), que se basan en archivos y en redes.

* Familia de sockets basada en el tipo de archivo *

Nombre de la familia del zócalo: AF_UNIX

Todo en Unix es un archivo. El socket basado en archivos llama al sistema de archivos subyacente para obtener datos. Dos procesos de socket se ejecutan en la misma máquina y pueden comunicarse indirectamente accediendo al mismo sistema de archivos.

* Familia de sockets basada en el tipo de red *

Nombre de la familia del conector: AF_INET

(También AF_INET6 se usa para ipv6, y hay algunas otras familias de direcciones, pero solo se usan para una determinada plataforma, o se abandonan, o rara vez se usan, o no se implementan en absoluto, todas las direcciones En la familia, AF_INET es el más utilizado. Python admite muchas familias de direcciones, pero dado que solo nos preocupamos por la programación de la red, la mayoría de las veces solo usamos AF_INET)

Segundo, el flujo de trabajo del socket

 Una escena de la vida. Desea llamar a un amigo, primero marque el número, el amigo levanta el teléfono después de escuchar el tono de llamada y luego establece una conexión con su amigo y puede hablar. Cuando termine la comunicación, cuelgue el teléfono para finalizar la conversación. Las escenas de la vida explican cómo funciona esto.

img

  Comencemos con el servidor. El servidor primero inicializa el Socket, luego se une al puerto (bind), escucha el puerto (listen), llama a aceptar para bloquear y espera a que el cliente se conecte. En este momento, si un cliente inicializa un Socket y luego se conecta al servidor (connect), si la conexión es exitosa, entonces se establece la conexión entre el cliente y el servidor. El cliente envía una solicitud de datos, el servidor recibe la solicitud y procesa la solicitud, luego envía los datos de respuesta al cliente, el cliente lee los datos y finalmente cierra la conexión, una interacción finaliza

uso de la función del módulo socket ()

import socket
socket.socket(socket_family,socket_type,protocal=0)
"""
socket_family 可以是 AF_UNIX 或 AF_INET。
socket_type 可以是 SOCK_STREAM  流式协议-->tcp协议
			     SOCK_DGRAM.protocol   报式协议-->udp协议
			     一般不填,默认值为 0,也就是流式协议-->tcp协议
"""

# 获取tcp/ip套接字(流式协议)
tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

#获取udp/ip套接字(报式协议)
udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 
"""
由于 socket 模块中有太多的属性。我们在这里破例使用了'from module import *'语句。使用 'from socket import *',我们就把 socket 模块里的所有属性都带到我们的命名空间里了,这样能 大幅减短我们的代码。
"""
tcpSock = socket(AF_INET, SOCK_STREAM

La función de socket del servidor
s.bind () se une (host, número de puerto) al socket
s.listen () inicia la escucha TCP
s.accept () acepta pasivamente las conexiones del cliente TCP, (bloquea) esperando la conexión Viniendo

La función de socket del cliente
s.connect () inicializa activamente la conexión del servidor TCP
s.connect_ex () La versión extendida de la función connect () devuelve un código de error cuando se produce un error en lugar de lanzar una excepción

La función de socket público
s.recv () recibe datos TCP
s.send () envía datos TCP (los datos enviados se pierden cuando la cantidad de datos a enviar es mayor que el espacio restante en el área del búfer propia y no se enviarán)
s.sendall () Enviar datos TCP completos (esencialmente, el envío se llama cíclicamente, sendall no perderá datos cuando la cantidad de datos a enviar sea mayor que el espacio restante en el área del búfer, llame cíclicamente el envío hasta que finalice)
s.recvfrom () Reciba datos UDP
s. sendto () envía datos UDP
s.getpeername () se conecta a la dirección del extremo remoto del
socket actual
s.getsockname () devuelvela dirección del socket actuals.getsockopt () devuelve los parámetros del socket especificado
s.setsockopt () Establezca el parámetro
s.close () delsocket especificado paracerrar el socket

El método de socket orientado a bloqueo
s.setblocking () establece los modos de bloqueo y no bloqueo del socket
s.settimeout () establece el tiempo de espera para bloquear operaciones de socket
s.gettimeout () obtiene el tiempo de espera para bloquear operaciones de socket

Función de socket orientada a
archivo
s.fileno ()descriptor de archivo de sockets.makefile () crea un archivo relacionado con el socket

Tres, zócalo basado en TCP

* tcp se basa en el enlace, primero debe iniciar el servidor y luego iniciar el cliente para vincular el servidor *

* servidor tcp *

1 ss = socket() #创建服务器套接字
2 ss.bind()      #把地址绑定到套接字
3 ss.listen()      #监听链接
4 inf_loop:      #服务器无限循环
5     cs = ss.accept() #接受客户端链接
6     comm_loop:         #通讯循环
7         cs.recv()/cs.send() #对话(接收与发送)
8     cs.close()    #关闭客户端套接字
9 ss.close()        #关闭服务器套接字(可选)

* cliente tcp *

1 cs = socket()    # 创建客户套接字
2 cs.connect()    # 尝试连接服务器
3 comm_loop:        # 通讯循环
4     cs.send()/cs.recv()    # 对话(发送/接收)
5 cs.close()            # 关闭客户套接字

El proceso de comunicación de socket es similar al proceso de llamada. Usaremos una llamada como ejemplo para implementar una versión baja de comunicación de socket:

Proceso: primero ejecute el servidor y descubra que el servidor está bloqueado en su lugar. El descubrimiento se debe a la aceptación, porque tiene que ir al grupo de semi-conexión para obtener las cosas, pero no, solo puede esperar. Después de que el servidor está conectado, el servidor imprimirá la dirección IP y el puerto. Cuando el cliente no envía datos, aunque el servidor ha pasado la aceptación, pero no hay recepción de datos. Después de que el cliente envía los datos, imprime inmediatamente los recibidos Contenido, luego el servidor capitaliza el contenido recibido y luego lo envía de vuelta al cliente, luego el cliente imprime los datos procesados ​​por el servidor y finalmente el cliente cierra y recicla los recursos del sistema operativo

Nota: aceptar, recibir, enviar causará bloqueo

Servidor:

import socket

# 买手机
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

# 绑定手机卡
phone.bind(('127.0.0.1',8080)) # 0-65535,1024以前的都被系统保留使用

# 开机
phone.listen(backlog=5) # 5指的是半连接池的大小
print('服务端启动完成')

# 等待电话连接请求
conn,client_addr = phone.accept() # 执行一次接收一个连接请求
print(conn)
print('客户端的Ip和端口:',client_addr)

# 收消息
data = conn.recv(1024) # 最大接收的数据量为1024Bytes,收到的是bytes类型,无穷增大没有意义,而且再大不能大过内存的大小,要像水流一样一点一点接收
print('客户端的Ip和端口:',client_addr)
conn.send(data.upper())

"""
关闭电话连接coon
因为accept建立的连接请求占的是python应用程序的内存空间所占
但这个连接请求是操作系统帮我们维护者tcp的双向连接,所以要考虑回收操作系统资源
"""
conn.close()

# 关机(可选操作)
phone.close()

Cliente:

import socket

#1、买手机
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 流式协议=》tcp协议

#2、拨通服务端电话
phone.connect(('127.0.0.1',8080))

#3、通信
import time
time.sleep(10)
phone.send('hello 噗噗'.encode('utf-8'))
data=phone.recv(1024)
print(data.decode('utf-8'))

#4、关闭连接(必选的回收资源的操作)
phone.close()

Lazo de comunicación plus

El error existe:

  1. Enviar vacío no es un problema, pero la recepción vacía provocará el bloqueo

  Tanto el envío como la recepción no están operando para la otra parte. Todos están enviando llamadas al sistema para llamar a sus propias operaciones desde su propia caché. De hecho, todos están en su propia caché y son administrados por su propio sistema operativo, pero eventualmente llegarán a la otra parte. Si se envía, se llama desde la memoria caché. Si no hay nada, no se hará, pero si se recibe, se encontrará en la memoria caché. Si no es así, esperará hasta que sepa que hay datos en la memoria caché.

  2. Si el cliente se termina por la fuerza, el servidor también colapsará y la situación del servidor en diferentes sistemas es diferente, hay dos tipos:

    Windows informará directamente un error: el host remoto forzado a cerrar un enlace existente

    Unix: entrará en un bucle sin fin

  Como el enlace es bidireccional, cuando el cliente realiza la operación de entrada, se mantiene un enlace entre el servidor y el cliente. El cliente espera perder y el servidor espera recibir en circunstancias normales, pero ahora El cliente termina por la fuerza y ​​el servidor piensa que la conexión es normal, por lo que se produce un error.

Versión de servidor mejorada:

import socket

# 1、买手机
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 流式协议=》tcp协议

# 2、绑定手机卡
phone.bind(('127.0.0.1',8080)) # 0-65535, 1024以前的都被系统保留使用

# 3、开机
phone.listen(5)
print('服务端启动完成,监听地址为:%s:%s' %('127.0.0.1',8080))

# 4、等待电话连接请求:拿到电话连接conn
conn,client_addr=phone.accept()

# 5、通信:收\发消息
while True:
    try:
        data=conn.recv(1024) # 最大接收的数据量为1024Bytes,收到的是bytes类型
        if len(data) == 0:
            # 在unix系统洗,一旦data收到的是空
            # 意味着是一种异常的行为:客户度非法断开了链接
            break
        print("客户端发来的消息:",data.decode('utf-8'))
        conn.send(data.upper())
    except Exception:
        # 针对windows系统
        break

# 6、关闭电话连接conn(必选的回收资源的操作)
conn.close()

# 7、关机(可选操作)
phone.close()

Versión mejorada del cliente.

import socket

#1、买手机
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 流式协议=》tcp协议

#2、拨通服务端电话
phone.connect(('127.0.0.1',8080))

#3、通信
while True:
    msg=input("输入要发送的消息>>>: ").strip() #msg=''
    if len(msg) == 0:continue
    phone.send(msg.encode('utf-8'))
    print('======')
    data=phone.recv(1024)
    print(data.decode('utf-8'))

#4、关闭连接(必选的回收资源的操作)
phone.close()

Enlace de enlace más (resuelva problemas existentes)

Las características que debe cumplir el servidor:

  1. Proporcionar siempre servicios

  2. Proporcionar servicios al mismo tiempo.

Versión de servidor mejorada:

import socket

# 1、买手机
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 流式协议=》tcp协议

# 2、绑定手机卡
phone.bind(('127.0.0.1',8080)) # 0-65535, 1024以前的都被系统保留使用

# 3、开机
phone.listen(5) # 5指的是半连接池的大小
print('服务端启动完成,监听地址为:%s:%s' %('127.0.0.1',8080))

# 4、等待电话连接请求:拿到电话连接conn
# 加上链接循环
while True:
    conn,client_addr=phone.accept()

    # 5、通信:收\发消息
    while True:
        try:
            data=conn.recv(1024) # 最大接收的数据量为1024Bytes,收到的是bytes类型
            if len(data) == 0:
                # 在unix系统洗,一旦data收到的是空
                # 意味着是一种异常的行为:客户度非法断开了链接
                break
            print("客户端发来的消息:",data.decode('utf-8'))
            conn.send(data.upper())
        except Exception:
            # 针对windows系统
            break

    # 6、关闭电话连接conn(必选的回收资源的操作)
    conn.close()

# 7、关机(可选操作)这样就没有用了
phone.close()

El problema:

Algunos estudiantes pueden encontrarse al reiniciar el servidor

img

Esto se debe a que su servidor todavía tiene cuatro estados agitados time_wait que ocupan la dirección (si no comprende, estudie 1.tcp tres apretón de manos, cuatro agitados 2.syn ataque de inundación 3. Habrá una gran concurrencia de servidores El método de optimización del estado time_wait)

Solución:

Método uno

#加入一条socket配置,重用ip和端口

phone=socket(AF_INET,SOCK_STREAM)
phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
phone.bind(('127.0.0.1',8080))

Método dos

发现系统存在大量TIME_WAIT状态的连接,通过调整linux内核参数解决,
vi /etc/sysctl.conf

编辑文件,加入以下内容:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30

然后执行 /sbin/sysctl -p 让参数生效。

net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;

net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;

net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。

net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间

Método tres:

  ¡Cambie el número de puerto, recuerde cambiar el cliente y el servidor!

Tres zócalos basados ​​en UDP

  • cliente udp

    import socket
    
    client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 流式协议=》tcp协议
    
    while True:
        msg=input('>>>: ').strip()
        client.sendto(msg.encode('utf-8'),('127.0.0.1',8080))
        res=client.recvfrom(1024)
        print(res)
    
    client.close()
    
  • servidor udp

    import socket
    
    server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 数据报协议=》udp协议
    
    server.bind(('127.0.0.1',8080))
    
    while True:
        data,client_addr=server.recvfrom(1024)
        server.sendto(data.upper(),client_addr)
    
    server.close()
    

Supongo que te gusta

Origin www.cnblogs.com/Lance-WJ/p/12741880.html
Recomendado
Clasificación