8

Lets consider following piece of code

pollfd file_descriptors[1];
file_descriptors[0].fd = sock_fd;
file_descriptors[0].events = POLLIN;

int return_value = poll(file_descriptors, 1, 0);

if (return_value == -1) { cerr << strerror(errno); }
else if (return_value == 0) { cerr << "No data available to be read"; }
else { 
    if (file_descriptors[0].revents & POLLIN) {
        recv(sock_fd, buff, 1024, 0); 
    }
}

Now I have two questions regarding the above code.

  1. If the call to poll() returns neither -1 nor 0 and sets the POLLIN flag in the bitmap revents for the first entry in the file_descriptors array, then will the call to recv() block? If no, then will the data be read in instantaneously?
  2. Assuming the call to poll() goes the same way as mentioned above. How much data is going to be read in? Is it just going to be the same as a regular call to recv()? i.e. an arbitrary (to the programmer) amount less than or equal to 1024 in the above case. Then if I want to poll() before reading again, do I just repeat from the first invocation of poll() until all data has been read in completely (i.e. in a client server scenario this would correspond to the request being completed)?

Thank you!

Curious
  • 20,870
  • 8
  • 61
  • 146
  • 1
    For 3, see http://stackoverflow.com/questions/589928/socket-programming-how-do-i-handle-out-of-band-data. Essentially, if one socket side sends normal data and *then* OOB data, the client gets the OOB data as quickly as possible, possibly *before* the other data. [well, it depends how the client calls recv, and on some other things, and that's not the whole story ... see the link] – deviantfan Dec 30 '15 at 22:29
  • Thank you so much! I will look into that :) – Curious Dec 30 '15 at 22:30
  • @deviantfan From what I read in the answers to that question. I will be ignoring all OOB data in my current use case. I have one followup question though. If there is indeed some OOB data that has been sent, and I am ignoring it, i.e. just calling recv() regulary. Will that data be ignored? Or will it be injected into the current stream of bytes that I am reading (or recv()-ing)? – Curious Dec 30 '15 at 22:35
  • 1
    It depends. Either you can use `MSB_OOB` in `recv` to query OOB data and no such option to query normal data, or you can use `setsockopt` with `SO_OOBINLINE` to receive everything without special `recv` options. – deviantfan Dec 30 '15 at 22:48
  • @deviantfan Thank you! So I will just ignore OOB data as something that doesn't exist entirely. I guess I will remove the third part of the question soon. I will wait just a little bit to make sure someone else wasn't already typing something. – Curious Dec 30 '15 at 22:49

3 Answers3

3

If the call to poll() returns neither -1 nor 0 and sets the POLLIN flag in the bitmap revents for the first entry in the file_descriptors array, then will the call to recv() block? If no, then will the data be read in instantaneously?

The call to poll has no effect on the call to recv. Whether or not recv blocks depends on what data is available at the time you call recv and whether the socket is in blocking or non-blocking mode.

Assuming the call to poll() goes the same way as mentioned above. How much data is going to be read in? Is it just going to be the same as a regular call to recv()? i.e. an arbitrary (to the programmer) amount less than or equal to 1024 in the above case. Then if I want to poll() before reading again, do I just repeat from the first invocation of poll() until all data has been read in completely (i.e. in a client server scenario this would correspond to the request being completed)?

Assuming you don't want to block, you should set the socket non-blocking. You have two basic choices. You can call the receive function once and then poll again. Or you can keep calling receive until you get a "would block" indication. If the socket is a TCP connection, you can keep calling the receive function until you either get a "would block" indication or receive fewer bytes than you asked for.

There are three common, non-blocking I/O strategies that you can base around poll or select. All require that the socket be set non-blocking.

1) You can always call poll or select before you perform an I/O operation. You then attempt a single read or write operation only if you get a read or write hit from poll or select.

2) You can attempt the read or write operation first. If it succeeds immediately, great. If not, wait for a poll or select hit before retrying the operation.

3) You can call poll or select before you perform an I/O operation. You then attempt multiple reads or writes until you either finish everything you need to do or get a "would block" indication. When you get a "would block" indication, you wait until select or poll tells you before you attempt another operation in that direction on that socket.

Method 3 is probably the most common for reads. Method 2 is probably the most common for writes.

It is very important to keep in mind that poll is a status reporting function that tells you current information. It does not come with any future guarantees. The assumption that a read indication from poll means that a future read operation will not block is a mistake and it has caused serious bugs with significant security implications in the past. The only way to guarantee that a socket operation will not block is to set the socket non-blocking.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • Thank you so much! But do you think my logic seems right? `poll()` and then `recv()` followed by `poll()` and then `recv()` and so on... And this is in order to get a non blocking read from the network. Basically what I mean to ask is, if the POLLIN flag is set in the `revents` structure then the immediate `recv()` that follows will not block correct? – Curious Dec 31 '15 at 00:52
  • @Curious If you want non-blocking reads, you have to set the socket non-blocking. Then you can use `poll` to know when to perform read and write operations. You can use `poll` before the operation every time or you can call `poll` only as a way to wait when you get a "would block" indication. Both methods work. It is common to `poll` first before reading but to write first and fall back to `poll` only if a write gets a "would block" indication. But you can do it however you want. – David Schwartz Dec 31 '15 at 00:53
  • Ok, so the following steps are correct? 1. Set the socket to be non blocking, 2. Call `poll()`, 3. If `poll()` indicated there is data to be read then call `recv()`, 4. If all the required data has been read in break, 5. repeat from (2) – Curious Dec 31 '15 at 00:55
  • Also is what I mentioned above the most efficient way to go about such a non blocking scheme? If not what can I change? – Curious Dec 31 '15 at 00:56
  • That's a common way to handle reads. I'll update my answer to explain the three different basic I/O strategies. – David Schwartz Dec 31 '15 at 00:56
  • Thank you! Your answer and comments have been helpful – Curious Dec 31 '15 at 00:57
1

If the poll() returns a strictly positive value, it means that at least one of your descriptors has some data to return.

The recv() SHOULD therefore not be blocking: as there is data it SHOULD return something. In any case, it is not guaranteed that all the 1024 bytes will be returned by the call.

You must therefore check the number of bytes returned by recv(). Eventually, you have to loop on your polling/receiving until you got all the expected data.

You can control the blocking/non-blocking behaviour either by call with the flag parameter MSG_DONTWAIT, or via fcntl() on a more durable fashion.

Edit: Note that if something goes wrong on the socket (for example a loss of connection) the available data promised by the poll MIGHT be lost and block the recv() against all expectations. It is hence safer to explicitely use the non-blocking flag (and consider that recv() might return an error code instead of a number of bytes read).

Christophe
  • 68,716
  • 7
  • 72
  • 138
  • Thank you! Could you share some references for your answer? – Curious Dec 31 '15 at 00:44
  • Also what is the point of having `MSG_DONTWAIT` when you have polled before calling `recv()`? – Curious Dec 31 '15 at 00:45
  • @Curious this last remark is a general information, as OP seemed to be worried about blocking or not. As said above, after a succesful poll() there will be no block. – Christophe Dec 31 '15 at 00:50
  • 1
    @Christophe That is not guaranteed. The `poll` function reports current status and cannot predict the future. The assumption that a read hit from `poll` assures that a future read operation will not block is a mistake that has caused serious bugs with security implications in the past. If you don't want to block, you must either set the socket non-blocking or use read/write operations with flags that request that they do not block. – David Schwartz Dec 31 '15 at 00:51
  • @DavidSchwartz Point taken: if something goes wrong or if the data is read elesewhere, there would be a blocking situation. – Christophe Dec 31 '15 at 01:02
  • 1
    Or if you're using UDP and the datagram gets dropped, which can be a real disaster if that causes a security-critical process to hang. (Yes, that really happened.) – David Schwartz Dec 31 '15 at 01:03
  • @DavidSchwartz In the case where there is an error and I have already set the socket to non blocking using `fcntl()`. What will the call to `recv()` return? Will it return -1 and set `errno` to `EAGAIN` or `EWOULDBLOCK`? If yes then how do I distinguish between needing to call `poll()` or `select()` again and throwing an exception to say that the connection is closed? – Curious Dec 31 '15 at 01:15
  • @DavidSchwartz In other words, does a return value of -1 and `errno` set to either `EAGAIN` or `EWOULDBLOCK` imply that I should call poll() again? Something going wrong would not set `errno` to these values right? – Curious Dec 31 '15 at 01:17
  • @Curious Correct. Those are "would block" indications that indicate that you should retry the operation later. You use `poll` to know when it's later. The connection is closed if zero is returned or a fatal error occurs. Errors like "would block" or "interrupted" are not fatal. – David Schwartz Dec 31 '15 at 01:20
  • @DavidSchwartz Thank you! So the approach I will follow for `recv()`ing is `poll()` followed by `recv()` as many times as it does not return `-1` and set `errno` to either `EAGAIN` or `EWOULDBLOCK`, if any other `errno` is returned then I will throw an exception – Curious Dec 31 '15 at 01:26
  • @Curious That sounds good. If you're only using TCP, you can add a small optimization -- if `recv` gives you fewer bytes than you asked for, there's no need to call `recv` again. It's near certain to give you a "would block" indication and you're better off just assuming that it will and waiting for `poll` again. (But you don't have to do that if you don't want to.) – David Schwartz Dec 31 '15 at 01:46
  • @DavidSchwartz Thank you for that! Could you suggest some place for me to read up on things like this myself? Just so I can know more :) – Curious Dec 31 '15 at 02:01
  • In the case where the data is lost due to a reset the call will not block. – user207421 Dec 31 '15 at 02:09
0

If the call to poll() returns neither -1 nor 0 and sets the POLLIN flag in the bitmap revents for the first entry in the file_descriptors array, then will the call to recv() block?

It shouldn't, unless another thread has read all the data presently available from the socket.

If no, then will the data be read in instantaneously?

Yes.

Assuming the call to poll() goes the same way as mentioned above. How much data is going to be read in?

As much data as has already arrived.

Is it just going to be the same as a regular call to recv()? i.e. an arbitrary (to the programmer) amount less than or equal to 1024 in the above case.

Yes.

Then if I want to poll() before reading again, do I just repeat from the first invocation of poll() until all data has been read in completely (i.e. in a client server scenario this would correspond to the request being completed)?

Yes.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Thank you for your answer! Could you share some references to it though? Just so I know where to look when I have similar questions – Curious Dec 31 '15 at 00:39
  • 1
    The `poll` function is a status reporting function. It does not come with any future guarantees. Assuming that an indication from `poll` or `select` will guarantee that a future read or recv will not block *under any circumstances* is a mistake and it is one that has caused bugs with security implications in the past. – David Schwartz Dec 31 '15 at 00:46