Socket problem

Hello

I am new to ruby, but I think it’s excellent language - no doubt the
best one (cleanest, fastest to write in etc.) I ever used :slight_smile:

I’m trying to write a NNTP client and I encountered some problems with
sockets. I have to read server response from socket. The response is
multiline and ends with “.\r\n”. Is there any way to get whole response
at once? At the moment I use gets and read the response line by line,
which is horribly inefficient. In C program I used ioctl FIONREAD call
to read number of available bytes and then fetched them with read, but
can’t get this call working in ruby - always get “Bad address
(Errno::EFAULT” error. And if I use recv with arbitrary number (eg.
10000) it hangs forever.

Thanks for any help.

···


Marek Janukowicz
http://starware.one.pl

Hi,

···

At Thu, 19 Dec 2002 11:53:24 +0900, Child wrote:

I’m trying to write a NNTP client and I encountered some problems with
sockets. I have to read server response from socket. The response is
multiline and ends with “.\r\n”. Is there any way to get whole response
at once? At the moment I use gets and read the response line by line,
which is horribly inefficient. In C program I used ioctl FIONREAD call
to read number of available bytes and then fetched them with read, but
can’t get this call working in ruby - always get “Bad address
(Errno::EFAULT” error. And if I use recv with arbitrary number (eg.
10000) it hangs forever.

socket.gets(“\r\n.\r\n”)


Nobu Nakada

“Child” child@t9.ds.pwr.wroc.pl wrote in message
news:slrnb02blb.ejn.child@child.t9.ds.pwr.wroc.pl…

Hello

I am new to ruby, but I think it’s excellent language - no doubt the
best one (cleanest, fastest to write in etc.) I ever used :slight_smile:

I’m trying to write a NNTP client and I encountered some problems with
sockets. I have to read server response from socket. The response is
multiline and ends with “.\r\n”. Is there any way to get whole response
at once? At the moment I use gets and read the response line by line,
which is horribly inefficient. In C program I used ioctl FIONREAD call
to read number of available bytes and then fetched them with read, but
can’t get this call working in ruby - always get “Bad address
(Errno::EFAULT” error. And if I use recv with arbitrary number (eg.
10000) it hangs forever.

Thanks for any help.


Marek Janukowicz
http://starware.one.pl

Below is a pretty basic server that makes use of “select” to do what you
want to do.
You have to be a little careful with “select”. I returns nil if nothing is
going on
with the read end of the socket. If something is there to read. It will
return an
array. However, it will also return an array of three empty arrays if the
socket is closed
on the other end, at least it does in Windows. Thus, the begin and rescue
sections.
Of course I’m assuming the protocol you are using doesn’t permit lines with
only a \r\n unless
the line marks the end of the data.

class SocketProc
def initalize(session)
@sess=session
end
def readIn()
dataIn=“”
while true
begin
foo=select([@sess],nil, nil, timeoutvalue)
if foo
bar=@sess.readline
@dataIn.concat(bar)
break if bar=~/^\r\n/
end
rescue
@sess.close
end
end
processData (dataIn)
end
def processData(dataIn)
#do what you want including writing to @sess
@sess.close
end
end

server=TCPServer.new(port)
BasicSocket.do_not_reverse_lookup=true
#the do_not_reverse_lookup is necessary if you want some speed. By default
it is false.
while (session=server.accept) do
Thread.new(session) do |aSession|
z=SocketProc.new(aSession)
while aSession
z.readIn
end
end
end

In article 200212190343.gBJ3hZb22278@sharui.nakada.kanuma.tochigi.jp,
nobu.nokada@softhome.net writes:

socket.gets(“\r\n.\r\n”)

Unfortunately it doesn’t work if the text response is empty.

···


Tanaka Akira

Hi,

···

At Thu, 19 Dec 2002 12:47:59 +0900, Tanaka Akira wrote:

socket.gets(“\r\n.\r\n”)

Unfortunately it doesn’t work if the text response is empty.

Hmm, then read the status response at first, and read the text
response if present, according to the protocol.

Fetching available data is possible with io/wait extension
library, but I don’t guess it’s reliable to determin the end of
input.


Nobu Nakada

In article 200212190449.gBJ4nBb24264@sharui.nakada.kanuma.tochigi.jp,
nobu.nokada@softhome.net writes:

Hmm, then read the status response at first, and read the text
response if present, according to the protocol.

I expected following case.

list active comp.lang.r*
215 Newsgroups in form “group high low flags”.
comp.lang.rexx 0000037710 0000037671 y
.
list active comp.lang.ru*
215 Newsgroups in form “group high low flags”.
.

(Note that comp.lang.ruby is not exist in our news server.)

The client reads text response after status line.
The status line is “215 … .\r\n”.

So, socket.gets(“\r\n.\r\n”) works on “list active comp.lang.r*” but
not on “list active comp.lang.ru*”. Because the text response with
terminator is only “.\r\n” which is not terminated by “\r\n.\r\n”.

Fetching available data is possible with io/wait extension
library, but I don’t guess it’s reliable to determin the end of
input.

I think ungetc can solve the problem by unget “\r\n” before reading
a text response. But stdio doesn’t support more than one byte with
ungetc.

···


Tanaka Akira

Hi,

At Thu, 19 Dec 2002 14:04:14 +0900,

Hmm, then read the status response at first, and read the text
response if present, according to the protocol.

I expected following case.

list active comp.lang.r*
215 Newsgroups in form “group high low flags”.
comp.lang.rexx 0000037710 0000037671 y
.
list active comp.lang.ru*
215 Newsgroups in form “group high low flags”.
.

(Note that comp.lang.ruby is not exist in our news server.)

The client reads text response after status line.
The status line is “215 … .\r\n”.

So, socket.gets(“\r\n.\r\n”) works on “list active comp.lang.r*” but
not on “list active comp.lang.ru*”. Because the text response with
terminator is only “.\r\n” which is not terminated by “\r\n.\r\n”.

Sorry, misunderstood.

response = socket.gets
if response.has_text?
while s = socket.gets(“.\r\n”)
response << s
break if /^.\r\n\z/ =~ s
end
end

Fetching available data is possible with io/wait extension
library, but I don’t guess it’s reliable to determin the end of
input.

I think ungetc can solve the problem by unget “\r\n” before reading
a text response. But stdio doesn’t support more than one byte with
ungetc.

ungetc(?\n) and gets(“\n.\r\n”) ?

···


Nobu Nakada

In article 200212190525.gBJ5PNb24601@sharui.nakada.kanuma.tochigi.jp,
nobu.nokada@softhome.net writes:

ungetc(?\n) and gets(“\n.\r\n”) ?

Wow. It will work… especially if a news server spools articles in
LF-form.

···


Tanaka Akira

response = socket.gets
if response.has_text?
while s = socket.gets(“.\r\n”)
response << s
break if /^.\r\n\z/ =~ s
end
end

It is exactly the way I do it at the moment - very slow (speed is very
important when fetching a lot of headers/bodies)

I think ungetc can solve the problem by unget “\r\n” before reading
a text response. But stdio doesn’t support more than one byte with
ungetc.

ungetc(?\n) and gets(“\n.\r\n”) ?

This one should be good.

Thank you for all your answers. Speed is essential to me only when
fetching headers (and I think I can expect no empty headers), so I can
use the “gets (”\r\n.\r\n")" form. I made some tests and the profiler
shows (in a simple example - fetching all articles in group) that the
time used by the fetching method is now about 10-15% instead of 40-50%
:). So I think I’ll use one “gets …” when fetching headers, and
ungetc/loop/whatever in other cases.

Thanks once again!

···

On Thu, 19 Dec 2002 14:25:26 +0900, nobu.nokada@softhome.net wrote:


Marek Janukowicz

This all reminds me - is there a way to make a socket nonblocking?
like O_NONBLOCK in C’s socket() call.

It would seem to me if it could be made nonblocking, a simple call to
sock.readlines() would return all data that has already come in, instead
of hanging until it’s closed like it does with blocking sockets.

Just a thought.

  • Greg
···


Greg Millam
walker at deafcode.com

Hi,

This all reminds me - is there a way to make a socket nonblocking?
like O_NONBLOCK in C’s socket() call.

It would seem to me if it could be made nonblocking, a simple call to
sock.readlines() would return all data that has already come in, instead
of hanging until it’s closed like it does with blocking sockets.

It’ll return when the next data just doesn’t arrive yet.

io/nonblock.rb

require “fcntl”
class IO
def nonblock?
(fcntl(Fcntl::F_GETFL) & File::NONBLOCK) != 0
end

def nonblock=(nb)
f = fcntl(Fcntl::F_GETFL)
if nb
f |= File::NONBLOCK
else
f &= ~File::NONBLOCK
end
fcntl(Fcntl::F_SETFL, f)
end

def nonblock(nb = true)
nb, self.nonblock = nonblock?, nb
yield
ensure
self.nonblock = nb
end
end

···

At Thu, 19 Dec 2002 20:40:28 +0900, Greg Millam wrote:


Nobu Nakada