当客户端不正常退出时,如何检测服务器上 Python aiohttp Web 套接字的关闭

2024-04-17

我有一个简单的命令和控制服务器server.py(完全不安全 - 不要使用),被动客户端update_client.py和另一个可以发送命令的客户端update_commander.py。有一个 http 端点http://0.0.0.0:8080/ http://0.0.0.0:8080/其中列出了已连接的客户端。当。。。的时候update_commander.py脚本退出其客户端后会得到正确清理。什么时候update_client.py断开连接,服务器没有注意到断开连接,并在服务器发送进一步消息后继续连接update_commander.py我收到一个错误socket.send() raised exception.对于每个 Ghost 客户端连接。清理代码标记为### CLEANUP CODE ###

我觉得我应该做的是,当我尝试发送到套接字但没有引发异常,只是向标准输出发送一条消息时捕获错误。

服务器.py

import uuid
import asyncio
import aiohttp
from aiohttp import web


class Client(object):

    def __init__(self):
        self.websocket = None
        self.name = None


class ClientList(web.View):
    async def get(self):

        clients = self.request.app['clients']

        client_list = [client.name for name, client in clients.items()]
        txt = ", ".join(client_list)
        return web.Response(text=txt)


class WebSocket(web.View):
    async def get(self):
        ws = web.WebSocketResponse()
        await ws.prepare(self.request)

        # session = await get_session(self.request)
        # user = User(self.request.db, {'id': session.get('user')})
        # login = await user.get_login()
        login = str(uuid.uuid4())
        client = Client()
        client.name = login
        client.websocket = ws
        self.request.app['clients'][client.name] = client
        print('%s connected.' % login)

        for _ws in [c.websocket for name, c in self.request.app['clients'].items()]:
            _ws.send_str('%s joined' % login)

        async for msg in ws:
            if msg.tp == aiohttp.WSMsgType.text:
                if msg.data == 'close':
                    await ws.close()
                else:
                    # do something here like save it
                    print('%s sent: %s' % (login, msg.data))
                    # Send message to all clients other clients
                    for _ws in [c.websocket for name, c in self.request.app['clients'].items()]:
                        try:
                            _ws.send_str('(%s) %s' % (login, msg.data))
                            asyncio.sleep(0)
                        except:
                            print(ws.exception())
            elif msg.tp == aiohttp.WSMsgType.error:
                print('ws connection closed with exception %s' % ws.exception())

        ### CLEANUP CODE ###
        await client.websocket.close()
        del self.request.app['clients'][client.name]
        for _ws in [c.websocket for name, c in self.request.app['clients'].items()]:
            _ws.send_str('%s disconected' % login)
        print('%s disconnected' % login)

        return ws


routes = [
    ('GET', '/',        ClientList,  'main'),
    ('GET', '/ws',      WebSocket,   'chat'),
]

app = web.Application()

for route in routes:
    app.router.add_route(route[0], route[1], route[2], name=route[3])
app['clients'] = {}
web.run_app(app)

指挥官.py

import asyncio
import aiohttp
import os

HOST = os.getenv('HOST', '0.0.0.0')
PORT = int(os.getenv('PORT', 8080))

URL = f'http://{HOST}:{PORT}/ws'


async def main():
    session = aiohttp.ClientSession()
    async with session.ws_connect(URL) as ws:

        await prompt_and_send(ws)
        async for msg in ws:
            print('Message received from server:', msg.data)
            await prompt_and_send(ws)

            if msg.type in (aiohttp.WSMsgType.CLOSED,
                            aiohttp.WSMsgType.ERROR):
                break


async def prompt_and_send(ws):
    new_msg_to_send = input('Type a message to send to the server: ')
    if new_msg_to_send == 'exit':
        print('Exiting!')
        raise SystemExit(0)
    await ws.send_str(new_msg_to_send)


if __name__ == '__main__':
    print('Type "exit" to quit')
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

客户端.py

import aiohttp
import asyncio
import os
import time
import logging
import sys

logging.basicConfig(
        level=logging.DEBUG,

        format='%(name)s: %(message)s',
        stream=sys.stderr,
        )
log = logging.getLogger('main')

HOST = os.getenv('HOST', '0.0.0.0')
PORT = int(os.getenv('PORT', 8080))

URL = f'http://{HOST}:{PORT}/ws'


async def callback(msg):

    if msg == 'time':
        #ws.send_str(time.time())
        print(msg)
    else:
        print(msg)

async def main():
    session = aiohttp.ClientSession()
    async with session.ws_connect(URL) as ws:
        async for msg in ws:
            if msg.type == aiohttp.WSMsgType.TEXT:
                await callback(msg.data)
            elif msg.type == aiohttp.WSMsgType.CLOSED:
                print("CLOSED")
                break
            elif msg.type == aiohttp.WSMsgType.ERROR:
                print("error")
                break

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    tasks = loop.run_until_complete(main())
    try:
        loop.run_forever()
    except KeyboardInterrupt as e:
        print("Caught keyboard interrupt. Canceling tasks...")
        tasks.cancel()
        loop.run_forever()
    finally:
        log.debug('closing server')
        loop.run_until_complete(loop.shutdown_asyncgens())  # python 3.6 only
        log.debug('closing event loop')
        loop.close()

因此,有时只需向其他人阐明问题就会有所帮助。我通过包裹整个来修复它async for msg in ws:尝试/终于这样。

    try:
        async for msg in ws:
            if msg.tp == aiohttp.WSMsgType.text:
                if msg.data == 'close':
                    await ws.close()
                else:
                    # do something here like save it
                    print('%s sent: %s' % (login, msg.data))
                    # Send message to all clients other clients
                    for _ws in [c.websocket for name, c in self.request.app['clients'].items()]:

                            _ws.send_str('(%s) %s' % (login, msg.data))
            elif msg.tp == aiohttp.WSMsgType.error:
                print('ws connection closed with exception %s' % ws.exception())
    finally:
        await client.websocket.close()
        del self.request.app['clients'][client.name]
        for _ws in [c.websocket for name, c in self.request.app['clients'].items()]:
            _ws.send_str('%s disconected' % login)
        print('%s disconnected' % login)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当客户端不正常退出时,如何检测服务器上 Python aiohttp Web 套接字的关闭 的相关文章

随机推荐