Posted to tcl by colin at Tue Jan 31 16:03:45 GMT 2012view pretty

# rpclass.tcl - remote procedure call classes

::oo::class create ::oo::rpc::server {
    method getpacket {sock} {
	if {[binary scan [read $sock 4] I len] != 1} {
	    error "$sock closed"
	}
	return [read $sock $len]
    }

    method putpacket {sock data} {
	set content [binary format I [string length $data]]$data
	puts -nonewline $sock $content
	flush $sock
    }

    method connect {sock args} {
	chan configure $sock -translation {binary binary} -encoding binary -buffering none
	set constructor [my getpacket $sock]
	set obj [uplevel #0 [self] new {*}$constructor]
	my putpacket $sock $obj

	oo::objdefine $obj {
	    method __dispatch__ {sock} {
		set result {}
		while {1} {
		    catch {my {*}[[info object class [self]] getpacket $sock]} e eo
		    if {[catch {
			[info object class [self]] putpacket $sock [list -options $eo $e]
		    }]} {
			catch {close $sock}
			catch {[self] destroy}
			break
		    }
		}
	    }
	    export __dispatch__
	}

	trace add command $obj delete [list catch [list close $sock]]
	chan event $sock readable [list $obj __dispatch__ $sock]
    }
    export connect

    superclass ::oo::class
    constructor {args} {
	set name [self]

	lassign $args port host body
	if {$body eq ""} {
	    set body $host
	    set opts {}
	} else {
	    set opts [list -myaddress $host]
	}

	set listener [socket -server [list [self] connect] {*}$opts $port]

	next $body

	return [self]
    }
}

::oo::class create ::oo::rpc::client {
    method cleanup {obj sock} {
	catch {$obj destroy}
	set cmd destroy
	catch {
	    puts -nonewline $sock [binary format I [string length $cmd]]$cmd
	    flush $sock
	}
	catch {close $sock}
    }
    method intervene {obj cargs} {
	variable c2hp
	set sock [socket {*}[dict get $c2hp [self]]]
	chan configure $sock -translation {binary binary} -encoding binary -buffering none
	trace add command $obj delete [list [self] cleanup $obj $sock]
	set cmd [binary format I [string length $cargs]]$cargs
	puts -nonewline $sock $cmd; flush $sock
	if {[binary scan [::read $sock 4] I len] != 1} {
	    error "$sock closed on [self]"
	}
	set robj [::read $sock $len]
	
	set [info object namespace $obj]::__sock $sock
	return $obj
    }

    method new {args} {
	my intervene [next {*}$args] $args
    }

    method create {args} {
	my intervene [next {*}$args] [lrange $args 1 end]
    }

    superclass ::oo::class
    constructor {host port args} {
	set class [self]
	variable c2hp; dict set c2hp $class [list $host $port]

	next {*}$args
	oo::define $class method unknown {args} {
	    variable __sock
	    puts -nonewline $__sock [binary format I [string length $args]]$args
	    if {[binary scan [::read $__sock 4] I len] != 1} {
		error "$__sock closed on [self]"
	    }
	    return {*}[::read $__sock $len]
	}
    }
}

if {[info script] eq $argv0} {
    if {[lindex $argv 0]} {
	oo::rpc::server create Fred 8080 {
	    method Var {var} {
		variable $var
		return [set $var]
	    }
	    method error {} {
		error "This is an intentional ERROR"
	    }
	    
	    constructor {args} {
		variable {*}$args
	    }
	}
    } else {
	oo::rpc::client create Fred@8080 localhost 8080 {
	    method local {} {
		puts stderr "This is a local method"
	    }
	}

	set fred [Fred@8080 create fred a 1 b 2 c 3]
	$fred local
	puts [$fred Var a]
	$fred error
    }

    vwait forever

}