From: mooring@grimoire.tymnet.com (Ed Mooring)
Newsgroups: comp.lang.perl
Subject: Re: Multiple listeners to a data source
Date: 29 Jul 92 18:36:01 GMT
Organization: BT Tymnet Bit Bucket Brigade

In article <1992Jul29.145654.6622@nstn.ns.ca> daniel@nstn.ns.ca (Daniel MacKay) writes:
>Hello!
>
>I will be bringing up a data source in a few days, (it's a connection to a
>weather satellite) that I'd like people to be able to just telnet to a port
>on my machine, and listen in on- say, five or ten people, all with the same
>data scrolling by, kind of like a broadcast ticker tape machine.
>
>I'm looking for suggestions about how to do it.  The boneheaded solution is
>to set up a "tail -f" on a file that the data's being dumped into- but I
>was hoping to not save the data- it's jillions of lines a day.
>
>The data will be arriving on a TCP port to which a Perl program will be
>attached; the program will pull out selected data sets and pop them into
>individual files.  But I'd also like to make the raw data available to
>whoever wants it in real time.

Here's something I call a broadcast server that does almost exactly
what you want.  Right now it reads stdin, and writes to whomever
makes a connection to it.  It also has some primitive security,
and an even more primitive man page.
-------------------- snip here-------------------- 
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  /home/grimoire/a/mooring/errlogger/bcast
#   /home/grimoire/a/mooring/errlogger/bcast.1
# Wrapped by mooring@grimoire on Wed Jul 29 11:34:09 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f '/home/grimoire/a/mooring/errlogger/bcast' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'/home/grimoire/a/mooring/errlogger/bcast'\"
else
echo shar: Extracting \"'/home/grimoire/a/mooring/errlogger/bcast'\" \(3210 characters\)
sed "s/^X//" >'/home/grimoire/a/mooring/errlogger/bcast' <<'END_OF_FILE'
X#!/usr/local/bin/perl -s
X
X$pat = 'S n C4 x8';
X$inet = 2;
X$echo = 7;
X$smtp = 25;
X$nntp = 119;
X
Xdie "Usage:  $0 port \n" unless @ARGV;
X$this = pack($pat,$inet,$ARGV[0], 0,0,0,0);
X#$this = pack($pat,$inet,2345, 0,0,0,0);
X
Xsocket(LISTENER,2,1,6) || die "Socket $!\n";
Xbind(LISTENER,$this) || die "Bind $!\n";
Xlisten(LISTENER,5) || die "Listen $!\n";
X
X$readbits = $writebits = 8 x "\0";
X# always read from standard input
Xvec($readbits,0,1) = 1;
X
X# and look for new connections
X#
Xvec($readbits,fileno(LISTENER),1) = 1;
X
X$listener = fileno(LISTENER);
X
X$0 = $0;
X#
X# prototype file name
X#
X$sockp = 'clientaa';
Xwhile (1)
X{
X    $rbits = $readbits;
X    $wbits = $writebits;
X    grep(vec($wbits,$_,1) = 1, keys %bcastpending);
X    ($nfound, $timeleft) = select($rbits, $wbits, undef, undef);
X    if ($nfound > 0)
X    {
X	#
X	# we got a hit of some sort
X	# first see if anything to write
X	if ($wbits =~ /[^\0]/)
X	{
X	    $bstr = unpack('b*',$wbits);
X	    for($fd = index($bstr,'1'); $fd >= 0; $fd = index($bstr,'1',$fd+1))
X	    {
X		# we just ignore errors here
X		#
X		$sock = $filenames[$fd];
X		send($sock,$bcastdata,0);
X		delete $bcastpending{$fd};
X	    }
X	}
X	if ($rbits =~ /[^\0]/)
X	{
X	    $bstr = unpack('b*',$rbits);
X	    for($fd = index($bstr,'1'); $fd >= 0; $fd = index($bstr,'1',$fd+1))
X	    {
X		if ($fd == 0)
X		{
X		    # deal with stdin
X		    $incount = sysread(STDIN,$bcastdata,1024);
X		    if ($incount == 0)
X		    {
X			# lost our connection
X			die "EOF from source\n";
X		    }
X		    elsif ($incount < 0)
X		    {
X			# error
X			die "Error from source($!)\n" if ($! !~ /Interrupted/);
X		    }
X		    grep($bcastpending{$_} = 1, keys %active);
X		}
X		elsif ($fd == $listener)
X		{
X		    # deal with cloning new socket
X		    $newsock = $sockp++;
X		    if ($addr = accept($newsock,LISTENER))
X		    {
X			#
X			# see if we like this host
X			#
X			($fam,$port,$inetaddr) = unpack('SSL',$addr);
X			if ($verbose)
X			{
X			    $hostname = gethostbyaddr($addr, 2);
X			    printf "Connection from $hostname %x %d\n", $inetaddr, $port;
X			}
X			# change inet address to match your site
X			if ($inetaddr != 0x7f000001 && $inetaddr & 0xffff0000 != 0x83920000)
X			{
X			    #
X			    # not a host we like, bounce it.
X			    #
X			    close ($newsock);
X			    if ($verbose)
X			    {
X				$hostname = gethostbyaddr($addr, 2);
X				printf "Connection refused from $hostname %x %d\n", $inetaddr, $port;
X			    }
X			}
X
X			#
X			# set bit vectors for later use
X			#
X			vec($readbits,fileno($newsock),1) = 1;
X			$bcastpending{fileno($newsock)} = 1 if length $bcastdata;
X			$active{fileno($newsock)} = 1;
X			$filenames[fileno($newsock)] = $newsock;
X		    }
X		    else
X		    {
X			die "Error on accept $!\n";
X		    }
X		}
X		else
X		{
X		    # read data from socket and toss, check for eof
X		    $sock = $filenames[$fd];
X		    $incount = read($sock,$waste,1024);
X		    if ($incount == 0)
X		    {
X			# lost our connection
X			#
X			# reset bit vectors
X			#
X			vec($readbits,$fd,1) = 0;
X			$filenames[$fd] = '';
X			delete $bcastpending{$fd};
X			delete $active{$fd};
X		    }
X		    elsif ($incount < 0)
X		    {
X			# error
X			die "Error from socket($!)\n" if ($! !~ /Interrupted/);
X		    }
X		}
X	    }
X	}
X    }
X    elsif ($nfound < 0)
X    {
X	die "Error ($!) on select\n" unless $! =~ /Interrupted/;
X    }
X}
Xexit 0;
END_OF_FILE
if test 3210 -ne `wc -c <'/home/grimoire/a/mooring/errlogger/bcast'`; then
    echo shar: \"'/home/grimoire/a/mooring/errlogger/bcast'\" unpacked with wrong size!
fi
chmod +x '/home/grimoire/a/mooring/errlogger/bcast'
# end of '/home/grimoire/a/mooring/errlogger/bcast'
fi
if test -f '/home/grimoire/a/mooring/errlogger/bcast.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'/home/grimoire/a/mooring/errlogger/bcast.1'\"
else
echo shar: Extracting \"'/home/grimoire/a/mooring/errlogger/bcast.1'\" \(912 characters\)
sed "s/^X//" >'/home/grimoire/a/mooring/errlogger/bcast.1' <<'END_OF_FILE'
X.TH BCAST 1L
X.SH NAME
Xbcast \- broadcast daemon
X.SH SYNOPSIS
X.LP
X.B bcast
X[
X.B \-verbose
X]
X.I port
X.SH DESCRIPTION
X.LP
X.B bcast
Xis a daemon that binds
X.I port
Xon the local machine and broadcasts whatever it receives on its
Xstandard input to any processes that connect to it.  This is done
Xon a ``you snooze, you lose'' basis; that is, if a process hasn't read
Xthe data that
X.B bcast
Xreceived before it gets another chunk,
X.B bcast
Xwill not attempt to buffer the old data.  Any read done by that process
Xwill get the most recently-received data.
X.SH OPTIONS
X.TP
X.B \-verbose
Xputs the program in verbose mode, causing it to print a message whenever
Xa connection is accepted or rejected.
X.SH DIAGNOSTICS
X.LP
XVarious error messages on system call failures.
X.SH NOTES
X.LP
X.B bcast
Xwill not accept connections from any machines outside
Xof the Tymnet sub-network.
X.SH AUTHOR
X.LP
XEd Mooring (mooring@antares.tymnet.com)
END_OF_FILE
if test 912 -ne `wc -c <'/home/grimoire/a/mooring/errlogger/bcast.1'`; then
    echo shar: \"'/home/grimoire/a/mooring/errlogger/bcast.1'\" unpacked with wrong size!
fi
# end of '/home/grimoire/a/mooring/errlogger/bcast.1'
fi
echo shar: End of shell archive.
exit 0

Regards,
Ed Mooring (mooring@tymix.tymnet.com 408-922-7504)
Newsgroups: comp.lang.perl