Linux manpage для SO_RCVTIMEO говорит:SO_RCVTIMEO просыпается слишком рано
Укажите получение или отправка не таймаут до сообщения об ошибке ... Если функции ввода или вывода блоков за этот период времени ... [и ] никакие данные не были переданы и тайм-аут был достигнут, то возвращается -1 с ERRNO набор для EAGAIN или EWOULDBLOCK или EINPROGRESS (для подключения (2))
Это звучит для меня как I/O должен ждать не менее SO_RCVTIMEO перед возвратом выполнения вызывающему абоненту. Между тем, at the Open Group, они документируют обратное:
Устанавливает значение тайм-аута, который определяет количество времени функции входа ждет, пока он не закончит максимум.
Какое значение минимальное время блокировки или максимальное время блокировки? Ответ выглядит так: да. Вот что происходит, когда я прошу .500s тайм-аут на системе Linux:
time: 0.497054 result: 0
time: 0.495352 result: 0
time: 0.504948 result: 0
time: 0.495119 result: 0
time: 0.507884 result: 0
time: 0.491892 result: 0
time: 0.500764 result: 0
Мы видим, что время неправильно, обычно так же, как 7мс или так, что долгое время, чтобы быть неправильно, , И ошибка происходит в обоих направлениях. В то же время на Дарвина:
time: 0.500426 result: -1
time: 0.501144 result: -1
time: 0.500507 result: -1
time: 0.501119 result: -1
time: 0.501016 result: -1
time: 0.500540 result: -1
time: 0.500127 result: -1
time: 0.500815 result: -1
time: 0.500341 result: -1
time: 0.500871 result: -1
time: 0.500835 result: -1
time: 0.501138 result: -1
time: 0.501087 result: -1
time: 0.501153 result: -1
time: 0.501149 result: -1
Ошибка гораздо ниже (~ 1 мс), но по-прежнему существует, и они четко интерпретировать 500мс как минимальное время, не максимальным.
Теперь некоторые вопросы:
- ли SO_RCVTIMEO должна быть минимальная или максимальная продолжительность блокировки абонента?
- Если это максимальная продолжительность, то какой минимум? Разумеется, реализация не является бесплатной для выбора неблокирующего чтения при запросе на 500 мс тайм-аута?
- Если это минимальная продолжительность, Дарвин ошибается?
- Если я хочу гарантировать, что я пытался читать за как минимум 500 мс, я должен продолжать пытаться в цикле до истечения 500 мс? Что такое «правильный путь» для реализации поведения «по крайней мере X ms»?
- Почему существует множество вариаций от вызова к вызову в Linux? Каков источник ошибки?
- Есть ли лучший API, который я должен использовать для чтения из сокетов?
код я использовал для измерения этого:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
#include <fcntl.h>
#ifdef __MACH__
#include <mach/clock.h>
#include <mach/mach.h>
#endif
void error(const char *msg)
{
perror(msg);
exit(1);
}
struct timespec os_time() {
struct timespec ts;
#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
ts.tv_sec = mts.tv_sec;
ts.tv_nsec = mts.tv_nsec;
#else
clock_gettime(CLOCK_REALTIME, &ts);
#endif
return ts;
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
for (int i = 0; i < 100; i++) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 500000;
char buf[1];
if (setsockopt(newsockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval)) != 0){
error("setsockopt error");
}
struct timespec start = os_time();
int result = recv(newsockfd,buf,1,0);
struct timespec end = os_time();
double end_time = (double)end.tv_sec + ((double)end.tv_nsec)/1.0E9;
double start_time = (double)start.tv_sec + ((double)start.tv_nsec)/1.0E9;
printf("time: %f result: %d\n",end_time-start_time, result);
}
return 0;
}
Размножение:
clang test.c && ./a.out 5551 &
telnet localhost 5551
time: 0.497839 result: 0
time: 0.501052 result: 0
time: 0.498565 result: 0
time: 0.500741 result: 0
time: 0.500108 result: 0
time: 0.500244 result: 0
time: 0.499040 result: 0
time: 0.500212 result: 0
time: 0.500137 result: 0
time: 0.499920 result: 0
time: 0.500758 result: 0
time: 0.498068 result: 0
«Если данные уже существуют или до истечения времени ожидания, метод возвращается в этой точке, не дожидаясь истечения времени ожидания». Я понимаю это. Ситуация, о которой я спрашиваю, - это когда данные НЕ поступают. В этом случае Linux дает 490 мс в тайм-аут 500 мс. Это разумно? Или что нужно, чтобы Linux вместо этого отказался от 0 мс в тайм-аут 500 мс? Или попросить другой способ: если я знаю, что некоторые данные будут считаны ровно через 500 мс, какое значение SO_RCVTIMEO гарантирует, что чтение будет успешным? – Drew
Что нужно, чтобы предотвратить это спецификация, но вам нужно понять, что таймеры имеют детализацию. Например, в Windows это 16 мс. Вы не можете получить больше детализации, чем есть. – EJP
Где спецификация? Где я могу узнать, как долго или коротко может быть тайм-аут в Linux? – Drew