Hi all,
I'm currently trying to use a recv call on a network socket with the MSG_PEEK flag. According to the man 2 recv:
MSG_PEEK causes the receive operation to return data from the beginning of the receive queue without removing that data from the queue. Thus, a subsequent receive call will return the same data.
Yet this doesn't seem to be the case (at least with lwip). I implemented a test [1] that creates a connection from the test http client and subsequently calls recv(sock, buf, 1, MSG_PEEK) which always should yield 'G' into the buffer (the first character of the GET request). Yet the output looks as follows:
[init -> test-http_clnt] got IP address 10.0.2.15 [init -> test-http_clnt] Create new socket ... [init -> test-http_clnt] Connect to server ... [init -> test-http_clnt] Send request... [init -> test-libc_msg_peek] peek: G [init -> test-libc_msg_peek] peek: E [init -> test-libc_msg_peek] peek: T [init -> test-libc_msg_peek] peek: [init -> test-libc_msg_peek] peek: /
(this is just an extract but you get the point)
The problem is that this doesn't only account to the recv call but also any further read call will miss the first "peeked" bytes which completely defeats the purpose of MSG_PEEK.
According to the lwip_legacy port MSG_PEEK should be supported since 2007 and I'm not sure why it should stay broken such a long time.
2007-06-30 Frédéric Bernon
- sockets.h, sockets.c: Implement MSG_PEEK flag for recv/recvfrom functions.
As far as I can see I have used it correctly so I'm not sure why it isn't working. You can view the code at [1] and run the test with run/libc_msg_peek.
Regards, Johannes
[1]: https://github.com/jklmnn/genode/commit/b631a3068024748cc8443d1db4e30b35aae1...
Hello Johannes,
I've to admit that we never considered about supporting this (rather exotic) feature.
On Tue, Jun 19, 2018 at 03:52:16PM +0200, Johannes Kliemann wrote:
Yet this doesn't seem to be the case (at least with lwip). I implemented a test [1] that creates a connection from the test http client and subsequently calls recv(sock, buf, 1, MSG_PEEK) which always should yield 'G' into the buffer (the first character of the GET request). Yet the output looks as follows:
[init -> test-http_clnt] got IP address 10.0.2.15 [init -> test-http_clnt] Create new socket ... [init -> test-http_clnt] Connect to server ... [init -> test-http_clnt] Send request... [init -> test-libc_msg_peek] peek: G [init -> test-libc_msg_peek] peek: E [init -> test-libc_msg_peek] peek: T [init -> test-libc_msg_peek] peek: [init -> test-libc_msg_peek] peek: /
(this is just an extract but you get the point)
The problem is that this doesn't only account to the recv call but also any further read call will miss the first "peeked" bytes which completely defeats the purpose of MSG_PEEK.
According to the lwip_legacy port MSG_PEEK should be supported since 2007 and I'm not sure why it should stay broken such a long time.
2007-06-30 Frédéric Bernon
- sockets.h, sockets.c: Implement MSG_PEEK flag for recv/recvfrom functions.
As far as I can see I have used it correctly so I'm not sure why it isn't working. You can view the code at [1] and run the test with run/libc_msg_peek.
The reason for this behavior can be found in the libc_lwip library, which (as my statement above hints) does not translate the 'flags' parameter, which seems to be incompatibly defined in lwip and libc:
grep -rn define.*MSG_PEEK $(./tool/ports/current lwip_legacy)/include $(./tool/ports/current libc)/include .../contrib/lwip_legacy-7f5610426ed8c4c64903136122d9ba58836cdc89/include/lwip_legacy/lwip/sockets.h:157:#define MSG_PEEK 0x01 /* Peeks at an incoming message */ .../contrib/libc-5a5de5baab6e3ce23f24012a7604abf070fa990c/include/libc/sys/socket.h:450:#define MSG_PEEK 0x2 /* peek at incoming message */
The current migration to VFS-based socket operations does not include any support for recv flags too. Is this feature essential for your use case?
Regards
Hi Christian,
I've to admit that we never considered about supporting this (rather exotic) feature.
On Tue, Jun 19, 2018 at 03:52:16PM +0200, Johannes Kliemann wrote:
Yet this doesn't seem to be the case (at least with lwip). I implemented a test [1] that creates a connection from the test http client and subsequently calls recv(sock, buf, 1, MSG_PEEK) which always should yield 'G' into the buffer (the first character of the GET request). Yet the output looks as follows:
[init -> test-http_clnt] got IP address 10.0.2.15 [init -> test-http_clnt] Create new socket ... [init -> test-http_clnt] Connect to server ... [init -> test-http_clnt] Send request... [init -> test-libc_msg_peek] peek: G [init -> test-libc_msg_peek] peek: E [init -> test-libc_msg_peek] peek: T [init -> test-libc_msg_peek] peek: [init -> test-libc_msg_peek] peek: /
(this is just an extract but you get the point)
The problem is that this doesn't only account to the recv call but also any further read call will miss the first "peeked" bytes which completely defeats the purpose of MSG_PEEK.
According to the lwip_legacy port MSG_PEEK should be supported since 2007 and I'm not sure why it should stay broken such a long time.
2007-06-30 Frédéric Bernon
- sockets.h, sockets.c: Implement MSG_PEEK flag for recv/recvfrom functions.
As far as I can see I have used it correctly so I'm not sure why it isn't working. You can view the code at [1] and run the test with run/libc_msg_peek.
The reason for this behavior can be found in the libc_lwip library, which (as my statement above hints) does not translate the 'flags' parameter, which seems to be incompatibly defined in lwip and libc:
grep -rn define.*MSG_PEEK $(./tool/ports/current lwip_legacy)/include $(./tool/ports/current libc)/include .../contrib/lwip_legacy-7f5610426ed8c4c64903136122d9ba58836cdc89/include/lwip_legacy/lwip/sockets.h:157:#define MSG_PEEK 0x01 /* Peeks at an incoming message */ .../contrib/libc-5a5de5baab6e3ce23f24012a7604abf070fa990c/include/libc/sys/socket.h:450:#define MSG_PEEK 0x2 /* peek at incoming message */
Oh, I only grepped inside lwip so I didn't find the conflict with libc.
The current migration to VFS-based socket operations does not include any support for recv flags too. Is this feature essential for your use case?
While the MSG_PEEK isn't absolutely necessary to me some other flags are. We're trying to build a TCP/IP unwrapper/wrapper and transmit OSI layer 5 with a Terminal session in between. Yet the asynchronous semantics of the Terminal session and the synchronous ones of the socket API aren't exactly easy to combine. Currently I use MSG_DONTWAIT to prevent blocking in read operations and polling to check if data is available (I wasn't able to stop select if necessary and recv with MSG_PEEK obviously wasn't working).
So having at least MSG_DONTWAIT would be quite helpful. But I think the general problem is the API. Having a native asynchronous TCP or IP session with a native TCP/IP stack would be the best case but I assume this won't happen soon. But also an asynchronous (or at least more compatible) approach using libc would already be useful (and potentially resolve the need for any recv flags ;) ).
So currently I need some flags but I would happily switch to a generally better approach if there is one.
Regards, Johannes
Hello,
On Tue, Jun 19, 2018 at 08:14:07PM +0200, Johannes Kliemann wrote:
So having at least MSG_DONTWAIT would be quite helpful. But I think the general problem is the API. Having a native asynchronous TCP or IP session with a native TCP/IP stack would be the best case but I assume this won't happen soon. But also an asynchronous (or at least more compatible) approach using libc would already be useful (and potentially resolve the need for any recv flags ;) ).
So currently I need some flags but I would happily switch to a generally better approach if there is one.
I see two prominent directions we could explore. First on the libc level, you may use select() with a zero timeout for polling file descriptors and only read() if data is available. This option leaves us with the question, when to poll. The second option may be a deeper look into Emery's current lwip port which removes the socket API level and provides the low-level functions, which should be non-blocking.
There's also a third option in libc/select.h, which provides a mechanism to block in the entrypoint waiting for signals, RPC, and also for libc file descriptors to become readable. The semantics are a bit tricky, but I know of applications out there using this mechanism successfully.
Greets
Hi,
Am 19.06.2018 um 20:39 schrieb Christian Helmuth:
Hello,
On Tue, Jun 19, 2018 at 08:14:07PM +0200, Johannes Kliemann wrote:
So having at least MSG_DONTWAIT would be quite helpful. But I think the general problem is the API. Having a native asynchronous TCP or IP session with a native TCP/IP stack would be the best case but I assume this won't happen soon. But also an asynchronous (or at least more compatible) approach using libc would already be useful (and potentially resolve the need for any recv flags ;) ).
So currently I need some flags but I would happily switch to a generally better approach if there is one.
I see two prominent directions we could explore. First on the libc level, you may use select() with a zero timeout for polling file descriptors and only read() if data is available. This option leaves us with the question, when to poll.
This is what I currently do but with ioctl(int, FIONREAD, int*) which gives the number of bytes available to read from a socket.
The second option may be a deeper look into Emery's current lwip port which removes the socket API level and provides the low-level functions, which should be non-blocking.
I will look into that, thanks.
There's also a third option in libc/select.h, which provides a mechanism to block in the entrypoint waiting for signals, RPC, and also for libc file descriptors to become readable. The semantics are a bit tricky, but I know of applications out there using this mechanism successfully.
I also might look into this while I still prefer a non-blocking approach.
Thanks for your help, Johannes
Hello,
On Tue, Jun 19, 2018 at 10:20:56PM +0200, Johannes Kliemann wrote:
This is what I currently do but with ioctl(int, FIONREAD, int*) which gives the number of bytes available to read from a socket.
Another feature available only in libc_lwip with no plans to preserve, sorry.
There's also a third option in libc/select.h, which provides a mechanism to block in the entrypoint waiting for signals, RPC, and also for libc file descriptors to become readable. The semantics are a bit tricky, but I know of applications out there using this mechanism successfully.
I also might look into this while I still prefer a non-blocking approach.
Don't get my description wrong. The Libc::Select_handler fills the gap between Genode components (with signal handlers or RPC interfaces) and libc select(). So, it provides the feature to get deblocked if one of the selected file descriptors gets readable in conjunction with the common component functions.
Please have a look into the test residing in libports/src/test/libc_component.
Greets
Hi,
Don't get my description wrong. The Libc::Select_handler fills the gap between Genode components (with signal handlers or RPC interfaces) and libc select(). So, it provides the feature to get deblocked if one of the selected file descriptors gets readable in conjunction with the common component functions.
Please have a look into the test residing in libports/src/test/libc_component.
That looks indeed close to what I want to have. What is not clear to me why _select_ready is registered as handler but then still called manually after Select_handler::select returns.
Sorry for being nitpicky, I just want to get a better understanding about this (my understanding of the whole Libc interface also could be better so this is a starting point).
Regards, Johannes
Hello,
On Wed, Jun 20, 2018 at 11:03:21AM +0200, Johannes Kliemann wrote:
Please have a look into the test residing in libports/src/test/libc_component.
That looks indeed close to what I want to have. What is not clear to me why _select_ready is registered as handler but then still called manually after Select_handler::select returns.
Sorry for being nitpicky, I just want to get a better understanding about this (my understanding of the whole Libc interface also could be better so this is a starting point).
No problem. It is registered as handler function for the case the entrypoint was blocked when the fd becomes readable. But, (like in POSIX) Select_handler::select() may also return immediately with readable fds, which should also be handled immediately. I remember distantly that otherwise a deblocked fd ended up unhandled in some edge cases.
Greets