Bruce32903 has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks,

I would like to have a simple non-blocking chat example that specifies IP addresses and ports for both the client and the server. My goal is to set up 2 boxes with 3 or 4 quad NIC cards in each. I would set up chat accross each port to start develeloping code. Once I know a little I would change gears from chatting with another Intel Architecture box and use my code to talk to numerous small embedded boxes.

I am one of those people who get to use Perl every now and then to try to do something useful in support of a hardware project. I am not an expert, but I am not exactly a beginner. I have Googled, RTFM'd, bought Lincoln D. Stein's book (a good book) and put a lot of work into this already. I am overwhelmed by code fragments, snippets, "perl chat" examples and text that sounds very useful but the "non blocking" material is on page 3xx or later and is filled with references to "this is just like the previous 3xx pages except for the differences. I'm hoping someone can give me a 5 or 6 line server example with a 5 or 6 line client example that I (consider me a beginner) can use to get a good start that I can build upon.

I'm not necessarily hung up on "nonblocking" but I don't want to get stuck. Years ago someone showed me an example (about 6 lines) where buffers and available status was checked before attempting to send or receive. I don't think that this was "nonblocking", but you never got stuck. If I still had this example I would probably have my starting point.

If the example does not specify both the local and remote IP addresses and ports then I probably will not know how to code for the various 12 or so ports. If the code is just a 3 line snippet then I usually pull the other 3 lines from the wrong example and end up with 6 lines that don't work. Somehow starting with the 60+ line samples (MANY available) and trying to distill them down to 6 lines ends up being a lot of work just to understand that this is not the right set of 6 lines to start with.

Since I want to service 12 or more ports on one box I will probably do some RTFM on threads or forks after I get a simple example running. If the simple "Hello World" had a "Part 2" that easily serviced multiple ports then I would think that Christmas came early.

Yes, I know that UDP does not guarantee delivery, can drop packets in the "real world" and a UDP Hello World will be far short of the final test code I need. I don't mind doing work, but I would sure like to feel like I am getting some bang for my buck. Beginners excel at running around in circles and I am ready for a little linear motion.

Thanks for any help,
Bruce

Replies are listed 'Best First'.
Re: nonblocking UDP, "Hello World" level
by bingos (Vicar) on Dec 06, 2006 at 08:39 UTC

    Seeing the expression 'non-blocking' immediately makes me think POE.

    This is a simple Echo server using POE::Kernel and POE::Wheel::SocketFactory :

    use strict; use warnings; use POE; use POE::Wheel::SocketFactory; use Socket; POE::Session->create( package_states => [ 'main' => [ qw(_start _sock_up _sock_err _sock_read) ], ], ); $poe_kernel->run(); exit 0; sub _start { my ($kernel,$heap) = @_[KERNEL,HEAP]; $heap->{factory} = POE::Wheel::SocketFactory->new( SocketProtocol => 'udp', BindPort => 9090, SuccessEvent => '_sock_up', FailureEvent => '_sock_err', ); return; } sub _sock_up { my ($kernel,$heap,$socket) = @_[KERNEL,HEAP,ARG0]; delete $heap->{factory}; $kernel->select_read( $socket, '_sock_read' ); return; } sub _sock_err { my ($heap,$op,$errno,$errstr) = @_[HEAP,ARG0..ARG2]; delete $heap->{factory}; warn "$op $errno $errstr\n"; return; } sub _sock_read { my ($kernel,$socket) = @_[KERNEL,ARG0]; my $remote_address = recv( $socket, my $message = "", 1024, 0 ); return unless defined $remote_address; send( $socket, $message, 0, $remote_address ) == length($message) or warn "Trouble sending response: $!"; return; }

    There are many examples in the POE Cookbook

      Thanks for the info. I won't get to try working with it until tonight. I did find various pages while Googling that used POE. What stopped me were the references to "kernel" and "heap" and the larger size of "simple example" programs. I got the feeling that POE might be an area for Monks and with only a few hours a week to work on this I might not be able to snatch the pebble from his hand.

      Thanks
      Bruce

Re: nonblocking UDP, "Hello World" level
by ikegami (Patriarch) on Dec 06, 2006 at 05:27 UTC
    At the base of your system, use IO::Select to block until data arrives on any sockets or until any socket is ready to write.. I'll see about writting some code. Anything useful will be more than a few lines.
Re: nonblocking UDP, "Hello World" level
by jbert (Priest) on Dec 06, 2006 at 12:11 UTC
    Can I learn some more about your application, please? In return, I'll try to come up with some appropriate code ;-).

    I've just deleted two potential posts trying to second-guess what you want, so I thought I'd just ask instead. Do you want to use UDP because you have very low latency requirements? Is it because you think it will be simpler to start with that and then move to TCP? Will the chat be application-to-application or person-to-person?

    Note that you don't necessarily need a threaded or forked application to handle multiple clients for UDP or TCP.

    Some background info, sorry if you know it all already.

    UDP: each packet sent is standalone. There is no concept of connection. Packets can be dropped and also delivered out of order. If your server side doesn't need to do anything else, it can sit in a loop reading packets from the socket and responding to them, without any nonblocking, selecting, forking or threading.

    TCP: a connection is made to a server socket which is listening on a specific port. The server accept()'s the new connection, which creates a socket for that two-way connection. Data read and written is transmitted reliably and in-order. The server needs to manage multiple sockets: the listening socket and all current connections. This can be done with threading/forking or using select (or it's OO wrapper IO::Select), without any need for non-blocking (if the server is doing nothing else).

      I am part of a team working on something that is not a "code project". I am actually asking these questions with two goals in mind.

      1) One of my responsibilities is to have small ASAP microcontroller boards with sensors and status lines communicating back to an admin app over ethernet. I don't have final specs yet, but I think that UDP is a requirement placed upon me (thus, no TCP/IP). This will probably be binary data and thus "line operations" that sense "end of line" are probably not an option. That is probably the limit of what I can say about goal #1.

      2) The other goal is to be able to pump and verify many channels of ethernet traffic during testing. I am probably free to use UDP or TCP/IP, and I probably am free to use binary data or ASCII data terminated by CR, LF. I do need to be very confident that I am in control of what data is being sent through each RJ-45.

      The chat application at a "Hello World" level is just to get me started. Developing the code to deal with empty buffers, full buffers, fragmented packets, lost packets, etc should be quite a task by itself. I would like to do as much work as possible in an Intel Architecture to Intel Architecture environment before I put "under development" hardware and "under development" software at the other end of the wire.

      Thank you,
      Bruce

Re: nonblocking UDP, "Hello World" level
by zentara (Cardinal) on Dec 06, 2006 at 13:56 UTC
Re: nonblocking UDP, "Hello World" level
by jbert (Priest) on Dec 06, 2006 at 19:33 UTC
    OK, as a first step, here is some sample code. A simple UDP sender and receiver, using the basic Socket module. These are blocking (hmm...is UDP send blocking? I'd expect the packet to simply be discarded if there was a local send buffer and it was full).

    UDP receiver:

    #!/usr/bin/perl use strict; use warnings; use Socket; main(@ARGV); exit 0; sub main { my $listen_sock; socket($listen_sock, PF_INET, SOCK_DGRAM, getprotobyname('udp')) or die("Can't create socket : $!"); my $port = 1025; bind($listen_sock, sockaddr_in($port, INADDR_ANY)) or die("Can't bind socket to port $port"); my $max_packet_size = 65536; my $buffer; while (1) { my $sender = recv($listen_sock, $buffer, $max_packet_size, 0); my ($sender_port, $sender_ip) = sockaddr_in($sender); $sender_ip = inet_ntoa($sender_ip); print "Received ", length $buffer, " bytes from $sender_ip:$sender_port", "\n"; } }
    and the sender:
    #!/usr/bin/perl use strict; use warnings; use Socket; main(@ARGV); exit 0; sub main { my $socket; socket($socket, PF_INET, SOCK_DGRAM, getprotobyname('udp')) or die("Can't create socket : $!"); my $remote_port = 1025; my $remote = sockaddr_in($remote_port, inet_aton("localhost")); while (1) { my $buffer = "The time is now : ", scalar localtime(); send($socket, $buffer, 0, $remote) or die("Failed to send : $!"); sleep 1; } }
    You should be able to run them both up on the same box and they'll talk to each other (by rendezvousing on localhost:1025).

    Points of note:

    • the INADDR_ANY translates to an IP address of 0.0.0.0, which means "listen on this port all local IP addresses, including 127.0.0.1". You generally want this for servers.
    • try running two clients. You don't need any additional code on the server. You just see twice as many packets coming in.
    • there isn't any concept of connection. The information about who sent each packet is retrieved from the return code recvcall for each packet.
    There are almost certainly higher level modules on CPAN which will help you avoid some of the messing around with using sockaddr_in to pack/unpack the ip:port combinations.

    You don't need to edit the listener to get things running between two hosts, just pass in the appropriate hostname/ip to inet_aton on the sender.

    If you want to go non-blocking, then you're into a potentially different ball game. You'll be wanting to write your entire app from an event-driven perspective, not just the networking parts, and drinking the POE kool-aid might be a good idea in that case.