Чтение онлайн

ЖАНРЫ

UNIX: разработка сетевых приложений
Шрифт:

3 #define RTT_DEBUG

4 static struct rtt_info rttinfo;

5 static int rttinit = 0;

6 static struct msghdr msgsend, msgrecv;

/* предполагается, что обе структуры инициализированы нулем */

7 static struct hdr {

8 uint32_t seq; /* порядковый номер */

9 uint32_t ts; /* отметка времени при отправке */

10 } sendhdr, recvhdr;

11 static void signalrm(int signo);

12 static sigjmp_buf jmpbuf;

13 ssize_t

14 dg_send_recv(int fd, const void *outbuff, size_t outbytes,

15 void *inbuff, size_t inbytes,

16 const SA *destaddr, socklen_t destlen)

17 {

18 ssize_t n;

19 struct iovec iovsend[2], iovrecv[2];

20 if (rttinit == 0) {

21 rtt_init(&rttinfo); /*
первый вызов */

22 rttinit = 1;

23 rtt_d_flag = 1;

24 }

25 sendhdr.seq++;

26 msgsend.msg_name = destaddr;

27 msgsend.msg_namelen = destlen;

28 msgsend.msg_iov = iovsend;

29 msgsend.msg_iovlen = 2;

30 iovsend[0].iov_base = &sendhdr;

31 iovsend[0].iov_len = sizeof(struct hdr);

32 iovsend[1].iov_base = outbuff;

33 iovsend[1].iov_len = outbytes;

34 msgrecv.msg_name = NULL;

35 msgrecv.msg_namelen = 0;

36 msgrecv.msg_iov = iovrecv;

37 msgrecv.msg_iovlen = 2;

38 iovrecv[0].iov_base = &recvhdr;

39 iovrecv[0].iov_len = sizeof(struct hdr);

40 iovrecv[l].iov_base = inbuff;

41 iovrecv[l].iov_len = inbytes;

1-5
Мы включаем новый заголовочный файл
unprtt.h
, показанный в листинге 22.8, который определяет структуру
rtt_info
, содержащую информацию RTT для клиента. Мы определяем одну из этих структур и ряд других переменных.

Определение структур msghdr и структуры hdr

6-10
Мы хотим скрыть от вызывающего процесса добавление порядкового номера и отметки времени в начало каждого пакета. Проще всего использовать для этого функцию
writev
, записав свой заголовок (структура
hdr
), за которым следуют данные вызывающего процесса, в виде одной дейтаграммы UDP. Вспомните, что результатом выполнения функции
writev
на дейтаграммном сокете является отправка одной дейтаграммы. Это проще, чем заставлять вызывающий процесс выделять для нас место в начале буфера, а также быстрее, чем копировать наш заголовок и данные вызывающего процесса в один буфер (под который мы должны выделить память) для каждой функции
sendto
. Но поскольку мы работаем с UDP и нам необходимо задать адрес получателя, следует использовать
возможности, предоставляемые структурой
iovec
функций
sendmsg
и
recvmsg
и отсутствующие в функциях
sendto
и
recvfrom
. Вспомните из раздела 14.5, что в некоторых системах доступна более новая структура
msghdr
, включающая вспомогательные данные (
msg_control
), тогда как в более старых системах вместо них применяются элементы
msg_accright
(так называемые права доступа — access rights), расположенные в конце структуры. Чтобы избежать усложнения кода директивами
#ifdef
для обработки этих различий, мы объявляем две структуры
msghdr
как
static
. При этом они инициализируются только нулевыми битами, а затем неиспользованные элементы в конце структур просто игнорируются.

Инициализация при первом вызове

20-24
При первом вызове нашей функции мы вызываем функцию
rtt_init
.

Заполнение структур msghdr

25-41
Мы заполняем две структуры
msghdr
, используемые для ввода и вывода. Для данного пакета мы увеличиваем на единицу порядковый номер отправки, но не устанавливаем отметку времени отправки, пока пакет не будет отправлен (поскольку он может отправляться повторно, а для каждой повторной передачи требуется текущая отметка времени).

Вторая часть функции вместе с обработчиком сигнала

sig_alarm
показана в листинге 22.7.

Листинг 22.7. Функция dg_send_recv: вторая половина

//rtt/dg_send_rеcv.c

42 Signal(SIGALRM, sig_alrm);

43 rtt_newpack(&rttinfo); /* инициализируем для этого пакета */

44 sendagain:

45 sendhdr.ts = rtt_ts(&rttinfo);

46 Sendmsg(fd, &msgsend, 0);

47 alarm(rtt_start(&rttinfo)); /* вычисляем тайм-аут. запускаем таймер */

48 if (sigsetjmp(jmpbuf, 1) != 0) {

49 if (rtt_timeout(&rttinfо) < 0) {

50 err_msg("dg_send_recv: no response from server, giving up");

51 rttinit = 0; /* повторная инициализация для следующего вызова */

52 errno = ETIMEDOUT;

53 return (-1);

54 }

55 goto sendagain;

56 }

57 do {

58 n = Recvmsg(fd, &msgrecv, 0);

59 } while (n < sizeof(struct hdr) || recvhdr.seq != sendhdr.seq);

60 alarm(0); /* останавливаем таймер SIGALRM */

61 /* вычисляем и записываем новое значение оценки RTT */

62 rtt_stop(&rttinfo, rtt_ts(&rttinfo) — recvhdr.ts);

63 return (n - sizeof(struct hdr)); /* возвращаем размер полученной

дейтаграммы */

Поделиться с друзьями: