aio-libs / aiohttp

Asynchronous HTTP client/server framework for asyncio and Python
https://docs.aiohttp.org
Other
14.98k stars 2k forks source link

WARNING asyncio.write : socket.send() raised exception. #3122

Open initzx opened 6 years ago

initzx commented 6 years ago

Long story short

ws.send_json() fails with this warning: WARNING -- asyncio.write : socket.send() raised exception. ws is from aiohttp.ClientSession().ws_connect()

Expected behaviour

An exception should have be raised along with a traceback message, but nothing happened.

Actual behaviour

It shows the warning and does nothing.

Steps to reproduce

You can see the code here As far as I know, there is no exact way of reproducing it. You just wait a bit for the bug to appear, likely due to some connection errors along the way?

Your environment

aiohttp 3.3.2
Ubuntu 16.04 LTS

rmmr commented 6 years ago

Having the same issue in conjunction with this library: https://github.com/graphql-python/graphql-ws

I created the websocket as follows:

ws = web.WebSocketResponse(protocols=('graphql-ws',), heartbeat=5, autoping=True)

I did some debugging. And when closing the connection from the client, I found out that the heartbeat cb _pong_not_received was indeed called. However:

At aiohttp/web_ws.py:

def _pong_not_received(self):
        if self._req is not None and self._req.transport is not None:
            self._closed = True
            self._close_code = 1006
            self._exception = asyncio.TimeoutError()
            self._req.transport.close()

The if statement did not go through. The underlying transport was already None. The way I see it, the websocket property closed would never be set.

I did a small patch as follows:

@property
def closed(self):
    return self._closed or self._req is None or self._req.transport is None

This made everything work on my end. However I don't fully understand all consequences of doing this, and the reasoning behind the current aproach.

initzx commented 6 years ago

@adaptivdesign In my case the connection was never meant to be closed. I have never requested the websocket to be closed so I do not understand why it would want to close itself on its own?

rmmr commented 6 years ago

@initzx I assumed the socket.send() exception was because of a broken connection. I was having the same error, once a client disconnected, in which case the websocket did not close on the server end, and resulted in a socket.send() exception.

jimbrookes commented 6 years ago

The logic in the patch helped me, thanks @adaptivdesign.

In my case I was able to easily reproduce this by having a mobile device(running android) connect to the websocket. Background the browser after the connection and wait a short while, this exception message would show in the logs but can not be caught.

schlamar commented 5 years ago

Any update or a workaround without patching aiohttp directly?

schlamar commented 5 years ago

BTW, this is probably a duplicate of #2309

risha700 commented 4 years ago

Asyncio warning: socket.send() exception. I believe it is not aiohttp issue, eventually another frameworks suffer from the same infinite loop exception. after investigation:

  1. It is the way you feed your data through the socket especially large bits of binary
  2. server timeout to close the socket is really sensitive and I believe in asyncio is preset to 5 sec. during this five seconds if the socket establish new connection along with big flow of data.
  3. I tried to lower the timeout or set reusable port and host or catch conn_lost from streamresponse along with exception handling still getting the same result.

finally work around try asyncio.shield() that let the task complete and serve another one 'Gracefully'

    async def stream_vid(self, request):
        response = web.StreamResponse()
        response.content_type = "multipart/x-mixed-replace;boundary=frame"
        await response.prepare(request)
        try:
            while self.sock_opened:
                data = self.raw_frame
                if not data:
                    break
                await asyncio.shield(response.write(b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n' + data + b'\r\n\r\n'))
        except BaseException as e:
            print("asyncio socket.send() raised exception infinite no more %s" % e)
            pass

        finally:
            print(response.status)
            await response.write_eof()

works like a charm further readings https://gitlab.com/pgjones/quart/issues/240

crusaderky commented 4 years ago

I've stumbled on the same issue.

  1. open aiohttp server websocket with autoping=True, heartbeat=5
  2. open aiohttp client websocket with the same settings
  3. send some small-ish data through client -> server and server -> client
  4. starve the client's asyncio event loop for several minutes with blocking load
  5. send a burst of 100 messages worth 100kb each from client to server
  6. observe the message 'socket.send() raised exception.' printed on the client's console a hundred-ish times.

Setting the heartbeat on the server side to be safely longer than DOUBLE the time the client is starved (because aiohttp hardcodes self._pong_heartbeat = heartbeat / 2.0) works around the problem.

In my application, I can reproduce the issue 100% of the times. I tried hard to replicate it in a POC, but failed - in the POC, the client is raising sane exceptions instead, either on send() (if the message is enough to saturate the output buffer) or on receive() (for small messages).

redradist commented 1 year ago

@initzx @asvetlov @crusaderky Is there a workaround for this issue ? I have faced with the same issue and due to silent behavior some data was not sent to server from my clients ...

I am interesting in workaround on client-side, because server could be stopped randomly by some reason ...

Ericwonne commented 1 year ago

I wonder how we can use the try...except... to catch this error, or else this will not recover by itself even if the network becomes stable.

socket.send() raised exception.
socket.send() raised exception.
socket.send() raised exception.
socket.send() raised exception.
socket.send() raised exception.
socket.send() raised exception.
socket.send() raised exception.
socket.send() raised exception.
socket.send() raised exception.
socket.send() raised exception.
...
...
YTFarman9758 commented 2 months ago

app[worker.1]: [13-Jul-24 16:30:11 - WARNING] - asyncio - socket.send() raised exception.

Repo mein ye error ara he koi fixe karwa sakte hai..??

Dreamsorcerer commented 1 month ago

Looks like this is being worked on in cpython: https://github.com/python/cpython/pull/118960 So, hopefully will be resolved in an upcoming release.

bdraco commented 1 month ago

Looks like this is being worked on in cpython: python/cpython#118960 So, hopefully will be resolved in an upcoming release.

That fix looks reasonable. We might be able to get rid of cleanup_closed as well