From: Bernhard Brodowsky Date: 2012-09-05T02:53:26+09:00 Subject: Re: Socket Decorator Robert Klemme wrote in post #1074648: > On Tue, Sep 4, 2012 at 2:34 PM, Bernhard Brodowsky > 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 >> io classes, > > 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 different interface. > 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. > > Kind regards > > robert 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. -- Posted via http://www.ruby-forum.com/.