I'm making a proxy which sits between the browser and the web. There's a snippet of code I can't seem to get to work.
#send request to web server
web_client.send(request)
#signal client is done with sending
web_client.shutdown(1)
If I use shutdown(1), the proxy has a great improvement in performance and speed.
However, some web servers do not send responses if I use shutdown. Console output:
request sent to host wix.com
got response packet of len 0
got response packet of len 0
breaking loop
and the browser displays
The connection was reset
The connection to the server was reset while the page was loading.
However, if I remove shutdown(1), there are no problems of sort. Console output:
got response packet of len 1388
got response packet of len 1388
got response packet of len 1388
got response packet of len 989
got response packet of len 0
got response packet of len 0
breaking loop
and the browser normally displays the website.
Why is this happening? This is only happening on certain hosts.
From https://docs.python.org/2/library/socket.html#socket.socket.shutdown
Depending on the platform, shutting down one half of the connection
can also close the opposite half (e.g. on Mac OS X, shutdown(SHUT_WR)
does not allow further reads on the other end of the connection)
This may not be the problem because you say that only some web servers are affected, but is your proxy running on Mac OS X?
TCP/IP stack will do graceful connection close only if there is no pending data to be sent on the socket. send completion indicates only the data is pushed into the kernel buffer & ready for sending. Here, shutdown is invoked immediately after send while there is some send data pending in the TCP stack. So TCP stack sends reset to the other end as it decides the application doesn't wish to complete sending the process. To do a graceful connection close, invoke select on the socket & wait for socket to be writable which means all the data is pushed out the stack. Then invoke shutdown & close the socket.
Related
I'm implementing a file transfer protocol with the following use case:
The server sends the file chunk by chunk inside several frames.
The client might cancel the transfer: for this, it sends a message and disconnects at TCP level.
What happened in that case on server side (Python running on Windows) is that I catch a ConnectionResetException (this is normal, the client has disconnected the socket) while sending the data to the client. I would want to read the latest data sent by the client (the message used to abort the call), but calling mysocket.recv() still raises a ConnectionResetException.
With a wireshark capture, I can clearly see that the message was properly sent by the client prior to TCP disonnection.
Any idea floks? Thanks!
VR
In order to understand what to do about this situation, you need to understand how a TCP connection is closed (see, e.g. this) and how the socket API relates to a clean shutdown (without fail, see this).
Your client is most likely calling close to terminate the connection. The problem with this is that there may be unread data in the socket receive queue or data arriving shortly from the other end that you will no longer be able to read, which is basically an error condition. To signal to the other end that data sent cannot be delivered to the receiving application, a reset is sent (well, technically, "SHOULD be sent" as per the RFC) and the TCP connection is abnormally terminated.
You might think that enabling SO_LINGER will help (many, many bits have been spilt over this so I won't elaborate further), but it won't solve the problem of unread data by the client causing the reset.
The client needs to instead call shutdown(SHUT_WR) to indicate that it is done sending, and then continue to call recv() until it reads 0 bytes indicating the other side is done sending. You may then call close().
Note that the Python 2 socket documentation states that
Depending on the platform, shutting down one half of the connection can also close the opposite half (e.g. on Mac OS X, shutdown(SHUT_WR) does not allow further reads on the other end of the connection).
This sounds like a bug to me. To get around this, you would have to send your cancel message, then keep reading until you get 0 bytes so that you know the server received the cancel message. You may then close the socket.
The Python 3.8 docs make no such disclaimer.
In python, tcp connect returns success even though the connect request is in queue at server end. Is there any way to know at client whether accept happened or in queue at server?
The problem is not related to Python but is caused by the underlying socket machinery that does its best to hide low level network events from the program. The best I can imagine would be to try a higher level protocol handshake (send a hello string and set a timeout for receiving the answer) but it would make no difference between the following problem:
connection is queued on peer and still not accepted
connection has been accepted, but for any other reason the server could not process it in allocated time
(only if timeout is very short) congestion on machines (including sender) and network added a delay greater that the timeout
My advice is simply that you do not even want to worry with such low level details. As problems can arise server side after the connection has been accepted, you will have to deal with possible higher level protocol errors, timeouts or connection loss. Just say that there is no difference between a timeout after connection has been accepted and a timeout to accept the connection.
If connect returns and there is no error, the TCP 3-Way Handshake has taken place successfully.
Client: connect sends a SYN (and blocks)
Server: (blocking on accept) sends a SYN,ACK
Client: connect sends an ACK
After 3, connectgives control back to you on the client side and accept also gives control back to the caller on the server side.
Of course, if the server is fully loaded, there is no guarantee that the wake-up of accept means actual processing of the request, but the fact that connect has woken up and returned with no error is a guarantee of having successfully set-up the TCP connection.
Packets can be sent.
For a good explanation see for example:
https://www.ibm.com/developerworks/aix/library/au-tcpsystemcalls/index.html
And head to the The 3-way TCP handshake section
I'm using websocket-client to connect to a websocket server. The server i'm connected to seems to be expecting me to ping it periodically or else it disconnects me.
Right when I connect to the server, it sends me this message:
0{"sid":"SomeID","upgrades":[],"pingInterval":25000,"pingTimeout":60000}
This seems to tell me the ping interval and ping timeout. I noticed that my websocket client is getting disconnected consistently ~1 minute and 25 seconds after I connect. And if you add up those numbers 60s + 25s you get 1 min and 25 seconds. So It seems I need to ping this server every so often so it doesn't disconnect me.
How do I ping the server? I tried ws.ping() but that didn't seem to exist. Do I need to send data to the server in some format it's expecting? Or is there some built in ping command?
websocket.enableTrace(True)
ws = websocket.WebSocketApp("wss://socket.serverssite.com/socket.io/?transport=websocket",
on_message=on_message,
on_error=on_error,
on_close=on_close)
ws.run_forever()
Note: I made a websocket client in node (not python) and it doesn't get closed after 1 min 25 seconds. So It seems that has some built in ping that this python websocket client does not...
------------------------Edit-------------------------
Tried setting ping_interval and ping_timeout, but it's still closing:
2017-11-06 12:49:14.340037--------------------- Doing stuff
2017-11-06 12:49:14.340309--------------------- Doing stuff
send: '\x89\x80\\xd9\xc4\xdd'
2017-11-06 12:49:19.341680--------------------- Doing stuff
2017-11-06 12:49:19.341958--------------------- Doing stuff
send: '\x89\x80\xd9\x06\xef\xa8'
2017-11-06 12:49:24.343426--------------------- Doing stuff
2017-11-06 12:49:24.343769--------------------- Doing stuff
send: "\x89\x80\xe6\x92'\xb8"
send: '\x88\x823\xfb$\xd10\x13'
closed (Here is where the closed method gets called, the server shut me down)
From the source code -
def run_forever(self, sockopt=None, sslopt=None,
ping_interval=0, ping_timeout=None,
http_proxy_host=None, http_proxy_port=None,
http_no_proxy=None, http_proxy_auth=None,
skip_utf8_validation=False,
host=None, origin=None)
Specifying the ping_interval should work for you.
ws.run_forever(ping_interval=70, ping_timeout=10)
I had a similar problem and solved it by pinging the server with 2. I was using the asyncio and websockets libraries and just used the following to prevent the websocket from closing:
await ws.send('2')
Using the standard websocket library will be something similar.
Full explanation
The server is socket.io This means you need to send a ping message of 2 to the server within the pingTimeout. From socket.io:
Encoding - Packet
An encoded packet can be UTF-8 string or binary data. The packet
encoding format for a string is as follows
...
0 open Sent from the server when a new transport is opened (recheck)
1 close Request the close of this transport but does not shutdown the
connection itself.
2 ping
Sent by the client. Server should answer with a pong packet containing
the same data
...
Timeouts
The client must use the pingTimeout and the pingInterval sent as part
of the handshake (with the open packet) to determine whether the
server is unresponsive.
The client sends a ping packet. If no packet type is received within pingTimeout, the client considers the socket disconnected. If a pong
packet is actually received, the client will wait pingInterval before
sending a ping packet again.
Since the two values are shared between the server and the client, the
server will also be able to detect whether the client becomes
unresponsive when it does not receive any data within pingTimeout +
pingInterval.
I'm going crazy writing a little socket server in python. Everything was working fine, but I noticed that in the case where the client just disappears, the server can't tell. I simulate this by pulling the ethernet cable between the client and server, close the client, then plug the cable back in. The server never hears that the client disconnected and will wait forever, never allowing more clients to connect.
I figured I'd solve this by adding a timeout to the read loop so that it would try and read every 10 seconds. I thought maybe if it tried to read from the socket it would notice the client was missing. But then I realized there really is no way for the server to know that.
So I added a heartbeat. If the server goes 10 seconds without reading, it will send data to the client. However, even this is successful (meaning doesn't throw any kind of exception). So I am able to both read and write to a client that isn't there any more. Is there any way to know that the client is gone without implementing some kind of challenge/response protocol between the client and server? That would be a breaking change in this case and I'd like to avoid it.
Here is the core of my code for this:
def _loop(self):
command = ""
while True:
socket, address = self._listen_socket.accept()
self._socket = socket
self._socket.settimeout(10)
socket.sendall("Welcome\r\n\r\n")
while True:
try:
data = socket.recv(1)
except timeout: # Went 10 seconds without data
pass
except Exception as e: # Likely the client closed the connection
break
if data:
command = command + data
if data == "\n" or data == "\r":
if len(command.strip()) > 0:
self._parse_command(command.strip(), socket)
command = ""
if data == '\x08':
command = command[:-2]
else: # Timeout on read
try:
self._socket.sendall("event,heartbeat\r\n") # Send heartbeat
except:
self._socket.close()
break
The sendall for the heartbeat never throws an exception and the recv only throws a timeout (or another exception if the client properly closes the connection under normal circumstances).
Any ideas? Am I wrong that sending to a client that doesn't ACK should generate an exception eventually (I've tested for several minutes).
The behavior you are observing is the expected behavior for a TCP socket connection. In particular, in general the TCP stack has no way of knowing that an ethernet cable has been pulled or that the (now physically disconnected) remote client program has shut down; all it knows is that it has stopped receiving acknowledgement packets from the remote peer, and for all it knows the packets could just be getting dropped by an overloaded router somewhere and the issue will resolve itself momentarily. Given that, it does what TCP always does when its packets don't get acknowledged: it reduces its transmission rate and its number-of-packets-in-flight limit, and retransmits the unacknowledged packets in the hope that they will get through this time.
Assuming the server's socket has outgoing data pending, the TCP stack will eventually (i.e. after a few minutes) decide that no data has gone through for a long-enough time, and unilaterally close the connection. So if you're okay with a problem-detection time of a few minutes, the easiest way to avoid the zombie-connection problem is simply to be sure to periodically send a bit of heartbeat data over the TCP connection, as you described. When the TCP stack tries (and repeatedly fails) to get the outgoing data sent-and-acknowledged, that is what eventually will trigger it to close the connection.
If you want something quicker than that, you'll need to implement your own challenge/response system with timeouts (either over the TCP socket, or over a separate TCP socket, or over UDP), but note that in doing so you are likely to suffer from false positives yourself (e.g. you might end up severing a TCP connection that was not actually dead but only suffering from a temporary condition of lost packets due to congestion). Whether or not that's a worthwhile tradeoff depends on what sort of program you are writing. (Note also that UDP has its own issues, particularly if you want your system to work across firewalls, etc)
We have a server, written using tornado, which sends asynchronous messages to a client over websockets. In this case, a javascript app running in Chrome on a Mac. When the client is forcibly disconnected, in this case by putting the client to sleep, the server still thinks it is sending messages to the client. Additionally, when the client awakens from sleep, the messages are delivered in a burst.
What is the mechanism by which these messages are queued/buffered? Who is responsible? Why are they still delivered? Who is reconnecting the socket? My intuition is that even though websockets are not request/response like HTTP, they should still require ACK packets since they are built on TCP. Is this being done on purpose to make the protocol more robust to temporary drops in the mobile age?
Browsers may handle websocket client messages in a separate thread, which is not blocked by sleep.
Even if a thread of your custom application is not active, when you force it to sleep (like sleep(100)), TCP connection is not closed in this case. The socket handle is still managed by OS kernel and the TCP server still sends the messages until it reaches the TCP client's receive window overflow. And even after this an application on server side can still submit new messages successfully, which are buffered on TCP level on server side until TCP outgoing buffer is overflown. When outgoing buffer is full, an application should get error code on send request, like "no more space". I have not tried myself, but it should behave like this.
Try to close the client (terminate the process), you will see totally different picture - the server will notice disconnect.
Both cases, disconnect and overflow, are difficult to handle on server side for highly reliable scenarios. Disconnect case can be converted to overflow case (websocket server can buffer messages up to some limit on user space while client is being reconnected). However, there is no easy way to handle reliably overflow of transmit buffer limit. I see only one solution - propagate overflow error back to originator of the event, which raised the message, which has been discarded due to overflow.