UNIX: разработка сетевых приложений — страница 38 из 42

Различные исходные коды

Г.1. Заголовочный файл unp.h

Почти каждая программа в этой книге начинается с подключения заголовочного файла

unp.h
, показанного в листинге Г.1[1]. Этот файл подключает все стандартные системные заголовочные файлы, необходимые для работы большинства программ, а также некоторые общие системные заголовочные файлы. В нем также определены такие константы, как
MAXLINE
, прототипы функций ANSI С для тех функций, которые мы определяем в тексте (например,
readline
), и все используемые нами функции-обёртки. Сами прототипы в приведенном ниже листинге мы не показываем.

Листинг Г.1. Заголовочный файл unp.h

//lib/unp.h

  1 /* Наш собственный заголовочный файл */


  2 #ifndef __unp_h

  3 #define __unp_h


  4 #include "../config.h" /* параметры конфигурации для данной ОС */

  5 /* "../config.h" генерируется сценарием configure */


  6 /* изменив список директив #include,

  7    нужно также изменить файл acsite.m4 */


  8 #include  /* основные системные типы данных */

  9 #include  /* основные определения сокетов */

 10 #include  /* структура timeval{} для функции select() */

 11 #include  /* структура timespec{} для функции pselect() */

 12 #include  /* структура sockaddr_in{} и другие сетевые

                               определения */

 13 #include  /* inet(3) функции */

 14 #include 

 15 #include  /* для неблокируемых сокетов */

 16 #include 

 17 #include 

 18 #include 

 19 #include 

 20 #include 

 21 #include  /* для констант S_xxx */

 22 #include  /* для структуры iovec{} и ready/writev */

 23 #include 

 24 #include 

 25 #include  /* для доменных сокетов Unix */


 26 #ifdef HAVE_SYS_SELECT_H

 27 #include  /* для удобства */

 28 #endif


 29 #ifdef HAVE_SYS_SYSCTL_H

 30 #include 

 31 #endif


 32 #ifdef HAVE_POLL_H

 33 #include  /* для удобства */

 34 #endif


 35 #ifdef HAVE_SYS_EVENT_H

 36 #include  /* для kqueue */

 37 #endif


 38 #ifdef HAVE_STRINGS_H

 39 #include  /* для удобства */

 40 #endif


 41 /* Три заголовочных файла обычно нужны для вызова ioctl

 42    для сокета/файла: , ,

 43     */

 44 #ifdef HAVE_SYS_IOCTL_H

 45 #include 

 46 #endif

 47 #ifdef HAVE_SYS_FILIO_H

 48 #include 

 49 #endif

 50 #ifdef HAVE_SYS_SOCKIO_H

 51 #include 

 52 #endif


 53 #ifdef HAVE_PTHREAD_H

 54 #include 

 55 #endif


 56 #ifdef HAVE_NET_IF_DL_H

 57 #include 

 58 #endif


 59 #ifdef HAVE_NETINET_SCTP_H

 60 #include 

 61 #endif


 62 /* OSF/1 фактически запрещает recv() и send() в  */

 63 #ifdef __osf__

 64 #undef recv

 65 #undef send

 66 #define recv(a,b,c,d) recvfrom(a,b,c,d,0,0)

 67 #define send(a,b,c,d) sendto(a,b,c,d,0,0)

 68 #endif


 69 #ifndef INADDR_NONE

 70 #define INADDR_NONE 0xffffffff /* должно было быть в  */

 71 #endif


 72 #ifndef SHUT_RD     /* три новые константы Posix.1g */

 73 #define SHUT_RD   0 /* отключение чтения */

 74 #define SHUT_WR   1 /* отключение записи */

 75 #define SHUT_RDWR 2 /* отключение чтения и записи */

 76 #endif


 77 #ifndef INET_ADDRSTRLEN

 78 #define INET_ADDRSTRLEN 16 /* "ddd.ddd.ddd.ddd\0"

 79 1234567890123456 */

 80 #endif


 81 /* Нужно, даже если нет поддержки IPv6, чтобы мы всегда могли

 82    разместить в памяти буфер требуемого размера без директив #ifdef */

 83 #ifndef INET6_ADDRSTRLEN

 84 #define INET6_ADDRSTRLEN 46 /* максимальная длина строки адреса IPv6:

 85 "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx" или

 86 "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:ddd.ddd.ddd.ddd\0"

 87 1234567890123456789012345678901234567890123456 */

 88 #endif


 89 /* Определяем bzero() как макрос, если эта функция отсутствует в

       стандартной библиотеке С */

 90 #ifndef HAVE_BZERO

 91 #define bzero(ptr,n) memset(ptr, 0, n)

 92 #endif


 93 /* В более старых распознавателях отсутствует gethostbyname2() */

 94 #ifndef HAVE_GETHOSTBYNAME2

 95 #define gethostbyname2(host, family) gethostbyname((host))

 96 #endif


 97 /* Структура, возвращаемая функцией recvfrom_flags() */

 98 struct in_pktinfo {

 99  struct in_addr ipi_addr; /* IPv4-адрес получателя */

100  int    ipi_ifindex; /* полученный индекс интерфейса */

101 };


102 /* Нам нужны более новые макросы CMSG_LEN() и CMSG_SPACE(), но в

103    настоящее время их поддерживают далеко не все реализации. Им требуется

104    макрос ALIGN(), но это зависит от реализации */

105 #ifndef CMSG_LEN

106 #define CMSG_LEN(size) (sizeof(struct cmsghdr) + (size))

107 #endif

108 #ifndef CMSG_SPACE

109 #define CMSG_SPACE(size) (sizeof(struct cmsghdr) + (size))

110 #endif


111 /* POSIX требует макрос SUN_LEN(), но он определен

112 не во всех реализациях. Этот макрос 4.4BSD работает

123 независимо от того, имеется ли поле длины */

114 #ifndef SUN_LEN

115 #define SUN_LEN(su) \

116  (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))

117 #endif


118 /* В POSIX "домен Unix" называется "локальным IPC".

119    Но пока не во всех системах определены AF_LOCAL и PF_LOCAL */

120 #ifndef AF_LOCAL

121 #define AF_LOCAL AF_UNIX

122 #endif

123 #ifndef PF_LOCAL

124 #define PF_LOCAL PF_UNIX

125 #endif


126 /* POSIX требует определения константы INFTIM в , но во многих

127    системах она по-прежнему определяется в . Чтобы

128    не подключать все функции работы с потоками, определяем ее здесь.

129    Это стандартное значение, но нет гарантии, что оно равно -1 */

130 #ifndef INFTIM

131 #define INFTIM (-1) /* бесконечный тайм-аут */

132 #ifdef HAVE_POLL_H

133 #define INFTIM_UNPH /* надо указать в unpxti.h, что эта константа

                           определена здесь */

134 #endif

135 #endif


136 /* Это значение можно было бы извлечь из SOMAXCONN в ,

137    но многие ядра по-прежнему определяют его как 5,

       хотя на самом деле поддерживается гораздо больше */

138 #define LISTENQ 1024 /* второй аргумент функции listen() */


139 /* Различные константы */

140 #define MAXLINE  4096 /* максимальная длина текстовой строки */

141 #define BUFFSIZE 8192 /* размер буфера для чтения и записи */


142 /* Определение номера порта, который может быть использован для

       взаимодействия клиент-сервер */

143 #define SERV_PORT      9877  /* клиенты и серверы TCP и UDP */

144 #define SERV_PORT_STR "9877" /* клиенты и серверы TCP и UDP */

145 #define UNIXSTR_PATH "/tmp/unix.str" /* потоковые клиенты и серверы

                                            домена Unix */

146 #define UNIXDG_PATH "/tmp/unix.dg" /* клиенты и серверы протокола

                                          дейтаграмм домена Unix */

147 /* Дальнейшие определения сокращают преобразования типов

       аргументов-указателей */

148 #define SA struct sockaddr


149 #define HAVE_STRUCT_SOCKADDR_STORAGE

150 #ifndef HAVE_STRUCT_SOCKADDR_STORAGE

151 /*

152  * RFC 3493: протокольно-независимая структура адреса сокета

153  */

154 #define __SS_MAXSIZE 128

155 #define __SS_ALIGNSIZE (sizeof(int64_t))

156 #ifndef HAVE_SOCKADDR_SA_LEN

157 #define __SS_PADS1SIZE (__SS_ALIGNSIZE - sizeof(u_char) -

sizeof(sa_family_t))

158 #else

159 #define _SS_PAD1SIZE (__SS_ALIGNSIZE - sizeof(sa_family_t))

160 #endif

161 #define __SS_PAD2SIZE (__SS_MAXSIZE — 2*__SS_ALIGNSIZE)


162 struct sockaddr_storage {

163 #ifdef HAVE_SOCKADDR_SA_LEN

164  u_char ss_len;

165 #endif

166  sa_family_t ss_family;

167  char        __ss_pad1[__SS_PAD1SIZE];

168  int64_t     ss_align;

169  char        __ss_pad2[_SS_PAD2SIZE];

170 };

171 #endif


172 #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

173 /* заданные по умолчанию разрешения на доступ для новых файлов */

174 #define DIR_MODE (FILE_MODE | S_IXUSR | S_IXGRP | S_IXOTH)

175 /* разрешения по умолчанию на доступ к файлам для новых каталогов */


176 typedef void Sigfunc(int); /* для обработчиков сигналов */


177 #define min(a, b) ((а) < (b) ? (a) : (b))

178 #define max(a, b) ((a) > (b) ? (a) : (b))


179 #ifndef HAVE_ADDRINFO_STRUCT

180 #include "../lib/addrinfo.h"

181 #endif


182 #ifndef HAVE_IF_NAMEINDEX_STRUCT

183 struct if_nameindex {

184  unsigned int if_index; /* 1, 2, ... */

185  char *if_name; /* имя, заканчивающееся нулем: "le0", ... */

186 };

187 #endif


188 #ifndef HAVE_TIMESPEC_STRUCT

189 struct timespec {

190  time_t tv_sec; /* секунды */

191  long tv_nsec;  /* и наносекунды */

192 };

193 #endif

Г.2. Заголовочный файл config.h

Для обеспечения переносимости всего исходного кода, используемого в тексте книги, применялась утилита GNU

autoconf
. Ее можно загрузить по адресу
http://ftp.gnu.org/gnu/autoconf
. Эта программа генерирует сценарий интерпретатора с названием configure, который надо запустить после загрузки программного обеспечения в свою систему. Этот сценарий определяет, какие свойства обеспечивает ваша система Unix: имеется ли в структуре адреса сокета поле длины, поддерживается ли многоадресная передача, поддерживаются ли структуры адреса сокета канального уровня, и т.д. В результате получается файл с названием
config.h
. Этот файл — первый заголовочный файл, включенный в
unp.h
(см. предыдущий раздел). В листинге Г.2 показан заголовочный файл
config.h
для BSD/OS 3.0.

Строки, начинающиеся с

#define
, относятся к тем свойствам, которые обеспечены данной системой. Закомментированные строки и строки, начинающиеся с
#undef
, относятся к свойствам, данной системой не поддерживаемым.

Листинг Г.2. Заголовочный файл config.h для BSD/OS

i386-pc-bsdi3.0/config.h

 1 /* config.h. Автоматически генерируется сценарием configure. */

 2 /* Определяем константы, если имеется соответствующий заголовочный файл */

 3 #define CPU_VENDOR_OS "i386-pc-bsdi3.0"

 4 /* #undef HAVE_NETCONFIG_H */ /*  */

 5 /* #undef HAVE_NETDIR_H */    /*  */

 6 #define HAVE_PTHREAD_H 1      /*  */

 7 #define HAVE_STRINGS_H 1      /*  */

 8 /* #undef HAVE_XTI_INET_H */  /*  */

 9 #define HAVE_SYS_FILIO_H 1    /*  */

10 #define HAVE_SYS_IOCTL_H 1    /*  */

11 #define HAVE_SYS_SELECT_H 1   /*  */

12 #define HAVE_SYS_SOCKIO_H 1   /*  */

13 #define HAVE_SYS_SYSCTL_H 1   /*  */

14 #define HAVE_SYS_TIME_H 1     /*  */


15 /* Определена, если можно подключить  и  */

16 #define TIME_WITH_SYS_TIME 1


17 /* Определены, если имеются соответствующие функции */

18 #define HAVE_BZERO 1

19 #define HAVE_GETHOSTBYNAME2 1

20 /* #undef HAVE_PSELECT */

21 #define HAVE_VSNPRINTF 1


22 /* Определены, если прототипы функций есть в заголовочном файле */

23 /* #undef HAVE_GETADDRINFO_PROTO */    /*  */

24 /* #undef HAVE_GETNAMEINFO_PROTO */    /*  */

25 #define HAVE_GETHOSTNAME_PROTO 1       /*  */

26 #define HAVE_GETRUSAGE_PROTO 1         /*  */

27 #define HAVE_HSTRERROR_PROTO 1         /*  */

28 /* #undef HAVE_IF_NAMETOINDEX_PROTO */ /*  */

29 #define HAVE_INET_ATON_PROTO 1         /*  */

30 #define HAVE_INET_PTON_PROTO 1         /*  */

31 /* #undef HAVE_ISFDTYPE_PROTO */       /*  */

32 /* #undef HAVE_PSELECT_PROTO */        /*  */

33 #define HAVE_SNPRINTF_PROTO 1          /*  */

34 /* #undef HAVE_SOCKATMARK_PROTO */     /*  */


35 /* Определены, если определены соответствующие структуры */

36 /* #undef HAVE_ADDRINFO_STRUCT */     /*  */

37 /* #undef HAVE_IF_NAMEINDEX_STRUCT */ /*  */

38 #define HAVE_SOCKADDR_DL_STRUCT 1     /*  */

39 #define HAVE TIMESPEC STRUCT 1        /*  */


40 /* Определены, если имеется указанное свойство */

41 #define HAVE_SOCKADDR_SA_LEN 1    /* в sockaddr{} есть поле sa_len */

42 #define HAVE_MSGHDR_MSG_CONTROL 1 /* в msghdr{} есть поле msg_control */


43 /* Имена устройств XTI для TCP и UDP */

44 /* #undef HAVE_DEV_TCP */               /* большинство здесь */

45 /* #undef HAVE_DEV_XTI_TCP */           /* для AIX */

46 /* #undef HAVE_DEV_STREAMS_XTISO_TCP */ /* для OSF 3.2 */


47 /* При необходимости определяем типы данных */

48 /* #undef int8_t */             /*  */

49 /* #undef int16_t */            /*  */

50 /* #undef int32_t */            /*  */

51 #define uint8_t unsigned char   /*  */

52 #define uint16_t unsigned short /*  */

53 #define uint32_t unsigned int   /*  */

54 /* #undef size_t */             /*  */

55 /* #undef ssize_t */            /*  */

56 /* socklen_t должен иметь тип uint32_t, но configure определяет его

57    как unsigned int. т. к. это значение используется в начале компиляции.

58    иногда до того, как в данной реализации определяется тип uint32_t */

59 #define socklen_t unsigned int  /*  */

60 #define sa_family_t SA_FAMILY_T /*  */

61 #define SA_FAMILY_T uint8_t


62 #define t_scalar_t int32_t /*  */

63 #define t_uscalar_t uint32_t /*  */


64 /* Определены, если система поддерживает указанное свойство */

65 #define IPV4 1       /* IPv4, V в верхнем регистре */

66 #define IPv4 1       /* IPv4, v в нижнем регистре, на всякий случай */

67 /* #undef IPV6 */    /* IPv6, V в верхнем регистре */

68 /* #undef IPv6 */    /* IPv6, v в нижнем регистре, на всякий случай */

69 #define UNIXDOMAIN 1 /* доменные сокеты Unix */

70 #define UNIXdomain 1 /* доменные сокеты Unix */

71 #define MCAST 1      /* поддержка многоадресной передачи */

Г.3. Стандартные функции обработки ошибок

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

if (условие ошибки)

 err_sys(формат printf с любым количеством аргументов);

вместо

if (условие ошибки) {

 char buff[200];

 snprintf(buff, sizeof(buff), формат printf с любым количеством аргументов);

 perror(buff);

 exit(1);

}

Наши функции обработки ошибок используют следующую возможность ANSI С: список аргументов может иметь переменную длину. Более подробную информацию об этом вы найдете в разделе 7.3 книги [68].

В табл. Г.1 показано, в чем заключаются различия между функциями обработки ошибок. Если глобальная целочисленная переменная

daemon_proc
отлична от нуля, то сообщение об ошибке передается функции
syslog
с указанным уровнем, в противном случае оно отправляется в стандартный поток вывода сообщений об ошибках.


Таблица Г.1. Стандартные функции обработки ошибок

Функцияstrerror (errno ?)Завершение ?Уровень syslog
err_dumpДаabort();LOG_ERR
err_msgНетreturn;LOG_INFO
err_quitНетexit(1);LOG_ERR
err_retДаreturn;LOG_INFO
err_sysДаexit(1);LOG_ERR

В листинге Г.3 показаны первые пять функций из табл. Г.1.

Листинг Г.3. Стандартные функции обработки ошибок

//lib/error.c

 1 #include "unp.h"


 2 #include  /* заголовочный файл ANSI С */

 3 #include  /* для syslog() */


 4 int daemon_proc; /* устанавливается в ненулевое значение с

                       помощью daemon_init() */


 5 static void err_doit(int, int, const char*, va_list);


 6 /* Нефатальная ошибка, связанная с системным вызовом.

 7    Выводим сообщение и возвращаем управление */


 8 void

 9 err_ret(const char *fmt , ...)

10 {

11  va_list ap;


12  va_start(ap, fmt);

13  err_doit(1, LOG_INFO, fmt, ap);

14  va_end(ap);

15  return;

16 }


17 /* Фатальная ошибка, связанная с системным вызовом.

18    Выводим сообщение и завершаем работу */


19 void

20 err_sys(const char *fmt)

21 {

22  va_list ap;

23  va_start(ap, fmt);

24  err_doit(1, LOG_ERR, fmt, ap);

25  va_end(ap);

26  exit(1);

27 }


28 /* Фатальная ошибка, связанная с системным вызовом.

29    Выводим сообщение, сохраняем дамп памяти процесса и заканчиваем работу */


30 void

31 err_dump(const char *fmt, ... )

32 {

33  va_list ар;


34  va_start(ap, fmt);

35  err_doit(1, LOG_ERR, fmt, ap);

36  va_end(ap);

37  abort(); /* сохраняем дамп памяти и заканчиваем работу */

38  exit(1);

39 }


40 /* Нефатальная ошибка, не относящаяся к системному вызову.

41    Выводим сообщение и возвращаем управление */


42 void

43 err_msg(const char *fmt , ...)

44 {

45  va_list ap;


46  va_start(ap, fmt);

47  err_doit(0, LOG_INFO, fmt, ap);

48  va_end(ap);

49  return;

50 }


51 /* Фатальная ошибка, не относящаяся к системному вызову.

52    Выводим сообщение и заканчиваем работу. */


53 void

54 err_quit(const char *fmt, ...)

55 {

56  va_list ap;


57  va_start(ap, fmt);

58  err_doit(0, LOG_ERR, fmt, ap);

59  va_end(ap);

60  exit(1);

61 }


62 /* Выводим сообщение и возвращаем управление.

63    Вызывающий процесс задает "errnoflag" и "level" */


64 static void

65 err_doit(int errnoflag, int level, const char *fmt, va_list ap)

66 {

67  int errno_save, n;

68  char buf[MAXLINE + 1];


69  errno_save = errno; /* значение может понадобиться вызвавшему

                           процессу */

70 #ifdef HAVE_VSNPRINTF

71  vsnprintf(buf, MAXLINE, fmt, ap); /* защищенный вариант */

72 #else

73  vsprintf(buf, fmt, ap); /* незащищенный вариант */

74 #endif

75  n = strlen(buf);

76  if (errnoflag)

77   snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));

78  strcat(buf, "\n");


79  if (daemon_proc) {

80   syslog(level, buf);

81  } else {

82   fflush(stdout); /* если stdout и stderr совпадают */

83   fputs(buf, stderr);

84   fflush(stderr);

85  }

86  return;

87 }

Приложение Д