#!/usr/local/bin/perl
# $Id: echo.perl,v 1.6 2001/05/27 16:44:24 doug Exp $
# http://www.bagley.org/~doug/shootout/

use Socket;

my $DATA = "Hello there sailor\n";

sub server_sock {
    local *SS;
    socket(SS, PF_INET, SOCK_STREAM, 0) or
    die "server/socket ($!)";
    setsockopt(SS, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or
    die "server/setsockopt ($!)";
    bind(SS, sockaddr_in(0, INADDR_LOOPBACK)) or
    die "server/bind ($!)";
    listen(SS, 2);
    return(*SS);
}

sub get_port {
    local *SK = shift;
    (sockaddr_in(getsockname(SK)))[0];
}

sub client_sock {
    my $port = shift;
    local *CS;
    socket(CS, PF_INET, SOCK_STREAM, getprotobyname('tcp')) or
    die "client/socket ($!)";
    connect(CS, sockaddr_in($port, INADDR_LOOPBACK)) or
    die "client/connect ($!)";
    return(*CS);
}

sub echo_client {
    my($N, $port) = @_;
    local *SOCK = client_sock($port);
    select(SOCK);
    $| = 1;
    for my $i (0..($N-1)) {
    print $DATA;
    my $ans = <SOCK>;
    ($ans eq $DATA) or die qq{client: "$DATA" ne "$ans"};
    }
    close SOCK;
}

sub echo_server {
    my($N) = @_;
    local *SSOCK = server_sock();
    my $port = get_port(*SSOCK);
    my $pid = fork;
    defined $pid or die "server/fork ($!)";
    if ($pid) {
    # parent is server
    local *CSOCK;
    accept(CSOCK, SSOCK) or die "server/accept ($!)";
    select(CSOCK);
    $| = 1;
    my $n = 0;
    while (<CSOCK>) {
        print $_;
        $n += length($_);
    }
    select(STDOUT);
    print "server processed $n bytes\n";
    } else {
    # child is client
    echo_client($N, $port);
    }
    wait();
}

sub main {
    my $N = $ARGV[0] || 1;
    echo_server($N);
    exit(0);
}

main();