Посмотрим на вариацию этой идеи. Что если вы захотели бы применить случайные числа в каком-нибудь приложении? Поскольку обслуживающая программа на стороне сервера позволяет указать количество возвращаемых чисел, то было бы логично сохранить их в буфере. Учитывая, что при обращении к удаленному серверу задержки неизбежны, следует сразу заполнить буфер во избежание лишних запросов по сети.
В листинге 18.5 эта мысль реализована. Буфер заполняется отдельным потоком и совместно используется всеми экземплярами класса. Размер буфера и «нижняя отметка» ( @slack
) настраиваются; какие значения задать в реальной программе, зависит от величины задержки при обращении к серверу и от того, как часто приложение выбирает случайное число из буфера.
Листинг 18.5. Генератор случайных чисел с буферизацией
require "net/http"
require "thread"
class TrueRandom
def initialize(min=nil,max=nil,buff=nil,slack=nil)
@buffer = []
@site = "www.random.org"
if ! defined? @init_flag
# Принять умолчания, если они не были заданы явно И
# это первый созданный экземпляр класса...
@min = min || 0
@max = max || 1
@bufsize = buff || 1000
@slacksize = slack || 300
@mutex = Mutex.new
@thread = Thread.new { fillbuffer }
@init_flag = TRUE # Значение может быть любым.
else
@min = min || @min
@max = max || @max
@bufsize = buff || @bufsize
@slacksize = slack || @slacksize
end
@url = "/cgi-bin/randnum" +
"?num=#@bufsize&min=#@min&max=#@max&col=1"
end
def fillbuffer
h = Net::HTTP.new(@site, 80)
resp, data = h.get(@url, nil)
@buffer += data.split
end
def rand
num = nil
@mutex.synchronize { num = @buffer.shift }
if @buffer.size < @slacksize
if ! @thread.alive?
@thread = Thread.new { fillbuffer }
end
end
if num == nil
if @thread.alive?
@thread.join
else
@thread = Thread.new { fillbuffer }
@thread.join
end
@mutex.synchronize { num = @buffer.shift }
end
num.to_i
end
end
t = TrueRandom.new(1,6,1000,300)
count = {1=>0, 2=>0, 3=>0, 4=>0, 5=>0, 6=>0}
10000.times do |n|
x = t.rand
count[x] += 1
end
p count
# При одном прогоне:
# {4=>1692, 5=>1677, 1=>1678, 6=>1635, 2=>1626, 3=>1692}
18.2.2. Запрос к официальному серверу времени
Как мы и обещали, приведем программу для обращения к NTP-серверу в сети (NTP — Network Time Protocol (синхронизирующий сетевой протокол). Показанный ниже код заимствован с небольшой переработкой у Дэйва Томаса.
require "net/telnet"
timeserver = "www.fakedomain.org"
local = Time.now.strftime("%H:%M:%S")
tn = Net::Telnet.new("Host" => timeserver,
"Port" => "time",
"Timeout" => 60,
"Telnetmode" => false)
msg = tn.recv(4).unpack('N')[0]
# Преобразовать смещение от точки отсчета
remote = Time.at(msg — 2208988800).strftime("%H:%M:%S")
puts "Местное : #{local}"
puts "Удаленное : #{remote}"
Мы устанавливаем соединение и получаем четыре байта. Они представляют 32-разрядное число в сетевом (тупоконечном) порядке байтов. Это число преобразуется в понятную форму, а затем — из смещения от точки отсчета в объект Time
.
Мы не указали имя реального сервера. Дело в том, что его полезность часто зависит от того, где вы находитесь. Кроме того, многие серверы ограничивают доступ, так что для запроса вы должны получить разрешение или хотя бы уведомить владельца. Поисковая машина поможет найти открытый NTP-сервер в радиусе 1000 км от вас.
18.2.3. Взаимодействие с РОР-сервером
Многие серверы электронной почты пользуются почтовым протоколом (Post Office Protocol — POP). Имеющийся в Ruby класс POP3
позволяет просматривать заголовки и тела всех сообщений, хранящихся для вас на сервере, и обрабатывать их как вы сочтете нужным. После обработки сообщения можно удалить.
Для создания объекта класса Net::POP3
нужно указать доменное имя или IP-адрес сервера; номер порта по умолчанию равен 110. Соединение устанавливается только после вызова метода start
(которому передается имя и пароль пользователя).
Вызов метода mails
созданного объекта возвращает массив объектов класса POPMail
. (Имеется также итератор each
для перебора этих объектов.)
Объект POPMail
соответствует одному почтовому сообщению. Метод header
получает заголовки сообщения, а метод all
— заголовки и тело (у метода all
, как мы вскоре увидим, есть и другие применения).
Фрагмент кода стоит тысячи слов. Вот пример обращения к серверу с после дующей распечаткой темы каждого сообщения:
Читать дальше
Конец ознакомительного отрывка
Купить книгу