Анимация
JavaScript
|
Главная Библионтека 17.13. Серверы без ветвления 629 к Корректно убрать мусор и завершиться. # Этот выход ОЧЕНЬ важен, в противном случае потомок начнет # плодить все больше и больше потомков, что в конечном счете # приведет к переполнению таблицы процессов, exit; Комментарий Программа получилась большой, но ее логика проста: родительский процесс никогда не работает с клиентами сам, а вместо этого ответвляет $PREFORK потомков. Родитель следит за количеством потомков и своевременно плодит процессы, чтобы заменить мертвых потомков. Потомки завершаются после обработки $MAX CLIENTS PER CHILD клиентов. Пример 17.5 более или менее прямолинейно реализует описанную логику. Единственная проблема связана с обработчиками сигналов: мы хотим, чтобы родитель перехватывал SIGINT и убивал своих потомков, и устанавливает для этого свой обработчик сигнала &HUNTSMAN. Но в этом случае нам приходится соблюдать меры предосторожности, чтобы потомок не унаследовал тот же обработчик после ветвления. Мы используем сигналы POSIX, чтобы заблокировать сигнал на время ветвления (см. рецепт 16.20). Используя этот код в своих программах, проследите, чтобы в make new child никогда не использовался выход через return. В этом случае потомок вернется, станет родителем и начнет плодить своих собственных потомков. Система переполнится процессами, прибежит разъяренный системный администратор - и вы будете долго и мучительно жалеть, что не обратили должного внимания на этот абзац. В некоторых операционных системах (в первую очередь - Solaris) несколько потомков не могут вызывать accept для одного сокета. Чтобы гарантировать, что лишь один потомок вызывает accept в произвольный момент времени, придется использовать блокировку файлов. > Смотри также- Описание функции select вperlfunc{\); страница руководства fcntl{2) вашей системы (если есть); документация по стандартным модулям Fcntl, Socket, IO::Select, IO:;Socket и Tie::Refflash; рецепты 17.11-17.12. 17.13. Серверы без ветвления Сервер должен обрабатывать несколько одновременных подключений, но вы не хотите ответвлять новый процесс для каждого соединения. Решение Создайте массив открытых клиентов, воспользуйтесь select для чтения информации по мере ее поступления и работайте с клиентом лишь после получения полного запроса от него, как показано в примере 17.6. Пример 17.6. nonforker #!/usr/bin/perl -w # nonforker - мультиплексный сервер без ветвления use POSIX; use 10; .Socket; use 10;:Select; use Socket; use Fcntl; use Tie::RefHash; Sport = 1685; # Замените no своему усмотрению # Прослушивать порт. Sserver = 10;;Socket;;INET->new(LocalPort => Sport, Listen => 10 ) or die "Cant make server socket; $@\n"; # Начать с пустыми буферами %inbuffer =0; %outbuffer =0; %ready = (); tie %ready, Tie:;RefHash; nonblock(Sserver); Sselect = 10::Select->new(Sserver); # Главный цикл: проверка чтения/принятия, проверка записи, # проверка готовности к обработке while (1) { my Sclient; my $rv; my Sdata; # Проверить наличие новой информации на имеющихся подключениях # Есть ли что-нибудь для чтения или подтверждения? foreach Sclient ($select->can read(1)) { if (Sclient == Sserver) { # Принять новое подключение Sclient = $server->accept(); $select->add($client); nonblock(Sclient); } else { # Прочитать данные Sdata = ; Srv = $client->recv($data, POSIX;;BUFSIZ, 0); unless (defined($rv) && length Sdata) { 17.13. Серверы без ветвления 631 к Это должен быть конец файла, поэтому закрываем клиента delete $inbuffer{$client}; delete Soutbuffer{$client}; delete $ready{$client}; $select->remove($client); close Sclient; next; $inbuffer{$client} .= Sdata; # Проверить, говорят ли данные в буфере или только что й прочитанные данные о наличии полного запроса, ожидающего й выполнения. Если да - заполнить Sready{Sclient} й запросами, ожидающими обработки, while ($inbuffer{$client} = s/(.An) ) { push( @{Sready{Sclient}}, SI ); Й Есть ли полные запросы для обработки? foreach Sclient (keys %ready) { handle(Sclient); Й Сбрасываемые буферы ? foreach Sclient ($select->can write(1)) { Й Пропустить этого клиента, если нам нечего сказать next unless exists $outbuffer{$client}; Srv = $client->send(Soutbuffer{Sclient}, 0); unless (defined $rv) { Й Пожаловаться, но следовать дальше. warn "I was told I could write, but I cant.\n"; next; if (Srv == length Soutbuffer{$client} {$! == POSIX::EWOULDBLOCK) { substr($outbuffer{$client}, 0, Srv) = ; delete Soutbuffer{Sclient} unless length Soutbuffer{$client}; } else { Й He удалось записать все данные и не из-за блокировки, й Очистить буферы и следовать дальше, delete Sinbuffer{Sclient}; delete SoutbufferiSclient}; delete $ready{$client}; Sselect->rernove( Sclient); 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 [ 204 ] 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |