Distributed programming

From Rosetta Code
Jump to: navigation, search
Task
Distributed programming
You are encouraged to solve this task according to the task description, using any language you may know.

Write two programs (or one program with two modes) which run on networked computers, and send some messages between them.

The protocol used may be language-specific or not, and should be suitable for general distributed programming; that is, the protocol should be generic (not designed just for the particular example application), readily capable of handling the independent communications of many different components of a single application, and the transferring of arbitrary data structures natural for the language.

This task is intended to demonstrate high-level communication facilities beyond just creating sockets.

Contents

[edit] Ada

Works with: GNAT GPL version 2010
Works with: PolyORB

Ada defines facilities for distributed systems in its standard (Annex E, also called DSA).

This example works with PolyORB and the GNAT GPL 2010 compiler from AdaCore.

server.ads:

package Server is
pragma Remote_Call_Interface;
procedure Foo;
function Bar return Natural;
end Server;

server.adb:

package body Server is
Count : Natural := 0;
 
procedure Foo is
begin
Count := Count + 1;
end Foo;
 
function Bar return Natural is
begin
return Count;
end Bar;
end Server;

client.adb:

with Server;
with Ada.Text_IO;
 
procedure Client is
begin
Ada.Text_IO.Put_Line ("Calling Foo...");
Server.Foo;
Ada.Text_IO.Put_Line ("Calling Bar: " & Integer'Image (Server.Bar));
end Client;

required config (dsa.cfg):

configuration DSA is
pragma Starter (None);
 
-- Server
Server_Partition : Partition := (Server);
procedure Run_Server is in Server_Partition;
 
-- Client
Client_Partition : Partition;
for Client_Partition'Termination use Local_Termination;
procedure Client;
for Client_Partition'Main use Client;
end DSA;

compilation:

$po_gnatdist dsa.cfg
[...]
 ------------------------------
 ---- Configuration report ----
 ------------------------------
Configuration :
   Name        : dsa
   Main        : run_server
   Starter     : none

Partition server_partition
   Main        : run_server
   Units       :
             - server (rci)
             - run_server (normal)
             - polyorb.dsa_p.partitions (rci, from PCS)

   Environment variables :
             - "POLYORB_DSA_NAME_SERVICE"

Partition client_partition
   Main        : client
   Termination : local
   Units       :
             - client (normal)

   Environment variables :
             - "POLYORB_DSA_NAME_SERVICE"

 -------------------------------
[...]

preparation (run PolyORB name service):

$ po_ioc_naming
POLYORB_CORBA_NAME_SERVICE=IOR:010000002b00000049444[...]
POLYORB_CORBA_NAME_SERVICE=corbaloc:iiop:[email protected][...]

You have to set the environment variable POLYORB_DSA_NAME_SERVICE to one of the two values given by po_ioc_naming for the server/client partitions.

running server:

$ ./server_partition

running client:

$ ./client_partition
Calling Foo...
Calling Bar:  1
$ ./client_partition
Calling Foo...
Calling Bar:  2

[edit] AutoHotkey

See Distributed program/AutoHotkey.

[edit] C

Using PVM [[1] This program is in a sense both a server and a client, depending on if its task is spawned with a command-line argument: if yes, it spawns another task of the same executible on the parallel virtual machine and waits for it to transmit data; if no, it transmits data and is done.

#include <stdio.h>
#include <stdlib.h>
#include <pvm3.h>
 
int main(int c, char **v)
{
int tids[10];
int parent, spawn;
int i_data, i2;
double f_data;
 
if (c > 1) {
spawn = pvm_spawn("/tmp/a.out", 0, PvmTaskDefault, 0, 1, tids);
if (spawn <= 0) {
printf("Can't spawn task\n");
return 1;
}
 
printf("Spawning successful\n");
 
/* pvm_recv(task_id, msgtag). msgtag identifies what kind of data it is,
* for here: 1 = (int, double), 2 = (int, int)
* The receiving order is intentionally swapped, just to show.
* task_id = -1 means "receive from any task"
*/

pvm_recv(-1, 2);
pvm_unpackf("%d %d", &i_data, &i2);
printf("got msg type 2: %d %d\n", i_data, i2);
 
pvm_recv(-1, 1);
pvm_unpackf("%d %lf", &i_data, &f_data);
printf("got msg type 1: %d %f\n", i_data, f_data);
} else {
parent = pvm_parent();
 
pvm_initsend(PvmDataDefault);
i_data = rand();
f_data = (double)rand() / RAND_MAX;
pvm_packf("%d %lf", i_data, f_data);
pvm_send(parent, 1); /* send msg type 1 */
 
pvm_initsend(PvmDataDefault);
i2 = rand();
pvm_packf("%d %d", i_data, i2);
pvm_send(parent, 2); /* send msg type 2 */
}
 
pvm_exit();
return 0;
}
Running it: (on PVM console, exe is /tmp/a.out)
pvm> spawn -> /tmp/a.out 1
spawn -> /tmp/a.out 1
[2]
1 successful
t40028
pvm> [2:t40029] EOF
[2:t40028] Spawning successful
[2:t40028] got msg type 2: 1804289383 1681692777
[2:t40028] got msg type 1: 1804289383 0.394383
[2:t40028] EOF
[2] finished

[edit] C#

This example is incorrect. The protocol used is not sufficiently general-purpose. Please fix the code and remove this message.

The example server can handle one client at any one time. It will read what the client writes, and respond with "Hello World!". The client will write "Hello World!" and read the response from the server.

[edit] Server

 
using System.Net.Sockets;
 
class Program
{
static void Main(string[] args)
{
TcpListener server = new TcpListener(8000);
server.Start();
 
Console.WriteLine("Listening, port 8000");
 
TcpClient client;
do
{
// Accept client
client = server.AcceptTcpClient();
Console.WriteLine("Recieved client: " + client.Client.AddressFamily.ToString());
 
// Recieve
string tRecieve = "";
char t;
do
{
if (client.Available > 0)
{
t = (char)client.GetStream().ReadByte();
 
if (t == 0)
break;
 
tRecieve += t;
}
} while (true);
 
Console.WriteLine("Recieved: " + tRecieve);
 
// Send
byte[] tSend = Encoding.ASCII.GetBytes("Hello World!");
client.GetStream().Write(tSend, 0, tSend.Length);
client.GetStream().WriteByte(0);
 
Console.WriteLine("Sent: " + Encoding.ASCII.GetString(tSend));
 
// Close
client.Close();
} while (true);
 
}
}
 

[edit] Client

 
using System.Net.Sockets;
 
class Program
{
static void Main(string[] args)
{
TcpClient client;
 
// Connect
do
{
client = new TcpClient();
client.Connect(new System.Net.IPEndPoint(System.Net.IPAddress.Parse("127.0.0.1"), 8000));
 
Console.WriteLine("Connected");
 
// Send
byte[] tSend = Encoding.ASCII.GetBytes("Hello World!");
client.GetStream().Write(tSend, 0, tSend.Length);
client.GetStream().WriteByte(0);
 
Console.WriteLine("Sent: " + Encoding.ASCII.GetString(tSend));
 
// Read
string tRecieve = "";
char t;
do
{
if (client.Available > 0)
{
t = (char)client.GetStream().ReadByte();
 
if (t == 0)
break;
 
tRecieve += t;
}
} while (true);
 
Console.WriteLine("Recieved: " + tRecieve);
 
client.Close();
 
Console.Read();
} while (true);
}
}
 

[edit] D

Uses the rpc library: https://github.com/adamdruppe/misc-stuff-including-D-programming-language-web-stuff/blob/master/rpc.d

This library is not standard, so this code (by Adam D. Ruppe) could and should be rewritten using more standard means.

import arsd.rpc;
 
struct S1 {
int number;
string name;
}
 
struct S2 {
string name;
int number;
}
 
interface ExampleNetworkFunctions {
string sayHello(string name);
int add(in int a, in int b) const pure nothrow;
S2 structTest(S1);
void die();
}
 
// The server must implement the interface.
class ExampleServer : ExampleNetworkFunctions {
override string sayHello(string name) {
return "Hello, " ~ name;
}
 
override int add(in int a, in int b) const pure nothrow {
return a + b;
}
 
override S2 structTest(S1 a) {
return S2(a.name, a.number);
}
 
override void die() {
throw new Exception("death requested");
}
 
mixin NetworkServer!ExampleNetworkFunctions;
}
 
class Client {
mixin NetworkClient!ExampleNetworkFunctions;
}
 
void main(in string[] args) {
import std.stdio;
 
if (args.length > 1) {
auto client = new Client("localhost", 5005);
// These work like the interface above, but instead of
// returning the value, they take callbacks for success (where
// the arg is the retval) and failure (the arg is the
// exception).
client.sayHello("whoa", (a) { writeln(a); }, null);
client.add(1,2, (a){ writeln(a); }, null);
client.add(10,20, (a){ writeln(a); }, null);
client.structTest(S1(20, "cool!"),
(a){ writeln(a.name, " -- ", a.number); },
null);
client.die(delegate(){ writeln("shouldn't happen"); },
delegate(a){ writeln(a); });
client.eventLoop;
} else {
auto server = new ExampleServer(5005);
server.eventLoop;
}
}

[edit] E

Protocol: Pluribus

This service cannot be used except by clients which know the URL designating it, messages are encrypted, and the client authenticates the server. However, it is vulnerable to denial-of-service by any client knowing the URL.

[edit] Server

(The protocol is symmetric; this program is the server only in that it is the one which is started first and exports an object.)

def storage := [].diverge()
 
def logService {
to log(line :String) {
storage.push([timer.now(), line])
}
to search(substring :String) {
var matches := []
for [time, line] ? (line.startOf(substring) != -1) in storage {
matches with= [time, line]
}
return matches
}
}
 
introducer.onTheAir()
def sturdyRef := makeSturdyRef.temp(logService)
println(<captp>.sturdyToURI(sturdyRef))
interp.blockAtTop()

This will print the URL of the service and run it until aborted.

[edit] Client

The URL provided by the server is given as the argument to this program.

def [uri] := interp.getArgs()
introducer.onTheAir()
def sturdyRef := <captp>.sturdyFromURI(uri)
def logService := sturdyRef.getRcvr()
 
logService <- log("foot")
logService <- log("shoe")
 
println("Searching...")
when (def result := logService <- search("foo")) -> {
for [time, line] in result {
println(`At $time: $line`)
}
}

[edit] Erlang

The protocol is erlang's own

[edit] Server

srv.erl

-module(srv).
-export([start/0, wait/0]).
 
start() ->
net_kernel:start([srv,shortnames]),
erlang:set_cookie(node(), rosetta),
Pid = spawn(srv,wait,[]),
register(srv,Pid),
io:fwrite("~p ready~n",[node(Pid)]),
ok.
 
wait() ->
receive
{echo, Pid, Any} ->
io:fwrite("-> ~p from ~p~n", [Any, node(Pid)]),
Pid ! {hello, Any},
wait();
Any -> io:fwrite("Error ~p~n", [Any])
end.

[edit] Client

client.erl

-module(client).
-export([start/0, wait/0]).
 
start() ->
net_kernel:start([client,shortnames]),
erlang:set_cookie(node(), rosetta),
{ok,[[Srv]]} = init:get_argument(server),
io:fwrite("connecting to ~p~n", [Srv]),
{srv, list_to_atom(Srv)} ! {echo,self(), hi},
wait(),
ok.
 
wait() ->
receive
{hello, Any} -> io:fwrite("Received ~p~n", [Any]);
Any -> io:fwrite("Error ~p~n", [Any])
end.

running it (*comes later)

|erlc srv.erl
|erl -run srv start -noshell
 srv@agneyam ready
*-> hi from client@agneyam
|erlc client.erl
|erl -run client start -run init stop -noshell -server srv@agneyam
 connecting to "srv@agneyam"
 Received hi

[edit] Factor

The protocol is the one provided by Factor (concurrency.distributed, concurrency.messaging)

Example summary:

- A server node is listening for messages made of natural data types and structures, and simply prettyprint them.

- A client node is sending such data structure: an array of one string and one hashtable (with one key/value pair).

[edit] Server

USING: concurrency.distributed concurrency.messaging threads io.sockets io.servers ;
QUALIFIED: concurrency.messaging
: prettyprint-message ( -- ) concurrency.messaging:receive . flush prettyprint-message ;
[ prettyprint-message ] "logger" spawn dup name>> register-remote-thread
"127.0.0.1" 9000 <inet4> <node-server> start-server

Note: we are using QUALIFIED: with the concurrency.messaging vocabulary because the "receive" word is defined in io.sockets vocabulary too. If someone have a cleaner way to handle this.

[edit] Client

USING: concurrency.distributed io.sockets ;
QUALIFIED: concurrency.messaging
{ "Hello Remote Factor!" H{ { "key1" "value1" } } }
"127.0.0.1" 9000 <inet4> "logger" <remote-thread> concurrency.messaging:send

How to Run:

- Copy/Paste the server code in an instance of Factor Listener

- Copy/Paste the client code in another instance of Factor Listener.

The server node should prettyprint the data structure send by the client: { "Hello Remote Factor!" H{ { "key1" "value1" } } }

[edit] Go

Package net/rpc in the Go standard library serializes data with the Go-native "gob" type. The example here sends only a single floating point number, but the package will send any user-defined data type, including of course structs with multiple fields.

[edit] Server

package main
 
import (
"errors"
"log"
"net"
"net/http"
"net/rpc"
)
 
type TaxComputer float64
 
func (taxRate TaxComputer) Tax(x float64, r *float64) error {
if x < 0 {
return errors.New("Negative values not allowed")
}
*r = x * float64(taxRate)
return nil
}
 
func main() {
c := TaxComputer(.05)
rpc.Register(c)
rpc.HandleHTTP()
listener, err := net.Listen("tcp", ":1234")
if err != nil {
log.Fatal(err)
}
http.Serve(listener, nil)
}

[edit] Client

package main
 
import (
"fmt"
"log"
"net/rpc"
)
 
func main() {
client, err := rpc.DialHTTP("tcp", "localhost:1234")
if err != nil {
fmt.Println(err)
return
}
 
amount := 3.
var tax float64
err = client.Call("TaxComputer.Tax", amount, &tax)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Tax on %.2f: %.2f\n", amount, tax)
}

Client output:

Tax on 3.00: 0.15

[edit] Haskell

See:

Check license: http://www.haskell.org/haskellwiki/HaskellWiki:Copyrights

[edit] JavaScript

Works with: node.js

[edit] Server

var net = require('net')
 
var server = net.createServer(function (c){
c.write('hello\r\n')
c.pipe(c) // echo messages back
})
 
server.listen(3000, 'localhost')
 

[edit] Client

var net = require('net')
 
conn = net.createConnection(3000, '192.168.1.x')
 
conn.on('connect', function(){
console.log('connected')
conn.write('test')
})
 
conn.on('data', function(msg){
console.log(msg.toString())
})

[edit] Mathematica

The following sends a request for a random number to be generated on each of two nodes, these are then transmitted back to be assembled into an array with two elements. Omitting the first line, will cause the program to be run on all configured remote computers.

LaunchKernels[2];
ParallelEvaluate[RandomReal[]]
 

[edit] Objective-C

Distributed Objects are natural to Objective-C, and OpenStep and derivated framework offers an easy way of using remote objects as if it were local. The client must only know the protocol the remote object support. For the rest, calling a remote object's method or local object's method is transparent.

Works with: GNUstep

[edit] Server

The server vending the object with the name DistributedAction

ActionObjectProtocol.h

#import <Foundation/Foundation.h>
// our protocol allows "sending" "strings", but we can implement
// everything we could for a "local" object
@protocol ActionObjectProtocol
- (NSString *)sendMessage: (NSString *)msg;
@end

ActionObject.h

#import <Foundation/Foundation.h>
#import "ActionObjectProtocol.h"
 
@interface ActionObject : NSObject <ActionObjectProtocol>
// we do not have much for this example!
@end

ActionObject.m

#import <Foundation/Foundation.h>
#import "ActionObject.h"
 
@implementation ActionObject
-(NSString *)sendMessage: (NSString *)msg
{
NSLog(@"client sending message %@", msg);
return @"server answers ...";
}
@end

server.m

#import <Foundation/Foundation.h>
#import "ActionObject.h"
 
int main (void)
{
@autoreleasepool {
 
ActionObject *action = [[ActionObject alloc] init];
 
NSSocketPort *port = (NSSocketPort *)[NSSocketPort port];
// initWithTCPPort: 1234 and other methods are not supported yet
// by GNUstep
NSConnection *connect = [NSConnection
connectionWithReceivePort: port
sendPort: port]; // or sendPort: nil
 
[connect setRootObject: action];
 
/* "vend" the object ActionObject as DistributedAction; on GNUstep
the Name Server that allows the resolution of the registered name
is bound to port 538 */

if (![connect registerName:@"DistributedAction"
withNameServer: [NSSocketPortNameServer sharedInstance] ])
{
NSLog(@"can't register the server DistributedAction");
exit(EXIT_FAILURE);
}
 
NSLog(@"waiting for messages...");
 
[[NSRunLoop currentRunLoop] run];
 
}
return 0;
}

[edit] Client

client.m

#import <Foundation/Foundation.h>
#import "ActionObjectProtocol.h"
 
int main(void)
{
@autoreleasepool {
 
id <ActionObjectProtocol> action = (id <ActionObjectProtocol>)
[NSConnection
rootProxyForConnectionWithRegisteredName: @"DistributedAction"
host: @"localhost"
usingNameServer: [NSSocketPortNameServer sharedInstance] ];
 
if (action == nil)
{
NSLog(@"can't connect to the server");
exit(EXIT_FAILURE);
}
 
NSArray *args = [[NSProcessInfo processInfo] arguments];
 
if ([args count] == 1)
{
NSLog(@"specify a message");
exit(EXIT_FAILURE);
}
 
NSString *msg = args[1];
 
// "send" (call the selector "sendMessage:" of the (remote) object
// action) the first argument's text as msg, store the message "sent
// back" and then show it in the log
NSString *backmsg = [action sendMessage: msg];
NSLog("%@", backmsg);
 
}
return 0;
}

[edit] OCaml

Works with: JoCaml

Minimalistic distributed logger with synchronous channels using the join calculus on top of OCaml.

[edit] Server

open Printf
 
let create_logger () =
def log(text) & logs(l) =
printf "Logged: %s\n%!" text;
logs((text, Unix.gettimeofday ())::l) & reply to log
 
or search(text) & logs(l) =
logs(l) & reply List.filter (fun (line, _) -> line = text) l to search
in
spawn logs([]);
(log, search)
 
def wait() & finished() = reply to wait
 
let register name service = Join.Ns.register Join.Ns.here name service
 
let () =
let log, search = create_logger () in
register "log" log;
register "search" search;
Join.Site.listen (Unix.ADDR_INET (Join.Site.get_local_addr(), 12345));
wait ()

[edit] Client

open Printf
 
let ns_there = Join.Ns.there (Unix.ADDR_INET (Join.Site.get_local_addr(), 12345))
 
let lookup name = Join.Ns.lookup ns_there name
 
let log : string -> unit = lookup "log"
let search : string -> (string * float) list = lookup "search"
 
let find txt =
printf "Looking for %s...\n" txt;
List.iter (fun (line, time) ->
printf "Found: '%s' at t = %f\n%!" (String.escaped line) time)
(search txt)
 
let () =
log "bar";
find "foo";
log "foo";
log "shoe";
find "foo"

[edit] Oz

We show a program that starts a server on a remote machine, exchanges two messages with that server and finally shuts it down.

declare
functor ServerCode
export
port:Prt
define
Stream
Prt = {NewPort ?Stream}
thread
for Request#Reply in Stream do
case Request
of echo(Data) then Reply = Data
[] compute(Function) then Reply = {Function}
end
end
end
end
 
%% create the server on some machine
%% (just change "localhost" to some machine
%% that you can use with a passwordless rsh login
%% and that has the same Mozart version installed)
RM = {New Remote.manager init(host:localhost)}
 
%% execute the code encapsulated in the ServerCode functor
Server = {RM apply(ServerCode $)}
 
%% Shortcut: send a message to Server and receive a reply
fun {Send X}
{Port.sendRecv Server.port X}
end
in
%% echo
{System.showInfo "Echo reply: "#{Send echo(hello)}}
 
%% compute
{System.showInfo "Result of computation: "#
{Send compute(fun {$} 8 div 4 end)}}
 
%% shut down server
{RM close}

[edit] Perl

Using Data::Dumper and Safe to transmit arbitrary data structures as serialized text between hosts. Same code works as both sender and receiver.

use Data::Dumper;
use IO::Socket::INET;
use Safe;
 
sub get_data {
my $sock = new IO::Socket::INET
LocalHost => "localhost",
LocalPort => "10000",
Proto => "tcp",
Listen => 1,
Reuse => 1;
unless ($sock) { die "Socket creation failure" }
my $cli = $sock->accept();
 
# of course someone may be tempted to send you 'system("rm -rf /")',
# to be safe(r), use Safe::
my $safe = new Safe;
my $x = $safe->reval(join("", <$cli>));
close $cli;
close $sock;
return $x;
}
 
sub send_data {
my $host = shift;
my $data = shift;
my $sock = new IO::Socket::INET
PeerAddr => "$host:10000",
Proto => "tcp",
Reuse => 1;
 
unless ($sock) { die "Socket creation failure" }
 
print $sock Data::Dumper->Dump([$data]);
close $sock;
}
 
if (@ARGV) {
my $x = get_data();
print "Got data\n", Data::Dumper->Dump([$x]);
} else {
send_data('some_host', { a=>100, b=>[1 .. 10] });
}

[edit] PicoLisp

[edit] Server

(task (port 12321)                     # Background server task
(let? Sock (accept @)
(unless (fork) # Handle request in child process
(in Sock
(while (rd) # Handle requests
(out Sock
(pr (eval @)) ) ) ) # Evaluate and send reply
(bye) ) # Exit child process
(close Sock) ) ) # Close socket in parent process

[edit] Client

(let? Sock (connect "localhost" 12321)
(out Sock (pr '*Pid)) # Query PID from server
(println 'PID (in Sock (rd))) # Receive and print reply
(out Sock (pr '(* 3 4))) # Request some calculation
(println 'Result (in Sock (rd))) # Print result
(close Sock) ) # Close connection to server

Output:

PID 18372
Result 12

[edit] Python

Works with: Python version 2.4 and 2.6

[edit] XML-RPC

Protocol: XML-RPC

[edit] Server

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
import SimpleXMLRPCServer
 
class MyHandlerInstance:
def echo(self, data):
'''Method for returning data got from client'''
return 'Server responded: %s' % data
 
def div(self, num1, num2):
'''Method for divide 2 numbers'''
return num1/num2
 
def foo_function():
'''A function (not an instance method)'''
return True
 
HOST = "localhost"
PORT = 8000
 
server = SimpleXMLRPCServer.SimpleXMLRPCServer((HOST, PORT))
 
# register built-in system.* functions.
server.register_introspection_functions()
 
# register our instance
server.register_instance(MyHandlerInstance())
 
# register our function as well
server.register_function(foo_function)
 
try:
# serve forever
server.serve_forever()
except KeyboardInterrupt:
print 'Exiting...'
server.server_close()

[edit] Client

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
import xmlrpclib
 
HOST = "localhost"
PORT = 8000
 
rpc = xmlrpclib.ServerProxy("http://%s:%d" % (HOST, PORT))
 
# print what functions does server support
print 'Server supports these functions:',
print ' '.join(rpc.system.listMethods())
 
# echo something
rpc.echo("We sent this data to server")
 
# div numbers
print 'Server says: 8 / 4 is: %d' % rpc.div(8, 4)
 
# control if foo_function returns True
if rpc.foo_function():
print 'Server says: foo_function returned True'

[edit] HTTP

Protocol: HTTP

[edit] Server

#!/usr/bin/python
# -*- coding: utf-8 -*-
 
import BaseHTTPServer
 
HOST = "localhost"
PORT = 8000
 
# we just want to write own class, we replace do_GET method. This could be extended, I just added basics
# see; http://docs.python.org/lib/module-BaseHTTPServer.html
class MyHTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
# send 200 (OK) message
self.send_response(200)
# send header
self.send_header("Content-type", "text/html")
self.end_headers()
 
# send context
self.wfile.write("<html><head><title>Our Web Title</title></head>")
self.wfile.write("<body><p>This is our body. You wanted to visit <b>%s</b> page</p></body>" % self.path)
self.wfile.write("</html>")
 
if __name__ == '__main__':
server = BaseHTTPServer.HTTPServer((HOST, PORT), MyHTTPHandler)
try:
server.serve_forever()
except KeyboardInterrupt:
print 'Exiting...'
server.server_close()

[edit] Client

#!/usr/bin/python
# -*- coding: utf-8 -*-
 
import httplib
 
HOST = "localhost"
PORT = 8000
 
conn = httplib.HTTPConnection(HOST, PORT)
conn.request("GET", "/somefile")
 
response = conn.getresponse()
print 'Server Status: %d' % response.status
 
print 'Server Message: %s' % response.read()

[edit] Socket, Pickle format

Protocol: raw socket / pickle format

This example builds a very basic RPC mechanism on top of sockets and the pickle module. Please note that the pickle module is not secure - a malicious client can build malformed data to execute arbitrary code on the server. If untrusted clients can access the server, the json module could be used as a substitute, but we lose the ability to transfer arbitrary Python objects that way.

[edit] Server

#!/usr/bin/python
# -*- coding: utf-8 -*-
 
import SocketServer
import pickle
 
HOST = "localhost"
PORT = 8000
 
class RPCServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
# The object_to_proxy member should be set to the object we want
# methods called on. Unfortunately, we can't do this in the constructor
# because the constructor should not be overridden in TCPServer...
 
daemon_threads = True
 
class RPCHandler(SocketServer.StreamRequestHandler):
def handle(self):
in_channel = pickle.Unpickler(self.rfile)
out_channel = pickle.Pickler(self.wfile, protocol=2)
while True:
try:
name, args, kwargs = in_channel.load()
print 'got %s %s %s' % (name, args, kwargs)
except EOFError:
# EOF means we're done with this request.
# Catching this exception to detect EOF is a bit hackish,
# but will work for a quick demo like this
break
try:
method = getattr(self.server.object_to_proxy, name)
result = method(*args, **kwargs)
except Exception, e:
out_channel.dump(('Error',e))
else:
out_channel.dump(('OK',result))
 
class MyHandlerInstance(object):
def echo(self, data):
'''Method for returning data got from client'''
return 'Server responded: %s' % data
 
def div(self, dividend, divisor):
'''Method to divide 2 numbers'''
return dividend/divisor
 
def is_computer_on(self):
return True
 
if __name__ == '__main__':
rpcserver = RPCServer((HOST, PORT), RPCHandler)
rpcserver.object_to_proxy = MyHandlerInstance()
try:
rpcserver.serve_forever()
except KeyboardInterrupt:
print 'Exiting...'
rpcserver.server_close()
 

[edit] Client

#!/usr/bin/python
# -*- coding: utf-8 -*-
 
import socket
import pickle
 
HOST = "localhost"
PORT = 8000
 
class RPCClient(object):
def __init__(self, host, port):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((host, port))
self.rfile = self.socket.makefile('rb')
self.wfile = self.socket.makefile('wb')
self.in_channel = pickle.Unpickler(self.rfile)
self.out_channel = pickle.Pickler(self.wfile, protocol=2)
 
def _close(self):
self.socket.close()
self.rfile.close()
self.wfile.close()
 
# Make calling remote methods easy by overriding attribute access.
# Accessing any attribute on our instances will give a proxy method that
# calls the method with the same name on the remote machine.
def __getattr__(self, name):
def proxy(*args, **kwargs):
self.out_channel.dump((name, args, kwargs))
self.wfile.flush() # to make sure the server won't wait forever
status, result = self.in_channel.load()
if status == 'OK':
return result
else:
raise result
 
return proxy
 
if __name__ == '__main__':
# connect to server and send data
rpcclient = RPCClient(HOST, PORT)
 
print 'Testing the echo() method:'
print rpcclient.echo('Hello world!')
print
print 'Calculating 42/2 on the remote machine:'
print rpcclient.div(42, 2)
print
print 'is_computer_on on the remote machine returns:'
print rpcclient.is_computer_on()
print
print 'Testing keyword args:'
print '42/2 is:', rpcclient.div(divisor=2, dividend=42)
rpcclient._close()
del rpcclient

[edit] Pyro

Note: You should install Pyro (http://pyro.sourceforge.net) first and run pyro-ns binary to run code below.

[edit] Server

#!/usr/bin/python
# -*- coding: utf-8 -*-
 
import Pyro.core
import Pyro.naming
 
# create instance that will return upper case
class StringInstance(Pyro.core.ObjBase):
def makeUpper(self, data):
return data.upper()
 
class MathInstance(Pyro.core.ObjBase):
def div(self, num1, num2):
return num1/num2
 
if __name__ == '__main__':
server = Pyro.core.Daemon()
name_server = Pyro.naming.NameServerLocator().getNS()
server.useNameServer(name_server)
server.connect(StringInstance(), 'string')
server.connect(MathInstance(), 'math')
try:
server.requestLoop()
except KeyboardInterrupt:
print 'Exiting...'
server.shutdown()

[edit] Client

#!/usr/bin/python
# -*- coding: utf-8 -*-
 
import Pyro.core
 
DATA = "my name is eren"
NUM1 = 10
NUM2 = 5
 
string = Pyro.core.getProxyForURI("PYRONAME://string")
math = Pyro.core.getProxyForURI("PYRONAME://math")
 
print 'We sent: %s' % DATA
print 'Server responded: %s\n' % string.makeUpper(DATA)
 
print 'We sent two numbers to divide: %d and %d' % (NUM1, NUM2)
print 'Server responded the result: %s' % math.div(NUM1, NUM2)

[edit] Spread

Note: You should install Spread (http://www.spread.org) and its python bindings (http://www.python.org/other/spread/)

[edit] Server

You don't need any code for server. You should start "spread" daemon by typing "spread -c /etc/spread.conf -n localhost". If you want more configuration, look at /etc/spread.conf.

After starting daemon, if you want to make sure that it is running, enter spuser -s 4803 command where 4803 is your port set in spread.conf, you will see prompt, type j user, you should see something like this message: Received REGULAR membership for group test with 3 members, where I am member 2

[edit] Client (Listener)

#!/usr/bin/python
# -*- coding: utf-8 -*-
 
import spread
 
PORT = '4803'
 
# connect spread daemon
conn = spread.connect(PORT)
# join the room
conn.join('test')
 
print 'Waiting for messages... If you want to stop this script, please stop spread daemon'
while True:
recv = conn.receive()
if hasattr(recv, 'sender') and hasattr(recv, 'message'):
print 'Sender: %s' % recv.sender
print 'Message: %s' % recv.message

[edit] Client (Sender)

#!/usr/bin/python
# -*- coding: utf-8 -*-
 
import spread
 
PORT = '4803'
 
conn = spread.connect(PORT)
conn.join('test')
 
conn.multicast(spread.RELIABLE_MESS, 'test', 'hello, this is message sent from python')
conn.disconnect()

[edit] Racket

Server and client in the same piece of code, running a useless (fib 42) computation, four times, on four hosts (which all happen to be "localhost", but that can change, of course).

 
#lang racket/base
(require racket/place/distributed racket/place)
 
(define (fib n)
(if (<= n 1) n (+ (fib (- n 1)) (fib (- n 2)))))
 
(provide work)
(define (work)
(place ch
(place-channel-put ch (fib (place-channel-get ch)))))
 
(module+ main
(define places
(for/list ([host '("localhost" "localhost" "localhost" "localhost")]
[port (in-naturals 12345)])
(define-values [node place]
(spawn-node-supervise-place-at host #:listen-port port #:thunk #t
(quote-module-path "..") 'work))
place))
(message-router
(after-seconds 1
(for ([p places]) (*channel-put p 42))
(printf "Results: ~s\n" (map *channel-get places))
(exit))))
 

[edit] Ruby

Uses the distributed Ruby (dRuby) from the standard library. The "druby:" protocol uses TCP/IP sockets for communication.

Server

require 'drb/drb'
 
# The URI for the server to connect to
URI="druby://localhost:8787"
 
class TimeServer
 
def get_current_time
return Time.now
end
 
end
 
# The object that handles requests on the server
FRONT_OBJECT = TimeServer.new
 
$SAFE = 1 # disable eval() and friends
 
DRb.start_service(URI, FRONT_OBJECT)
# Wait for the drb server thread to finish before exiting.
DRb.thread.join

Client

require 'drb/drb'
 
# The URI to connect to
SERVER_URI = "druby://localhost:8787"
 
# Start a local DRbServer to handle callbacks.
#
# Not necessary for this small example, but will be required
# as soon as we pass a non-marshallable object as an argument
# to a dRuby call.
DRb.start_service
 
timeserver = DRbObject.new_with_uri(SERVER_URI)
puts timeserver.get_current_time

[edit] Tcl

A rudimentary IRC Server

proc main {} {
global connections
set connections [dict create]
socket -server handleConnection 12345
vwait dummyVar ;# enter the event loop
}
 
proc handleConnection {channel clientaddr clientport} {
global connections
dict set connections $channel address "$clientaddr:$clientport"
fconfigure $channel -buffering line
fileevent $channel readable [list handleMessage $channel]
}
 
proc handleMessage {channel} {
global connections
if {[gets $channel line] == -1} {
disconnect $channel
} else {
if {[string index [string trimleft $line] 0] eq "/"} {
set words [lassign [split [string trim $line]] command]
handleCommand $command $words $channel
} else {
echo $line $channel
}
}
}
 
proc disconnect {channel} {
global connections
dict unset connections $channel
fileevent $channel readable ""
close $channel
}
 
proc handleCommand {command words channel} {
global connections
switch -exact -- [string tolower $command] {
/nick {
dict set connections $channel nick [lindex $words 0]
}
/quit {
echo bye $channel
disconnect $channel
}
default {
puts $channel "\"$command\" not implemented"
}
}
}
 
proc echo {message senderchannel} {
global connections
foreach channel [dict keys $connections] {
if {$channel ne $senderchannel} {
set time [clock format [clock seconds] -format "%T"]
set nick [dict get $connections $channel nick]
puts $channel [format "\[%s\] %s: %s" $time $nick $message]
}
}
}
 
main

Client

proc main {} {
global argv argc
if {$argc != 2} {
error "usage: [info script] serveraddress serverport"
}
connect {*}$argv
vwait dummyVar
}
 
proc connect {addr port} {
global sock
set sock [socket $addr $port]
fconfigure $sock -buffering line
fileevent $sock readable getFromServer
fileevent stdin readable sendToServer
}
 
proc getFromServer {} {
global sock
if {[gets $sock line] == -1} {
puts "disconnected..."
exit
} else {
puts $line
}
}
 
proc sendToServer {} {
global sock
set msg [string trim [gets stdin]]
if {[string length $msg] > 0} {
puts $sock $msg
}
}
 
main

[edit] UnixPipes

Library: nc

Uses netcat and a buffer to cycle the server shell's stdout back to netcat's stdin.

[edit] Server

Security risk! Anything, able to reach 127.0.0.1 port 1234, can run shell commands as the user who runs the server. This allows other users to gain privileges.
: >/tmp/buffer
tail -f /tmp/buffer | nc -l 127.0.0.1 1234 | sh >/tmp/buffer 2>&1

Limitations:

  • The server can accept only one connection (but continues to run, not exit, after this connection dies).
  • With some systems, tail -f might be slow to notice changes to /tmp/buffer.

[edit] Client

nc 127.0.0.1 1234

Now you can enter commands in the client terminal and get the output back through the same connection.

Personal tools
Namespaces

Variants
Actions
Community
Explore
Misc
Toolbox