TCPServer bug or and interface change?

Looking in a way to use ruby to make some servers (like
http,pop3,smtp) I discovered that the TCPServer implementation doesn’t
allow set the queue_length size that is passed to “listen” and it uses
a fixed value of “5”.

That value is too low for a high trafic server every implementation I
looked in (GServer, wwwd, httpserver) uses the TCPServer call to
initialize the listen master socket and when we test it with
concurrent connections it simply blocks for a long time on my linux
machines.

Changing the interface to allow set the queue length make it works
under load.

diff from the ruby-1.6.7/ext/socket/socket.c
cut from here ------------------------------------------
787c787
< open_inet(class, h, serv, type)

···

open_inet2(class, h, serv, queue_length, type)
873c873
< listen(fd, 5);


  listen(fd, FIX2INT(queue_length));

880a881,888

open_inet(class, h, serv, type)
VALUE class, h, serv;
int type;
{
return open_inet2(class,h,serv,5,type);
}

static VALUE
1042c1050,1065
<


switch(argc){
  case 1:
      return open_inet2(class, 0, argv[0], INT2FIX(5), INET_SERVER);
      break;
  case 2:
      return open_inet2(class, argv[0], argv[1], INT2FIX(5), INET_SERVER);
      break;
  case 3:
      return open_inet2(class, argv[0], argv[1], argv[2], INET_SERVER);
      break;
  default:
      rb_raise(rb_eArgError, "You must supply 1 to 3 parameters.");
      break;
}

/*
1046a1070
*/
cut to here ------------------------------------------

After applying this patch the TCPServer accepts a third parameter for
the queue_length:

TCPServer.new(host,port,queue_length=5)

Old code should remain working since the default value is what was
used before hard coded.

If you whant to test the difference setting the queue length you can
use the apache utility called “ab” short for “apache bench”.

code example 1:
cut form here--------------------------------------------
require 'socket’
port = (ARGV[0] || 9999).to_i
server = TCPServer.new(‘127.0.0.1’,port)
while (session = server.accept)
puts "Request: #{session.gets}"
session.print "HTTP/1.1 200/OK\r\nContent-type: text/html\r\n\r\n"
session.print "

#{Time.now}

\r\n"
session.close
end
cut to here--------------------------------------------

using : "ab -c 1 -n 100 http://127.0.0.1:9999/"
requests per second = 622
time taken : 0.16 seconds

using : "ab -c 20 -n 100 http://127.0.0.1:9999/"
requests per second = 6
time taken : 16.22 seconds

code example 2:
cut form here--------------------------------------------
require 'socket’
port = (ARGV[0] || 9999).to_i
server = TCPServer.new(‘127.0.0.1’,port,511)
while (session = server.accept)
puts "Request: #{session.gets}"
session.print "HTTP/1.1 200/OK\r\nContent-type: text/html\r\n\r\n"
session.print "

#{Time.now}

\r\n"
session.close
end
cut to here--------------------------------------------

using : "ab -c 1 -n 100 http://127.0.0.1:9999/"
requests per second = 622
time taken : 0.16 seconds

using : "ab -c 20 -n 100 http://127.0.0.1:9999/"
requests per second = 622
time taken : 0.16 seconds

using : "ab -c 100 -n 1000 http://127.0.0.1:9999/"
requests per second = 600
time taken : 1.6 seconds

All tests was done in a celeron 700MHZ with linux 7.3

Hi,

Looking in a way to use ruby to make some servers (like
http,pop3,smtp) I discovered that the TCPServer implementation doesn’t
allow set the queue_length size that is passed to “listen” and it uses
a fixed value of “5”.

Is it late setting after TCPServer.new?

server = TCPServer.new(‘127.0.0.1’,port)
server.listen(511)

After applying this patch the TCPServer accepts a third parameter for
the queue_length:

I’m not against to adding the parameter, but

diff from the ruby-1.6.7/ext/socket/socket.c
We perfer unified diff (and I like to use -p option).

cut from here ------------------------------------------
787c787
< open_inet(class, h, serv, type)

open_inet2(class, h, serv, queue_length, type)
873c873
< listen(fd, 5);


  listen(fd, FIX2INT(queue_length));

queue_length in open_inet2() seems a VALUE.

880a881,888

open_inet(class, h, serv, type)
VALUE class, h, serv;
int type;
{
return open_inet2(class,h,serv,5,type);
}

But here an int 5 is passed.

···

At Tue, 23 Jul 2002 07:45:28 +0900, Domingo Alvarez Duarte wrote:


Nobu Nakada

Hi,

Of course you could use the Socket class in a way similar to this:

a = [Socket::AF_INET, 80, 127,0,0,1,""].pack(‘snCCCCa8’)
ms = Socket::new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
ms.bind(a)
ms.listen(511)
while true
Thread::new(ms.accept) do |xx|

end
end

Brad.

nobu.nokada@softhome.net wrote in message news:200207230819.g6N8JHc16852@sharui.nakada.kanuma.tochigi.jp

Hi,

Looking in a way to use ruby to make some servers (like
http,pop3,smtp) I discovered that the TCPServer implementation doesn’t
allow set the queue_length size that is passed to “listen” and it uses
a fixed value of “5”.

Is it late setting after TCPServer.new?

server = TCPServer.new(‘127.0.0.1’,port)
server.listen(511)

After applying this patch the TCPServer accepts a third parameter for
the queue_length:

I’m not against to adding the parameter, but

diff from the ruby-1.6.7/ext/socket/socket.c
We perfer unified diff (and I like to use -p option).

cut from here ------------------------------------------
787c787
< open_inet(class, h, serv, type)

open_inet2(class, h, serv, queue_length, type)
873c873
< listen(fd, 5);


  listen(fd, FIX2INT(queue_length));

queue_length in open_inet2() seems a VALUE.

880a881,888

open_inet(class, h, serv, type)
VALUE class, h, serv;
int type;
{
return open_inet2(class,h,serv,5,type);
}

But here an int 5 is passed.

You are right about the formal parameter, although a VALUE is equal an
int and the compiler didn’t complained, lets change that to:

open_inet(class, h, serv, type)
VALUE class, h, serv;
int type;
{
return open_inet2(class,h,serv,INT2FIX(5),type);
}

And trying to set the listen queue after new doesn’t work .

···

At Tue, 23 Jul 2002 07:45:28 +0900, > Domingo Alvarez Duarte wrote:

Hi,

socket.c-1.6.diff.gz (1.23 KB)

socket.c-1.7.diff.gz (1.37 KB)

···

At Thu, 25 Jul 2002 07:08:25 +0900, Domingo Alvarez Duarte wrote:

You are right about the formal parameter, although a VALUE is equal an
int and the compiler didn’t complained, lets change that to:

open_inet(class, h, serv, type)
VALUE class, h, serv;
int type;
{
return open_inet2(class,h,serv,INT2FIX(5),type);
}

The source you have seems a quite old. open_inet() has been
modified to ensure releasing resources on exceptions.