dantol29 / webserver

Our own HTTP server
4 stars 1 forks source link

Understand the blocking vs non-blocking socket issue (and decide) #36

Open 552020 opened 6 months ago

552020 commented 6 months ago

Introduction

A socket can be blocking or non-blocking. A blocking socket will block on potentially blocking operations like read and write, whereas a non-blocking socket will not. This means that if we read from a blocking socket and the socket is open but there is nothing to read, the read function will 'block' execution and wait for something to become available to read. Conversely, the write function on a blocking socket will also block if the socket's buffer is full and it cannot write; it waits until space becomes available. If the socket is non-blocking, read and write operations will immediately return, and it becomes the responsibility of the program to handle these non-blocking properties properly. Since there's a possibility that the socket from which we want to read or to which we want to write will have something to read or space to write in the very near future, we should periodically revisit these sockets to check if the operation can be performed. For this, we will use polling mechanisms like poll.

The subject

Since our web server should be non blocking, and this non blocking capabilities should be reached through "multiplexing" function like poll, epoll or select (since we can't use multithreading) the question is if our sockets should be blocking or non blocking.

On page 6 (4th points of the requirements we read): "It must be non-blocking and use only 1 poll() (or equivalent) for all the I/O operations between the client and the server (listen included)." The subject "it" is the server (not the sockets). The server can be non blocking even if the sockets are blocking.

On page 6 (in the warning box) we read: "Because you have to use non-blocking file descriptors, it is possible to use read/recv or write/send functions with no poll() (or equivalent), and your server wouldn’t be blocking. But it would consume more system resources. Thus, if you try to read/recv or write/send in any file descriptor without using poll() (or equivalent), your grade will be 0." This passage implies that we need to use non-blocking sockets, even if this hasn't been stated before (the non-blocking capabilities of the web server can be reached also with blocking sockets).

On page 7: "Since MacOS doesn’t implement write() the same way as other Unix OSes, you are allowed to use fcntl(). You must use file descriptors in non-blocking mode in order to get a behavior similar to the one of other Unix OSes." This seems to imply that Linux users has to use blocking sockets, and only the MacOs users can use non-blocking sockets to achieve the same behavior of the Unix users

In the evaluation sheet there is no reference to sockets being blocking non-blocking.

**The question**

Should we use blocking or non blocking sockets? And if we are going to use non blocking sockets, do you think we are allowed to use the fcntl function (which is among the allowed functions but depending on the interpretations on the subject (par. III.2) could be allowed only for MacOS users). To give a little bit more context, we are going to create new sockets with the socket() function, which on Linux with the appropriate flag can also create non blocking sockets (default is blocking) and with the accept() function.

But the man page of accept() inform us that:

 On  Linux,  the  new socket returned by accept() does not inherit file status flags
       such as O_NONBLOCK and O_ASYNC from the listening socket.   This  behavior  differs
       from  the  canonical BSD sockets implementation.  Portable programs should not rely
       on inheritance or noninheritance of file status flags and always explicitly set all
       required flags on the socket returned from accept().

To create directly non blocking sockets while accepting a connection we should use accept4() which is not in the list of the allowed functions. It seems there is no other way to create non blocking sockets while accepting besides using fcntl() or accept4()

I took a look on the codebase of our mates. Most of them are using fcntl and working with non blocking sockets. No one uses accept4, no one uses the NO_BLOCKING flag with socket(). 1 team (jissert & co.) doesn't use fcntl, so they seem to work with blocking sockets, at least 1 team doesn't use fcntl for the sockets create with accept, i.e. only their listening socket seems to be blocking the ones created with accept seem to be blocking.

What do you think? @lmangall @dtolmaco

I guess I would go with non blocking and I would use fcntl as well…

linear[bot] commented 6 months ago

WEB-38 Understand the blocking vs non-blocking socket issue