I'm writing a C client-server program, in which the client has to receive a large amount of data from the server. Since I want my client not to wait indefinitely on a recv()
if something goes wrong with the server (e.g., it's stopped when data is being sent), I opted to use the poll()
function as specified in the linux man page.
My code is something like the following:
while (...)
{
struct pollfd fds;
fds.fd = sock;
fds.events = POLLIN;
retry:
r = poll(&fds, 1, TIMEOUT*1000);
if (r == -1 && errno == EINTR)
goto retry;
else if (r == -1)
err_sys("poll() failed");
else if (r == 0)
err_sys("timeout expired");
recv(...)
}
where sock
is the file descriptor associated with the socket, TIMEOUT
is set to 5 seconds and I specify POLLIN
as the event since I'm interested in reading data.
Problem
According to man:
The timeout argument specifies the number of milliseconds that poll() should block waiting for a file descriptor to become ready. The call will block until either:
(1) a file descriptor becomes ready; (2) the call is interrupted by a signal handler; or (3) the timeout expires.
However, the program blocks indefinitely on the poll()
function even though the timeout expires (I used valgrind) as soon as I stop the server. I also tried to set the events to POLLIN | POLLPRI
(in order to catch some exceptional conditions) but it didn't work. I read the documentation multiple times and I couldn't figure out what's causing this problem.
Other info
I'm using Xubuntu 18.04, gcc version 7.4.0, target x86_64
Your code is unconditionally calling recv()
even when there is no data to read. In fact, you are completely ignoring the fds.revents
field altogether if poll()
does not return an error/timeout.
Your loop should look more like this:
struct pollfd fds;
fds.fd = sock;
fds.events = POLLIN;
do {
r = poll(&fds, 1, TIMEOUT*1000);
if (r == -1) {
if (errno == EINTR) continue;
perror("poll() failed");
break;
}
else if (r == 0) {
printf("timeout expired");
break;
}
else if (fds.revents & POLLIN) {
r = recv(...);
if (r < 0) {
perror("recv() failed");
break;
}
else if (r == 0) {
printf("socket disconnected\n");
break;
}
else {
// process data as needed...
}
}
else if (fds.revents & (POLLERR | POLLNVAL)) {
printf("socket error\n");
break;
}
}
while (1);
close(sock);