2015-01-30 2 views
4

По различным причинам я хотел бы реализовать тайм-аут при чтении и записи в сокет на сервере, но не смог его запустить и, следовательно, любезно попросить некоторое представление о том, в чем может возникнуть проблема ,Правильное использование getsockopt и setsockopt для SO_RCVTIMEO и SO_SNDTIMEO

Чтобы установить тайм-аут на чтение и запись в сокет, я пытаюсь использовать функции setsocketopt() и getsocketopt(). Однако я должен делать что-то неправильно, так как возвращаемое значение указывает на то, что возникла проблема, а perror выводит «Invalid argument». Как ни странно, ошибка не всегда возникает при первом использовании setsocketopt() и getsocketopt(), что меня озадачивает.

Следующие серверные и клиентские коды воспроизводит мои проблемы (скомпилированы с использованием GCC)

код сервера:

#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; 
} 

код клиента:

#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; 
} 

В настоящем примере второй вызов getsockopts() на сервере выходит из строя, что приводит к вызову perror("3"). Если я, однако, прокомментирую эту строку, а также последний звонок setsockopts(), первый вызов getsockopts() не удался (что раньше казалось сработало).

Любые идеи, где я пойти не так ценится

+0

Могу ли я спросить о вашем подходе для установки этих параметров сокета? Вы сначала устанавливаете значение для 'SO_RCVTIMEO'' tout' (являющегося значением, которое вы хотите применить), но после второй спящего вы вернетесь к 'trcv' (исходное значение). Я не вижу цели этого. Можете ли вы уточнить? – gleerman

+0

Привет, Gleerman, извините за неточность, линия сна (1) была просто предназначена во время моего теста и должна быть заменена (а также предыдущий вызов printf()) с любой задачей, которую должен быть установлен сервер. Затем, как только закончите, я верну SO_RCVTIMEO к его исходному значению. – cpaitor

ответ

5

Вы не инициализацией ntsnd и ntrcv до размера буфера доступны для getsockopt. Поэтому, если случайное значение больше или равно требуемому размеру, вызов будет успешным. Иначе это сработает с EINVAL. От человека странице:

Аргументы optval и optlen используются для доступа к значениям опций для setsockopt(). Для getsockopt() они идентифицируют буфер, в котором должно быть возвращено значение для запрашиваемой опции (ов). Для getsockopt(), optlen - аргумент значения-результата, первоначально содержащий размер буфера, на который указывает optval, и изменен по возврату, чтобы указать фактический размер возвращаемого значения. Если значение параметра не должно быть поставлено или возвращено, optval может быть NULL.

Чтобы исправить это, сделайте их как sizeof(struct timeval).

+0

Aaaaarghhh просто так, спасибо – cpaitor

+1

@cpaitor: Нет проблем - лучший способ сказать спасибо, чтобы принять ответ :-) – abligh

+0

Ну, кажется, что есть какой-то срок, прежде чем вы сможете принять, мои первые попытки просто отображали красный поле с текстом «есть x минут, пока вы не сможете принять ответ» (или аналогичный) – cpaitor

2

Вы должны установить ntsnd и ntrcv переменные sizeof ntsnd и sizeof ntrcv соответственно, или sizeof struct timeval.

+0

Спасибо, подумайте, что это в основном то, что сказал @abligh, пропустил эту часть информации, когда я впервые прочитал man-страницу. – cpaitor