windowsperlposixhandle

Perl program on Strawberry on Windows needs "use POSIX" in order to evaluate <$sock> and read a packet from the port


Perl program to read UDP packets & clean the data and transmit to another port.
Without use POSIX; in the program it cannot turn <$sock> into a packet of data.

`$sock` comes from:
    socket($sock, PF_INET, SOCK_DGRAM, getprotobyname('udp'))
    bind($sock, $sockets[0])

with use POSIX data comes in correctly:

!AIVDM,1,1,,B,37Oms:?Oh0bHEEMb=j5A5AtL00N0,0*76" (AIS data)
without that, <$data> appears as "GLOB(0x1563bcda6e8)

PERldocs "strongly discourages" using "use POSIX;" https://perldoc.perl.org/POSIX

    my $data = <$sock>;

should give me data from the socket, without use POSIX; it hangs at this point.


Solution

  • First off, you should add use strict, it would tell you why your program doesn't work without POSIX: PF_INET and SOCK_DGRAM are not defined. With strictness enabled you would get errors like

    Bareword "PF_INET" not allowed while "strict subs" in use at...
    Bareword "SOCK_DGRAM" not allowed while "strict subs" in use at...
    

    Which you can then repair by adding

    use POSIX qw(PF_INET SOCK_DGRAM);
    

    or by adding

    use POSIX ();
    

    and referring to POSIX::PF_INET and POSIX::SOCK_DGRAM, as the documentation suggests. It's only importing everything from POSIX that's an awful idea.

    The problem is that if you don't import and don't use strict, you're passing the strings "PF_INET" and "SOCK_DGRAM" to socket, which get silently interpreted as completely the wrong thing (probably whatever socket type and protocol correspond to the number 0). The socket creation probably fails entirely, but you're not checking $! for an error, so you never find out about it.

    However,

    Using any of the stuff from Socket and POSIX is rather 1990s. It forces you to do an awful lot of error-prone manual work, and requires different invocations for IPv4 and IPv6. On perl 5.20+ you could instead

    use IO::Socket::IP;
    my $sock = IO::Socket::IP->new(Proto => 'udp', LocalPort => $port)
        or die "listening on udp/$port: $IO::Socket::errstr";
    

    which replaces the calls to sockaddr_in, getprotobyname, socket, and bind with a single, much more readable, call. The socket you get from that works in all the same ways as the one you get from socket.

    If for some reason your perl is more than 9 years old, you can use IO::Socket::INET instead of IO::Socket::IP. It works simiarly, except for the IPv6 support, and it's been in core since 2000.

    If you're reading a tutorial or something that tells you to do things the 25-years-outdated way, you may want to throw it away.