Los websockets de Python realizan la comunicación entre el servidor y el cliente

Dirección del proyecto: https://github.com/aaugustin/websockets

Dirección del documento: https://websockets.readthedocs.io/en/stable/

1, enchufes web

WebSocket es un protocolo para la comunicación full-duplex en una única conexión TCP, lo que facilita el intercambio de datos entre el cliente y el servidor, lo que permite que el servidor envíe datos al cliente de forma activa. En la API de WebSocket, el navegador y el servidor solo necesitan completar un protocolo de enlace, y se puede establecer una conexión persistente entre los dos y se puede realizar una transmisión de datos bidireccional. Las características principales son las siguientes:

1) Basado en el protocolo TCP, la implementación del lado del servidor es relativamente fácil;

2) Tiene buena compatibilidad con el protocolo HTTP. Los puertos predeterminados también son 80 y 443, y la fase de negociación utiliza el protocolo HTTP, por lo que no es fácil protegerse durante la negociación y puede pasar a través de varios servidores proxy HTTP;

3) El formato de datos es relativamente ligero, la sobrecarga de rendimiento es pequeña y la comunicación es eficiente;

4) Puede enviar texto o datos binarios;

5) No hay restricción del mismo origen, el cliente puede comunicarse con cualquier servidor;

6) El identificador del protocolo es ws (o wss si está encriptado), y la URL del servidor es la URL.

Instalación del paquete: pip install websockets

2. Funciones comunes de websockets

servir: utilizado en el lado del servidor, esperando la conexión del cliente. Si la conexión es exitosa, se devuelve un websocket.

connect: se utiliza en el lado del cliente para establecer una conexión.

enviar: enviar datos, tanto el servidor como el cliente pueden usarlo.

recv: Recibir datos, tanto el servidor como el cliente pueden usarlo.

close: cierra la conexión, tanto el servidor como el cliente pueden usarla.

3. Un sencillo programa de prueba servidor-cliente

Código del lado del servidor:

websockets.serve(), utilizado para esperar la conexión del cliente. Si el cliente llama al método de conexión con éxito, se devuelve un websocket.

import asyncio
import websockets

IP_ADDR = "127.0.0.1"
IP_PORT = "8888"

async def server_hands(websocket):
    """ 握手,通过接收hello,发送"123"来进行双方的握手
    """
    while True:
        recv_text = await websocket.recv()
        print("recv_text=" + recv_text)
        if recv_text == "hello":
            print("connected success")
            await websocket.send("123")
            return True
        else:
            await websocket.send("connected fail")

async def server_recv(websocket):
    """ 接收从客户端发来的消息并处理,再返给客户端ok。
    """
    while True:
        try:
            recv_text = await websocket.recv()
            print("recv:", recv_text)
            await websocket.send("ok!!!")
        except websockets.ConnectionClosed as e:
            # 客户端关闭连接,跳出对客户端的读取,结束函数
            print(e.code)
            await asyncio.sleep(0.01)
            break

async def server_run(websocket, path):
    """ 握手并且接收数据
    :param websocket:
    :param path:
    """
    # 下面两个函数顺序执行
    await server_hands(websocket)  # 握手

    await server_recv(websocket)  # 接收客户端消息并处理

# main function
if __name__ == '__main__':
    print("======server main begin======")
    server = websockets.serve(server_run, IP_ADDR, IP_PORT)  # 服务器端起server
    asyncio.get_event_loop().run_until_complete(server)  # 事件循环中调用
    asyncio.get_event_loop().run_forever()  # 一直运行

Código del lado del cliente:

Establezca comunicación con el servicio llamando al método de conexión.

Al enviar "hola" y recibir "123" para verificar el protocolo de enlace entre las dos partes, solo cuando el protocolo de enlace es exitoso se pueden enviar y recibir los datos.

import asyncio
import websockets

IP_ADDR = "127.0.0.1"
IP_PORT = "8888"

async def client_hands(websocket):
    """ 握手,通过发送hello,接收"123"来进行双方的握手。
        若成功,则跳出循环,结束函数;若失败,则继续发送hello.
    """
    while True:
        await websocket.send("hello")
        response_str = await websocket.recv()
        if "123" in response_str:
            print("握手成功")
            return True

async def client_send(websocket):
    """ 向服务器端发送消息
    """
    while True:
        input_text = input("input text: ")
        if input_text == "exit":
            print(f'"exit", bye!')
            await websocket.close(reason="exit")  # 关闭本次连接
            return False

        # 发送数据并打印服务端返回结果
        await websocket.send(input_text)
        recv_text = await websocket.recv()
        print(f"{recv_text}")

async def client_run():
    """ 进行websocket连接
    """
    server_url = "ws://" + IP_ADDR + ":" + IP_PORT
    print("websockets server url: ", server_url)
    try:
        async with websockets.connect(server_url) as websocket:
            # 下面两行同步进行
            await client_hands(websocket)  # 握手

            await client_send(websocket)  # 发数据
    except ConnectionRefusedError as e:
        # 服务端未启动,或连接失败时退出.
        print("e:", e)
        return

# main function
if __name__ == '__main__':
    print("======client main begin======")
    asyncio.get_event_loop().run_until_complete(client_run())  # 等价于asyncio.run(client_run())

resultado de la operación:

Después de que se inicia el servidor, el cliente ingresa 111 y sale en secuencia.

Servidor:

python server_1.py
======server main begin======
recv_text=hello
connected success
recv: 111
1000

cliente:

>python client_1.py
======client main begin======
websockets server url:  ws://127.0.0.1:8888
握手成功
input text: 111
ok!!!
input text: exit
"exit", bye!

4. Mecanismo de reconexión

En el ejemplo anterior, después de conectarse por un período de tiempo, se desconectará automáticamente. ¡Se producirá un error cuando la solicitud se envíe de nuevo! Por lo tanto, realice mejoras en el código del cliente. Lo primero que debemos saber es:

1) Cuando el servidor se niega a conectarse (sin abrir ws), el cliente arrojará un error ConnectionRefusedError.

2) Cuando el servidor cierra ws en la conexión (ya sea que se cierre normalmente o de forma anormal), se generará un error ConnectionClosed (una clase de error en websockets).

4.1 Reconectarse después de detectar una excepción

Consulte el ejemplo en https://sakina.blog.csdn.net/article/details/108090049 para volver a conectarse después de detectar las dos excepciones anteriores.

import asyncio
import websockets as ws
from websockets import ConnectionClosed

count = 0

async def hello():
    uri = "ws://localhost:8765"

    while True:
        try:
            async with ws.connect(uri) as websocket:
                await websocket.send('start')
                while True:
                    try:
                        await websocket.recv()
                    except ConnectionClosed as e:
                        print(e.code)
                        if e.code == 1006:
                            print('restart')
                            await asyncio.sleep(2)
                            break
        except ConnectionRefusedError as e:
            print(e)
            global count
            if count == 10: 
                return
            count += 1
            await asyncio.sleep(2)


asyncio.get_event_loop().run_until_complete(hello())

En el corazón de este código hay dos while s:

La función de la primera capa mientras es ciclar el contexto de conexión ws. Cuando el servidor se niega a conectarse, se lanzará un ConnectionRefusedError. Reintentamos cada 2 segundos, hasta 10 veces.

La función de la segunda capa de while es garantizar que la conexión ws esté siempre en el estado de recepción (conexión larga). Cuando el servidor cierra ws, se lanzará ConnectionClosed. Generalmente, recibiremos 1000 códigos de cierre normales y Código de cierre de excepción de error interno del servidor 1006 Dos, en el código anterior, cuando recibimos el código de cierre 1006 anormal, saldremos del ciclo while, cerrando automáticamente el contexto de conexión ws y creando una nueva conexión de contexto ws.

Nota: en la segunda capa, no puede usar continuar para omitir este ciclo, y la conexión de contexto ws debe reconstruirse; de ​​lo contrario, la conexión ws siempre está cerrada.

4.2 Tareas programadas

Si queremos agregar una tarea programada, como un latido personalizado, debemos crear una tarea asincrónica. La función de la tarea es la siguiente:

async def ping(ws):
    while True:
        try:
            await ws.send('ping')
            await asyncio.sleep(10)
        except:
            break

Luego agregue tareas de ejecución de tareas asincrónicas en el proceso de contexto ws.

async with ws.connect(uri) as websocket:
    await websocket.send('start')

    asyncio.create_task(ping(websocket))

    while True:
        try:
            await websocket.recv()
        except ConnectionClosed as e:
            print(e.code)
            if e.code == 1006:
                print('restart')
                await asyncio.sleep(2)
                break

La función ping() es nuestro ping de latido personalizado, que enviará un mensaje de ping al servidor cada 10 segundos.

Independientemente de si el servidor cierra el ws o la conexión del ws no tiene éxito, esta tarea informará un error porque el ws no está disponible. Podemos romper el ciclo while, para que la tarea se ejecute. Cuando el ws se establezca con éxito nuevamente, el nueva tarea de sincronización de tareas se activará de nuevo.

4.3 Implementación de reconexión de código de prueba

Soporte: reconectarse cuando el servidor no se inicia; el cliente sale activamente; reconectarse cuando el servidor es anormal.

import asyncio
import websockets
from enum import Enum, unique

@unique
class WSStatus(Enum):
    Exit = 1  # 用户退出
    Interrupt = 2  # 连接中断
    Error = 3  # 程序内部错误
    Success = 4  # 调用成功
    Fail = 5  # 调用失败

IP_ADDR = "127.0.0.1"
IP_PORT = "8888"

async def client_hands(websocket):
    """ 握手,通过发送hello,接收"123"来进行双方的握手。
        若成功,则跳出循环,结束函数;若失败,则继续发送hello.
    """
    while True:
        await websocket.send("hello")
        response_str = await websocket.recv()
        if "123" in response_str:
            print("握手成功")
            return WSStatus.Success

async def client_send(websocket):
    """ 向服务器端发送消息
    """
    input_text = input("input text: ")
    if input_text == "exit":
        print(f'"exit", bye!')
        await websocket.close(reason="exit")  # 关闭本次连接
        return WSStatus.Exit

    # 发送数据并打印服务端返回结果
    r = await websocket.send(input_text)
    print("r:", r)
    recv_text = await websocket.recv()
    print(f"{recv_text}")
    return WSStatus.Success

async def client_run():
    """ 进行websocket连接
    """
    con_count = 0
    MAX_RECONNECT_COUNT = 10  # 定义最大重连次数
    server_url = "ws://" + IP_ADDR + ":" + IP_PORT
    print("websockets server url: ", server_url)
    while True:
        try:
            async with websockets.connect(server_url) as websocket:
                # 下面两行同步进行
                await client_hands(websocket)  # 握手
                r = None
                while True:
                    try:
                        r = await client_send(websocket)  # 发数据
                        # print("r:", r)
                        if r == WSStatus.Exit:  # 程序内部正常退出
                            break
                    except websockets.ConnectionClosed as e:
                        print(e.code)
                        if e.code == 1006:
                            print('restart')
                            await asyncio.sleep(0.1)
                            break
                if r == WSStatus.Exit:  # 程序内部正常退出
                    break

        except ConnectionRefusedError as e:
            # 服务端未启动,或连接失败时退出.
            print("e:", e)
            if con_count == MAX_RECONNECT_COUNT:
                return

            con_count += 1
            print(f"reconnect {con_count} times...")
            await asyncio.sleep(0.01)

# main function
if __name__ == '__main__':
    print("======client main begin======")
    asyncio.get_event_loop().run_until_complete(client_run())  # 等价于asyncio.run(client_run())

5. Cifrado, agregar encabezados de llamada

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36',
    'Origin': 'https://live.bilibili.com',
    'Connection': 'Upgrade',
    'Accept-Language': 'zh-CN,zh;q=0.9',
}

...

sync with websockets.connect(self.wss_url, 
                extra_headers=headers
                ) as websocket:

referencia:

1. El uso de python websockets para realizar la comunicación entre el servidor y el cliente

https://blog.csdn.net/liranke/article/details/120533682

2. Algunos hoyos de conexión wss

https://blog.csdn.net/zouzhe121/article/details/103122532

3. Código de caso de prueba

https://blog.csdn.net/liranke/article/details/120533682

4. Python websockets comunicación full-duplex

https://blog.csdn.net/qq_42195302/article/details/120150438

5. Introducción simple a la biblioteca de websockets asincrónicos de alto rendimiento de Python (incluido el mecanismo de reconexión y las tareas de temporización)

https://sakina.blog.csdn.net/article/details/108090049

6. Uso de python | biblioteca websockets (aumentar la llamada de encabezados)

https://www.cnblogs.com/Mz1-rc/p/16472294.html

Supongo que te gusta

Origin blog.csdn.net/weixin_34910922/article/details/128794946
Recomendado
Clasificación