Robert K. wrote in post #1074648:
On Tue, Sep 4, 2012 at 2:34 PM, Bernhard B.
[email protected] wrote:
Hi, I am implementing a special kind of socket that takes an io object
(usually a socket) as its argument and then behaves like a socket, but
does some transformations before/after it sends/receives, so in pseudo
code, it could look about like this:
But now I would like to implement the io interface like all those other
Why? I’d rather have a clean separation, i.e. you have a class
representing the connection on a higher level of abstraction and make
it contain a Socket instance. Then implement the methods you need on
the abstract level. You could also have custom classes for messages
(or only one custom class depending on your needs). That way you make
sure clients pass in only what they are allowed to etc.
My transformed socket can send arbitrary strings, so a client can send
anything he likes. The advantage of implementing the same interface as
the other IO classes is that my transformed socket can be reused in more
different ways and passed to other methods that take IO streams as
arguments, for example YAML::load(transformed_socket) might read yaml
directly from my transformed socket, which is not possible if I have a
If you allow the socket to be visible outside your connection class
you allow people to manipulate the socket in arbitrary ways which
might interfere with your message exchange logic. It’s usually better
to have this layered approach and manipulations of the socket which
interfere with your logic (i.e. for example, someone invoking
close_read() while you are still waiting for an answer).
But that is a totally different thing. I did not want to allow my
internal io object to be visible from outside. That is why I could not
use def_delegators etc. I do want to use a layered approach, the lower
layer is the “real” socket, the upper layer is my class. Hence calling
close_read() would not close my internal socket, but it would close my
transformed socket (probably closing the internal socket would be one
step, though) and if the client does it and tries to read afterwards, he
will of course get an IOError from my class because my transformed
socket would already be closed for reading.
Since the length of the messages I send through the internal sockets are
fixed (because of the kind of transformations I do), I should probably
use an internal buffer and for example return the next byte of that
buffer if getc is called and if the buffer is empty, receive the next
message and put it into the buffer. But what should I use for the
buffer? I could use a string, but I guess +=ing and splitting strings
all the time may not be the most efficient way to do it.
You can use String#<< and String#slice! which are more efficient.
But: since the length is fixed I’d probably just have a thread reading
messages which simply uses @socket.read(1234). That will block until
a message is complete.
Thanks a lot, I alredy switched to String#<< and String#slice!. The
message size sent through the internal socket is fixed, but from
outside, it appears like a normal IO stream, so a client may call gets,
but several internal messages may have to be read until a newline
character is found. Or read(n) may be called for n != block_size, so a
buffer is definitely necessary.