title: How can I detect the closure of an Python aiohttp web socket on the server when the client exits uncleanly
body: <p>I have a simple command and control server <code>server.py</code> (completely insecure - don't use), a passive client <code>update_client.py</code> and another client that that can send commands <code>update_commander.py</code>. There is a http endpoint at <a href="http://0.0.0.0:8080/" rel="nofollow noreferrer">http://0.0.0.0:8080/</a> which lists the connected clients. When the <code>update_commander.py</code> script exits its client gets cleaned up properly. When  <code>update_client.py</code> disconnects the server doesn't notice the disconnection and on upon further messages sent by the <code>update_commander.py</code> I get an error <code>socket.send() raised exception.</code> for each ghost client connection. The cleanup code is marked by <code>### CLEANUP CODE ###</code> </p>
  
  <p>What I feel I should do is to catch a error when I try to send to the socket but no Exception is raised, just a message to stdout.</p>
  
  <p><strong>server.py</strong></p>
  
  <pre><code>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)
  </code></pre>
  
  <p><strong>commander.py</strong></p>
  
  <pre><code>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())
  </code></pre>
  
  <p><strong>client.py</strong></p>
  
  <pre><code>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()
  </code></pre>