/*
 * Copyright (C) 1999-2000 Jonathan R. Hudson
 * Developed by Jonathan R. Hudson <jrhudson@bigfoot.com>
 *
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program; if not, write to the Free Software
 *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


VDKInput, Pipes, Forking and some socket stuff example/tutorial
===============================================================

Introduction
------------

This is a short introduction to using VDK with other input sources,
for example, a pipe or socket (or for that matter, a serial port, a
file (in the manner of tail -f) etc).


VDKInput
--------

VDKInput is a wrapper for gdk_input_add(). It allows you to monitor
input from one or more file descriptors within a VDK application and
define member function(s) to be called when some activity is detected
on that file descriptor. It is particularly useful for pipes, sockets
or serial devices.

This example expands on the VDKInput example distributed with VDK to
v1.2, in that it allows the user to define which user_signal is used to
notify pending I/O.

The class defines a constructor and destructor, and two methods. 

#include "input.h"

VDKInput (VDKForm* obj, 
	int fd,
	GdkInputCondition condition = GDK_INPUT_READ,
	int signal_number = (user_signal+1))

where 
	obj is the parent object
	fd is the file descriptor to be monitored
	condition is a GdkInputCondition
	signal_number is a VDK signal number.

 constructor, wrapper to gdk_input_add()

virtual ~VDKInput()

 destructor, wrapper to gdk_input_remove()

 int getfd();
  Returns the file descriptor associated with the object.

 int getcondition();
  Returns the condition that caused the signal_number to be fired.

Note: VDKInput uses its own signal, INP_SIGNAL, in input.h, as
(user_signal+1) , unless you specify a different number in the
constructor.

In order to demonstrate the use of VDKInput, as well as some
techniques for using child processes with VDK, and catching Unix
signals in C++, please see the the following example programs.

The examples
------------

All the examples have a single theme; there is a window with two
fields (and a Quit button).

	+-------------------------------------------------------+
	|							|
	| Input_____________________________________		|
	|							|
	| +Output--------------------------------------------+	|
	| |						     |  |
	| |						     |  |
	| |						     |  |
	| |						     |  |
	| |						     |  |
	| +--------------------------------------------------+  |
	|							|
	|			[Quit]				|
	+-------------------------------------------------------+
	

A line typed in the "Input" field is sent to the 'helper' proccess,
which will just send it back to the example program, where it is
displayed in the "Output" window. 

helper0
-------

Helper0 is a simple 'C' program that receives data on STDIN and
returns the data on STDOUT, preceeded by a character count. 

For example. if you type "hello\n" on STDIN, it will echo "5: hello\n" on
STDOUT.

If you send it "quit\n", it will echo "4: quit\n" and then terminate.

iodemo0
-------

iodemo0 requires a single parameter, the name of its helper program,
which in this example is 'helper0'.

usage: $ iodemo0 helper0

The program creates anonymous pipes and then forks and execs the
helper program.

Any lines you type are piped to the helper program, which then pipes a 
character count and the line back again.

If you send 'quit', then helper0 will exit, and you will see the
termination caught by the SIGCHLD (unix) signal handler, and EOF on
the pipe.

The program will wait 30 seconds and then exit (unless you press the
quit button first).

iodemo1
-------

usage: $ iodemo1 helper0

iodemo1 is pretty much the same as iodemo0, however, rather than
using pipes, we use anonymous sockets, using the libc socketpair(2)
function. This is slightly neater then using pipe(2), as we don't end
up with 'redundent' pipes. In the this example, we use SOCK STREAM
(reliable byte stream) sockets. This example behaves like example
iodemo0.

iodemo1a
--------

usage: $ iodemo1a helper0

We change iodemo1 to use SOCK_DGRAM (datagram) sockets. This behaves
differently! The EOF on is not seen on the input side when the client
closes.

Goto line 91 

    if(res <= 0 /*|| strncasecmp(buf, "4: quit\n", 5) == 0*/)

and remove the comment. Recompile ... use like iodemo0 again.

iodemo1b
--------

usage: $ iodemo1b helper1

We change iodemo1a.cc to catch the signal SIGUSR1 and arrange for the
new helper program 'helper1' (from helper1.c) to send its parent
SIGUSR1 on exit. This causes iodemo1b to go into its tidy up mode.


iodemo2
-------

iodemo2 works in a different way, in that it uses a new Socket class
and a 'named' socket. iodemo2 does not start the helper program (as
the helper program may now reside on a different machine).

[ note Socket:: is a very trival class, don't use this for anything
serious! ]

usage: $ iodemo2 tcp|udp host:port|filename

The program can use either tcp or udp type sockets and either
"internet" AF_INET or "unix" (AF_UNIX) domain sockets. Please refer to
a good reference (GNU libc 'Info' or Stevens "Unix Network
Programming") if you need more infomation on socket communications.

Where AF_INET sockets are used, the second parameter is a hostname and 
a port number or name, for example:

iodemo2 tcp trespassersw.dld.org:7
iodemo2 udp localhost:echo
iodemo2 udp foo.bar.com:iodemotest 

At my home, the first two examples would use the 'echo' server on the
same machine, one using tcp and the other udp; I could run the first
command from any other machine on my LAN. The echo service (port 7) is
a standard inetd [/etc/services] port and provides just the 'echo'
service we need. You should check that /etc/services defines the
following lines:

echo            7/tcp
echo            7/udp

and inetd.conf

echo    stream  tcp     nowait  root    internal
echo    dgram   udp     wait    root    internal

In the final case (iodemo2 udp foo.bar.com:iodemo), we have defined a
bespoke service, and we will need an echo type server listening on it.

You could define the iodemo service in /etc/services, using a spare
port number, for example:

iodemo          12345/tcp
iodemo          12345/udp

To try this, I've included the perl iodemo-inet.pl program, run this
as:

	iodemo-inet.pl

iodemo-inet.pl assumes a symbolic port name of 'iodemo' unless you
give it a port number (or name) on the command line.

and start as many iodemo2 programs as:

	iodemo2 tcp hostname:iodemo

where hostname is the machine running iodemo-inet.pl.

or
	iodemo-inet.pl 24680
with
	iodemo2 tcp hostname:24680

To use an Unix domain server, we again need to write one first, I've
included a very simple example as 'iodemo-server.pl'.

For example:

	iodemo-unix.pl /tmp/.iodemo

	iodemo2	tcp /tmp/.iodemo

The iodemo-unix.pl server only understands tcp sockets! It can also
handle multiple connections.
	
In gnome-terminal 1
	iodemo-unix.pl /tmp/.iodemo

In gnome-terminal 2
	iodemo2	tcp /tmp/.iodemo

In gnome-terminal 3
	iodemo2	tcp /tmp/.iodemo

In gnome-terminal n
 	iodemo2	tcp /tmp/.iodemo

Other techniques
----------------

This is not an exhaustive set of examples, for example, it does
consider named pipes [mkfifo(2)], which might be used on a single
machine in the manner of iodemo2, or SysV IPC msg functions.

Hope this helps someone.

Jonathan R Hudson, Southampton, England 09 April 2000


	
