Socket with recv-timeout: What is wrong with this

2020-07-06 03:31发布

问题:

I'm trying to implement a socket with a recv timeout of 1 Second:

int sockfd;
struct sockaddr_in self;
struct sockaddr_in client_addr;
int addrlen=sizeof(client_addr);
ssize_t nBytes;

sockfd = socket(AF_INET, SOCK_STREAM, 0);

self.sin_family = AF_INET;
self.sin_port = htons(PORT);
self.sin_addr.s_addr = INADDR_ANY;

int on = 1;
setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on);

// 1 Sec Timeout
tv.tv_sec  = 1;  
tv.tv_usec = 0;
setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv);

bind(sockfd, (struct sockaddr*)&self, sizeof(self));

listen(sockfd, 20);

clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &addrlen);

nBytes = recv(clientfd, buffer, MAXBUF-1, 0);

Without 'setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv);' the calls to accept and recv work, but recv blocks.

With 'setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv);' the call to accept produces the error 'Resource temporarily unavailable'.

Can somebody please tell me what is wrong with this approach?

回答1:

Which socket do you want to have the one-second timeout on? The one accepting connections, or the one established by accept()?

I'd assume the latter - so try setting the receive timeout on clientfd AFTER the accept returns. You can also get to where you need to be using select, but you shouldn't need to.



回答2:

This is little bit off topic, but I really want to share this solution to set recv timeout both on windows and unix. Maybe it's me, but it took me a lot of time to figure out why my program doesn't work and how to properly set timeout. Hope you find it useful. It sets timeout to 10 seconds.

For Windows:

DWORD sock_timeout = 10*1000;

For Unix:

const struct timeval sock_timeout={.tv_sec=10, .tv_usec=0};

For both:

setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&sock_timeout, sizeof(sock_timeout));


回答3:

Here's a snippet using select:

FD_ZERO(&masterfds);
FD_SET(sockfd,&masterfds);
memcpy(&readfds,&masterfds,sizeof(fd_set));
timeout.tv_sec = 2;
timeout.tv_usec = 0;
if (select(sockfd+1, &readfds, NULL, NULL, &timeout) < 0)
{
    printf("select error");
    exit(1);
}

if (FD_ISSET(sockfd, &readfds))
{
    //printf("Read from socket\n");
    // read from the socket
    res = recvfrom(sockfd, (char *)hdrbuf, sizeof(hdrbuf), MSG_PEEK, recvaddr, address_len);
}
else
{
    // the socket timedout
    //printf("Socket timeout started=%d\n",packets_started);


回答4:

Nothing is wrong... The error code EAGAIN (Resource temporarily unavailable) is exactly what you should get after the timeout expires!



回答5:

You need one more closing parenthesis at each of these two lines.

- setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on);
+ setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
- setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv);
+ setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));


回答6:

Try using select() before calling recv() or accept().

select() takes an array of file descriptors (includinig sockets) and returns when at least one of them is ready to receive. It can also return on a timeout.

In linux you can also try poll() (not sure if Winsock provides this).



标签: c linux sockets