I have a listener socket, every new connection I get I add it to epoll like this:
int connfd = accept(listenfd, (struct sockaddr *)&clnt_addr, &clnt_addr_len);
ev.events = EPOLLIN | EPOLLET | EPOLLONESHOT | EPOLLHUP;
ev.data.fd = connfd;
epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev)
When new data is received, epoll signal 'EPOLLIN' event - as expected.
In such a situation I read all the information as follows:
long read = 0;
do {
read = recv(events[n].data.fd, buffer, sizeof (buffer), 0);
} while (read > 0);
In case I disconnected brutally or normally, epoll does not signal an event.
This code run in each thread, that's what I'm using EPOLLET.
So my question:
There are a few problems with your attempt.
You should not use EPOLLONESHOT
unless you know what you are doing and you really need it. It disables the report of any other events to the epoll instance until you enable it again with EPOLL_CTL_MOD
.
You should not use EPOLLHUP
to determine if a connection was closed. The EPOLLHUP
event may be raised before all data is read from the socket input stream, even if the client disconnects gracefully. I recommend you to only use EPOLLIN
. If there is no input left (because of forceful or graceful disconnect), the read()
would return 0
(EOF) and you can close the socket.
Your read()
call will block the reading thread and consume the whole stream until EOF (connection closed). The whole point in using epoll()
is to not have to use a while ( read(...) > 0 )
loop.
You should not use EPOLLET
because "the code runs multithreaded" but because you need it. You can write multithreaded code without the edge-triggered mode. The use of EPOLLET
requires a thorough knowledge of the differences between blocking and non-blocking I/O. You can easily run into pitfalls (as mentioned in the manual), like edge-triggered starvation.