Правильное использование getsockopt и setsockopt для SO RCVTIMEO и SO SNDTIMEO



По разным причинам я хотел бы реализовать timeout на чтение и запись в сокет на сервере, но не удается запустить его и поэтому любезно прошу некоторого понимания, где может находиться проблема.



Для установки таймаута на чтение и запись в сокет я пытаюсь использовать функции setsocketopt() и getsocketopt(). Однако я, должно быть, делаю что-то не так, поскольку возвращаемое значение указывает на то, что произошла проблема, и perror выводит "недопустимый аргумент". Как ни странно ошибка не всегда возникает при первом использовании 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 servern");
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 nown");
sleep(3);
printf("done sleepingn");
close(sock);
printf("socket is closedn");
}
}
}
return 0;
}


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



Любое понимание того, где я ошибаюсь, ценится

940   2  

2 ответов:

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

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

Чтобы исправить эту intialize их обоих sizeof(struct timeval).

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

Comments

    Ничего не найдено.