C语言 SO_RCVTIMEO 和 SO_SNDTIMEO 正确使用getsockopt 和setsockopt

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28244082/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 11:43:19  来源:igfitidea点击:

Proper use of getsockopt and setsockopt for SO_RCVTIMEO and SO_SNDTIMEO

csocketstimeoutsetsockopt

提问by cpaitor

By various reasons I would like to implement timeout on reading and writing to socket in a server but fail to get it running and therefore kindly ask for some insight into wherein the problem may reside.

由于各种原因,我想在服务器中读取和写入套接字时实现超时,但无法使其运行,因此请对问题可能存在的地方有所了解。

In order to set the timeout on the read and write to the socket I'm trying to use of the functions setsocketopt()and getsocketopt(). However I must be doing something wrong as the return value indicates that a problem have occurred and perror outputs "Invalid argument". Strangely enough the error does not always occur at the first usage of setsocketopt()and getsocketopt(), which puzzles me bit.

为了设置对套接字的读取和写入超时,我正在尝试使用函数setsocketopt()getsocketopt(). 但是我一定是做错了什么,因为返回值表明发生了问题并且 perror 输出“无效参数”。奇怪的是,错误并不总是发生在第一次使用setsocketopt()and 时getsocketopt(),这让我有点困惑。

The following server and client codes reproduces my problems (compiled using gcc)

以下服务器和客户端代码重现了我的问题(使用gcc编译)

server code:

服务器代码:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h>  

int main() {
   struct sockaddr_in saddr,caddr; 
   socklen_t clen;
   int sock, csock, reuse = 1, ret=0;
   socklen_t ntrcv, ntsnd;
   struct timeval tout, tsnd, trcv;

   // create a new stream socket
   if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) perror("failed to create socket");
   else {          
      // enable the socket to reuse the address
      if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) perror("failed allowing server socket to reuse address");
      else {
         // set up the server address
         memset((char *) &saddr, 0, sizeof(saddr));
         saddr.sin_family = AF_INET;
         saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
         saddr.sin_port = htons(45454);

         // bind socket to address
         if (bind(sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) perror("failed to bind");
         else {
            // listen to the socket for connections 
            if (listen(sock,5) < 0) perror("failed to listen");
            else {
               clen = sizeof(caddr);
               if ((csock = accept(sock, (struct sockaddr *) &caddr, &clen)) < 0) perror("failed to accept");
               else {
                  tout.tv_sec=0;
                  tout.tv_usec=10000;

                  // check value of errno prior to setting timeout
                  perror("errno prior to timeout");

                  if (getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &trcv, &ntrcv) < 0) perror("2");
                  else if (getsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tsnd, &ntsnd) < 0) perror("3");
                  else if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tout, sizeof(tout)) < 0) perror("4");
                  else if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tout, sizeof(tout)) < 0) perror("5");
                  else {
                     printf ("all ok so far in server\n");
                 sleep(1);
                     if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &trcv, ntrcv) < 0) perror("6");
                     else if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tsnd, ntsnd) < 0) perror("7");
                  }
                  if (close(csock) < 0) perror("failed to close csock");
               }
            }
         }
      }
   }
   if (close(sock) < 0) perror("failed to close sock");
   return ret;
}

client code:

客户端代码:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main() {
   struct sockaddr_in addr;     
   struct hostent *server; 
   int sock = 0;

   // resolve server name
   if (!(server = gethostbyname("127.0.0.1"))) perror("failed to resolve host");
   else {
      // prepare the server address
      memset((char *) &addr, 0, sizeof(addr));
      addr.sin_family = AF_INET;
      bcopy((char *)server->h_addr, (char *)&addr.sin_addr.s_addr, server->h_length);
      addr.sin_port = htons(45454);

      // create a socket and connect to the server
      if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) perror("failed to create client socket");
      else {
         if (connect(sock,(struct sockaddr *) &addr,sizeof(addr)) < 0) perror("failed to connect to server");
         else {
            printf("Connection is established will sleep now\n");
            sleep(3);
            printf("done sleeping\n");
            close(sock);
            printf("socket is closed\n");
         }
      }
   }
   return 0;
}

In the present example the second call to getsockopts()in the server fails resulting in perror("3")to be called. If I however comment this line out as well as the last call to setsockopts()the first call to getsockopts()fails (which previously seemed to work).

在本示例getsockopts()中,服务器中的第二次调用失败导致perror("3")被调用。但是,如果我将此行注释掉以及对第一个调用的最后setsockopts()一个调用getsockopts()失败(以前似乎有效)。

Any insights to where I go wrong are appreciated

对我出错的地方的任何见解表示赞赏

回答by abligh

You are not initialising ntsndand ntrcvto the size of the buffer available to getsockopt. Therefore if the random value there greater or equal to the size needed, the call will succeed. Else it will fail with EINVAL. From the man page:

您没有初始化ntsndntrcv可用的缓冲区大小getsockopt。因此,如果那里的随机值大于或等于所需的大小,则调用将成功。否则它将失败EINVAL。从手册页:

The arguments optvaland optlenare used to access option values for setsockopt(). For getsockopt()they identify a buffer in which the value for the requested option(s) are to be returned. For getsockopt(), optlenis a value-result argument, initially containing the size of the buffer pointed to by optval, and modified on return to indicate the actual size of the value returned. If no option value is to be supplied or returned, optvalmay be NULL.

参数optvaloptlen用于访问 的选项值setsockopt()。因为getsockopt()它们标识了一个缓冲区,在该缓冲区中将返回所请求选项的值。对于getsockopt(),optlen是一个值结果参数,最初包含由 指向的缓冲区的大小optval,并在返回时修改以指示返回值的实际大小。如果不提供或返回任何选项值,则optval可能是NULL

To fix this intialize them both to sizeof(struct timeval).

为了解决这个问题,将它们都初始化为sizeof(struct timeval).

回答by user207421

You need to set the ntsndand ntrcvvariables to sizeof ntsndand sizeof ntrcvrespectively, or sizeof struct timeval.

您需要设置ntsndntrcv变量sizeof ntsndsizeof ntrcv分别,或sizeof struct timeval