-
-
Notifications
You must be signed in to change notification settings - Fork 30.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding a recvexactly() to socket.socket: receive exactly n bytes #41448
Comments
|
This patch is a first take at adding a recvall method If the MSG_WAITALL flag is available, the recvall If it is not available, it uses an internal loop Having this method makes Python code much simpler; Note: the patch hasn't been tested very well yet. (code is based on a separate extension module found |
|
Logged In: YES I like the feature (but see below). The patch is incomplete,
Furthermore, the patch is also wrong: if a later recv call A note on coding style: please omit the spaces after the while ( (bytes_got<total_size) && (n > 0) ) |
|
Irmen, do you want to update this patch for the current Python trunk, taking Martin's comments into account? |
|
Sure, I'll give it another go. I've not done any c-development for quite a while though, so I have to pick up the pieces and see how far I can get. Also, I don't have any compiler for Windows so maybe I'll need someone else to validate the patch on Windows for me, once I've got something together. |
|
Ok I've looked at it again and think I can build an acceptable patch this time. However there are 2 things that I'm not sure of:
|
|
Ok I think I've got the code and doc changes ready. I added a recvall and a recvall_into method to the socket module. Any partially received data in case of errors is returned to the application as part of the args for a new exception, socket.partialdataerror. Still need to work on some unit tests for these new methods. |
|
Just a couple comments:
Someone should do another review when there are unit tests. |
|
Currently if MSG_WAITALL is defined, recvall() just calls recv() internally with the extra flag. Maybe that isn't the smartest thing to do because it duplicates recv's behavior on errors. Which is: release the data and raise an error. Why C: this started out by making the (very) old patch that I once wrote for socketmodule.c up to date with the current codebase, and taking Martin's comments into account. |
|
@irmen if you do proceed with this it should be against the py3k trunk. |
|
I'm frankly not sure why this is useful. If you want a guaranteed read size you should use the buffered layer - i.e. socket.makefile(). No need to complicate the raw socket implementation. |
|
The patch uses the flag MSG_WAITALL for recv() if available. Extract of the manual page: It looks interesting, but it doesn't guarantee that you will always get exactly the expected size. You still have to call again recv() to get more data if a signal was received. Jean-Paul Calderone wrote:
sendall() is implemented in C while it would be possible to implement it in Python. The same rationale can be used on a large part of the stdlib :-) (The io module is implemented in Python in Python 2.6!) The C gives you a full control on the GIL, signal handle, and it might be faster. Antoine Pitrou wrote:
recvall() allows to easily fix existing code: just replace recv() with recvall(), no need to refactor code to call makefile() which has a different API (ex: read/recv, write/send). The addition is small and well defined. -- About the exception: asyncio.StreamReader.read_exactly() raises an IncompleteReadError which contains the read bytes and inherits from EOFError: see The following issue discussed the design on this exception in asyncio: http.client uses an IncompleteRead (which inherits from HTTPException): |
|
I created the patch about 5 years ago and in the meantime a few things have happened:
In other words, I will never follow up on my original C-based patch from 5 years ago. I do still like the idea of having a reliable recvall in the stdlib instead of having to code a page long one in my own code. |
Something else occurred since 5 years: the PEP-475 was accepted, it makes Python more reliable when it receives signals. If recv(WAIT_ALL) is interrupted by a signal and returns less bytes, we must call PyErr_CheckSignal(). If the signal handler raises an exception, drop read data and raises the exception. If the signal handler does not raise an exception, we now *must* retry recv(WAIT_ALL) (with a shorter length, to not read too much data). The IncompleteRead exception is still needed if the socket is closed before receiving the requested number of bytes. |
|
recvall.patch: implement socket.socket.recvall() in pure Python. It raises a new socket.IncompleteReadError (copied from asyncio) exception if the connection is closed before we got the expected number of bytes. The patch has unit tests, document the new method and the new exception. TODO: I don't like how the method handles timeout. The method must fail if it takes longer than socket.gettimeout() seconds, whereas currently the timeout is reset each time we got data from the server. If the idea of the new socket method is accepted, I will reimplement it in C. In C, it's more easy to implement the timeout as I want. In Python, the socket timeout cannot be changed temporary, because it would impact other threads which may use the same socket. I changed how socket.sendall() handle timeout in Python 3.5, it is now the maximum total duration to send all data. The timeout is no more reset each time we send a packet. Related discussion: See also the issue bpo-23236 which adds a timeout reset each time we get data to the asyncio read() method. It will be complementary to the existing "wait(read(), timeout)" timeout method, it's for a different use case. |
|
Oh, in fact recvall() is a bad name. The io module uses the "readall()" name to really read all content of a file, whereas "recvall(N)" here only read up to N bytes. It would be better to reuse the same name than asyncio, "readexactly(N)": asyncio and http.client already have their IncompleteRead exceptions. Maybe it would be time to add a builtin exception? |
|
I can’t say I’ve often wanted this kind of method for socket objects. I guess this would treat a zero-length message (e.g. UDP datagram) as end-of-stream. Maybe it would be more useful as a general function or method for RawIOBase (maybe also BufferedIOBase) streams. As for the exception, I have used the existing EOFError in the past for similar purposes. After all, an unexpected EOF error at a low level often means an incomplete or truncated data error at a higher level. |
|
This is almost 20 years old, it has been abandoned by the OP and I don't think anyone else is likely to pick it up. Marking as pending and will close it soon if nobody will object. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: