[#954] Rational — keiju@... (Keiju ISHITSUKA)

けいじゅ@SHLジャパンです.

23 messages 1996/10/30
[#955] Re: Rational — matz@... (Yukihiro Matsumoto) 1996/10/30

まつもと ゆきひろです.

[#968] Re: Rational 1996/10/31

けいじゅ@SHLジャパンです.

[ruby-list:760] httpd.rb (Re: sample/svr.rb (socket, select))

From: Hara Shin-ichiro <sinara@...>
Date: 1996-10-05 04:19:43 UTC
List: ruby-list #760
原です。

>黒田です.

>>sample/ にある svr.rb ですが、何度か動かして見たのですが、
>>中身を今回はじめてよみました。^^;
>>
>>これ面白いです。fork も Thread も使わずに、複数のクライアントに
>>サービスしている。

>この svr.rb を良く理解せずに httpd に書きかえていたのですが,
>netscape みたいなクライアントが, 一度に複数のコネクションを張
>ろうとすると, うまくいかない(要求したとおりのものをくれない)の
>ですが, しょうがないんでしょうか? こういう時こそ thread の出番
>なんでしょうか?

奇遇ですねー。私も朝から Thread の練習にと思って、同じことをしてい
ました。私の考えた方法は、そのまんま svr.rb をぱくって

def session(s)
# s でなんとか
  s.close
  $m.syncronize{ $threads -= 1 }
end
while TRUE
  nsock = select(socks)
  next if nsock == nil
  for s in nsock[0]
    if s == gs
      ns = s. accept
      socks.push(ns)
    else
      socks.delete(s)
      while $threads >= $max_thread; sleep 0.1; end;
      $m.syncronize{ $threads += 1 }
      Thread.start{ session(s) }
    end
  end
end

と、いう風にしてみました。これでいいのかなー。

ちなみに Thread なし(下のスクリプトで $max_thread = 0)でもちゃ
んと netscape で読めたので、黒田さんのうまくいかない原因は別の
所にあるのではないでしょうか?

で、結論として Thread を使わないほうが倍も早いです。^^; Thread
の実装にファンクションコールの select を使っているそうなので、
どっちにしろ select を使っている事になり、Thread のご利益が少
ないのかもしれません。server の CPU が速くて、コネクションあた
りの通信速度が遅く、送るデータサイズが大きければ結果はちがうと
おもいますが。

sleep 0.1 をやめると、さらに極端にパフォーマンスが落ちます。普
通、各スレッドの働き具合をコントロールするにはどうするのでしょ
うか?

以下に httpd.rb をベタに張り付けます。いちおう手もとの CGI, 
clickable map はうごいていますが、あくまで練習。けっこういい
かげんです。

---------^ httpd.rb
#!/usr/local/bin/ruby
# USAGE: httpd.rb [PORT]
$port = 80
$max_thread = 4
$host_name = "www.nagaokaut.ac.jp"
$root_dir = "/usr/local/etc/httpd"
$user_dir = "public_html"
$html_dir = $root_dir + "/htdocs"
$icons_dir = $root_dir + "/icons"
$icons_vdir = "/icons"
$cgi_dir = $root_dir + "/cgi-bin"
$cgi_vdir = "/cgi-bin"
$default_html = ["index.html", "index.shtml"]
$version = "MIME-Version: 1.0\nServer: httpd.rb/1.1\n"

require "thread" if $max_thread > 0
require "socket"
$envs = ['HTTP_REFERER','CONTENT_LENGTH','QUERY_STRING',
  'REQUEST_METHOD', 'PATH_INFO', 'PATH_TRANSLATED', 'SCRIPT_NAME']
  
def read_head(s)
  phost_name = s.peeraddr[2]
  while s.gets
    if /(GET|POST)\s+(\S*)\s*(HTTP.*)/i
      rqst, html, vers = $1.upcase, $2, $3
      ENV['REQUEST_METHOD'] = rqst
      print "(#{phost_name}): ", $_
    end
    ENV['HTTP_REFERER'] = $1 if /Referer: *(.+)/i
    ENV['CONTENT_LENGTH'] = $1 if /Content-length: *(.+)/i
    break if /^\r?\n$/
  end
  param = ''
  html, param = $1, $2 if html =~ /([^\?]+)\?(.*)/
  [rqst, html, param, vers]
end  

def fname_complete(fname)
  if fname =~ /\/$/
    for i in $default_html
      filename = fname + i
      if FileTest.file?(filename)
	return filename
      end
    end
  end
  fname
end

def mk_local_name(html)
  filename = if html =~ /^#$icons_vdir\//o
    html.sub(/^#$icons_vdir/o, $icons_dir)
  elsif html =~ /^\/~([^\/]+)/
    html.sub(/^\/~([^\/]+)/, '/home/'+$1+"/#$user_dir/")
  else
    $html_dir + html
  end
  filename
end

def do_cgi(s, rqst, html, param)
  html =~ /^(#$cgi_vdir)(\/[^\/]+)(.*)/o;
  ENV['SCRIPT_NAME'] = $1 + $2
  cgi, map = $cgi_dir + $2, $3
  if map
    ENV['PATH_INFO'] = map
    ENV['PATH_TRANSLATED'] = mk_local_name(map)
  end
  if html !~ /^#$cgi_vdir\/nph-[^\/]+/o
    head = if map then "HTTP/1.0 302 Found" else "HTTP/1.0 200 OK\n" end
    head += $version
  end
  if param != ''
    (param0 = param.dup).gsub!(/\+/, "' '")
    cgi += " '" + param0 + "'"
  end
  if rqst == 'GET'
    ENV['QUERY_STRING'] = param 
    body = `#{cgi}`
  elsif rqst == 'POST'
    str = s.read(ENV['CONTENT_LENGTH'])
    t = IO.popen(cgi, "r+")
    t << str
    t.flush
    body = t.read
    t.close
  end
  [head, body]
end  

def get_image(html)
  filename = mk_local_name(html)
  filename = fname_complete(filename)
  if FileTest.file?(filename)
    type = if filename =~ /\.([^.]+)$/; $1; else; 'plain'; end
    cat = if type =~ /txt|text|htm|plain/i; 'text'; else 'image'; end
    head = "HTTP/1.0 200 Document follows\n" + $version +
      "Content-Type: #{cat}/#{type}\n\n"
    h = open(filename)
    body = h.read
    h.close
  elsif FileTest.directory?(filename)
    head = "HTTP/1.0 302 Found\n" + $version +
      "Content-Type: text/html\n" +
      "Location: http://#$host_name:#$port#{html}/\n\n"
    body = ''  
  else
    head = "HTTP/1.0 404 Not Found\n" + $version +
      "Content-Type: text/html\n\n"
    body = "<HEAD><TITLE>NOT FOUND</TITLE></HEAD>\n" +
      "<HTML><BODY><H2>File(#{html}) Not Found.</H2></BODY><HTML>\n"
  end
  [head, body]
end

def session(s)
  $envs.each{ |e| ENV.delete(e) }
  rqst, html, param, vers = read_head(s)
  if html =~ /^#$cgi_vdir\//o
    head, body = do_cgi(s, rqst, html, param)
  else
    head, body = get_image(html)
  end
  s << head << body
  s.close
  $m.syncronize{ $threads -= 1 } if $max_thread > 0
end

ENV['HTTP_HOST'] = $host_name
ENV['DOCUMENT_ROOT'] = $html_dir
if ARGV.size > 0; $port = ARGV[0]; end
ENV['SERVER_PORT'] = $port.to_s

gs = TCPserver.open($port)
socks = [gs]
printf("START on %d\n", gs.addr[1])

if $max_thread > 0
  $m = Mutex.new
  $threads = 0
end
  
while TRUE
  nsock = select(socks)
  next if nsock == nil
  for s in nsock[0]
    if s == gs
      ns = s. accept
      socks.push(ns)
    else
      socks.delete(s)
      if $max_thread == 0
	session(s)
      else
	while $threads >= $max_thread; sleep 0.1; end;
	$m.syncronize{ $threads += 1 }
	Thread.start{ session(s) }
#	print "New Thread(#$threads threads running).\n"
      end
    end
  end
end
---------$ httpd.rb

---===-=-=-=-=-=-=-=-=======--=-=-=-==-=-===-=-=-=-=-=-=--=-==-=--
                           Shin-ichro Hara(Nagaoka Univ.of Tech.)

In This Thread

Prev Next