in reply to Re: Socket descriptor passed across fork -- hanging
in thread Socket descriptor passed across fork -- hanging

Yeah, I know the line terminators are "wrong" in my server; it's for an existing local protocol with existing clients and servers, so I can't mess with that. So "\n" is "right" for this case, though non-standard.

I see you're going down to the sysread level. That's probably the cleanest solution. I started out using line-mode reads, which was working fine until we uncovered the clients that didn't terminate their last line. Since the value proposition for this server is increasing our flexibility in deploying multiple copies of servers, bundling commands into servers dynamically, and so forth, without having to change and redeploy all the clients, that was kind of a quandry, so I've had to give up line-oriented IO. But because you're using fixed-length packets, you avoid the actual problem that trapped me.

You say in the code that the "next" if accept returns undef isn't necessary any more, but I've seen my $listen->accept() call return EINTR (perl 5.8.8, Linux 2.6.18), so it seems to be necessary still here.

I really appreciate a more sophisticated running example. I've been wondering about things like closing the handles not used after the fork, for example; you seem to think that's worth bothering with.

  • Comment on Re^2: Socket descriptor passed across fork -- hanging

Replies are listed 'Best First'.
Re^3: Socket descriptor passed across fork -- hanging
by Marshall (Canon) on Oct 13, 2011 at 14:28 UTC
    I would like for you to explain what this means:

    until we uncovered the clients that didn't terminate their last line What?

    They closed the socket before sending \r\n?

    My code deals with that.

    because you're using fixed-length packets, you avoid the actual problem that trapped me
    No, not at all.

    I thought that I explained clearly how to deal with an indeterminate length \r\n terminated packet.
    What was not clear?
    Obviously something was not.
    It would help if you could ask the question in a different way.

    Please look at: sysread. sysread() will return with a number of bytes read.
    If the other side sends: "1234\r\n", just look at the end of the buffer to see if there is a line termination (last 2 bytes).
    What's the problem?
    I think that you can easily adapt my code to deal with your requirements.

      They closed their socket without sending \n, yes. Shit happens in the real world; possibly encouraged by servers being generous in what they accept.

      I guess I misread the code or your description of the protocol, sorry.

      What are the tradeoffs of using sysread/syswrite vs. read/print? The obvious one is I have to implement my own line-oriented read for the first line of the protocol, which I have to read and parse. Is dropping down a level going to be of significant benefit, and if so in what?

      I've been finding various strange returns from $sock->read(), including undef with $! empty. However, by taking a desperate leap and treating all errors except EINTR and EAGAIN as end-of-file, my proxy is now successfully passing data back and forth between test clients and real servers (next step will be to more aggressive test clients, and then real clients). For the proxy, treating a read error as EOF isn't really too bad a choice, clearly it's time to close that connection and terminate the proxy child!

        I guess I should explain, that I started with C code and then said, "hey, what would this look like in Perl for the fun of it". I was able to any combination working together. C server/Perl Client, Perl Server/C client or both each way.

        You brought up some good points, one is the "or next" in the accept().
        You say in the code that the "next" if accept returns undef isn't necessary any more, but I've seen my $listen->accept() call return EINTR (perl 5.8.8, Linux 2.6.18), so it seems to be necessary still here. I agree with you and would leave the code as is - my comment is wrong.

        Yes, I would close the sockets that aren't going to be used! This is cheap and solves problems!

        For example, as a parent (the server) after I have forked off a child, I am not going to talk on the active socket. That is the child's job. One reason to close it is assist in freeing resources. If the child decides to hang up on the client close the active socket, then we don't have to worry about what kind of weird stuff might or might not happen because I still have it open in a parent.

        We get into a similar sort of a thing if the child doesn't close the passive socket. We don't want children somehow running off making grandchildern! A child's job is to talk to one client. If somehow some kid had a baby then we have some dual role stuff going on and there can be issues with who reaps who when who dies. "close($passive)"; closes off a lot of possible nasty things at very little cost.

        Keep the fork architecture "clean":
        Parents only listen for new clients, they make a new child to deal with the new client and then go back to listening.

        Children only talk to the client that they are connected to. They die when the need for them to talk to that client goes away. The parent who started them is notified when they die.

        I've been finding various strange returns from $sock->read(), including undef with $! empty. Yes there can be strange things. $! is only set only an error. It is not reset if there is no error. I think in some circumstances undef is just considered "normal status" and is not an error. This is true in the C interface where there is of course no such thing as "undef".

        Systems programming is complicated stuff and there are lots of timing related "yeah but's".

        What are the tradeoffs of using sysread/syswrite vs. read/print? The obvious one is I have to implement my own line-oriented read for the first line of the protocol, which I have to read and parse. Is dropping down a level going to be of significant benefit, and if so in what?

        This is a good question. Perhaps some other Monks will chime in here..

        If you read my code and can see where it can go..Implementing a line oriented protocol is not hard. You have a non-standard "\n" termination of the line but you just check the last character for "\n" to see if the line is finished. $buf is a string, not an array, so you can use a substr() operation, or just run a regex on the $buf chars to see if the last char is "\n";

        The big difference between sysread and read is non-buffered vs buffered. This might lead to lower performance when many lines are being sent at once. Depends upon how smart the readn() is.

        However, it sounds to me like you just need to implement a server that talks with some clients over which you have no control. And if the performance is adequate and if your server's reliability is rock solid, I would "stick a fork in it" and say its done! Normally for something like this: reliability is the main thing...Doesn't crash..doesn't loose data..recovers when a client crashes..function is 100% great in all respects.

        I see ways to make this faster, but if that is not necessary, then you are loosing the argument.

        Kick ass with less than one page of Perl code and go!