Distributed programming: Difference between revisions
(→Client: typo) |
(→{{header|Go}}: netchan going unsupported for now. here's an rpc solution.) |
||
Line 466: | Line 466: | ||
=={{header|Go}}== |
=={{header|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. |
|||
Shown here is netchan, a standard Go library that enables Go channel operations across network connections. The significance is that these are type-safe data transfers of native Go types. Channels can be of any Go type although only an int channel is shown here. Netchans allow for arbitrary connections between computers, client and server roles are not mandatory. The netchan interface is independent of the type of network connection, TCP is used here. |
|||
=== |
===Server=== |
||
<lang go>package main |
<lang go>package main |
||
import ( |
import ( |
||
" |
"errors" |
||
⚫ | |||
"net" |
"net" |
||
" |
"net/http" |
||
"net/rpc" |
|||
) |
) |
||
type TaxComputer float64 |
|||
⚫ | |||
// channels to be exported, created as usual |
|||
squareCh := make(chan int) |
|||
resultCh := make(chan int, 1) |
|||
func (taxRate TaxComputer) Tax(x float64, r *float64) error { |
|||
// create exporter for the two channels |
|||
⚫ | |||
exp := netchan.NewExporter() |
|||
return errors.New("Negative values not allowed") |
|||
err := exp.Export("square", squareCh, netchan.Recv) |
|||
⚫ | |||
fmt.Println(err) |
|||
return |
|||
} |
|||
err = exp.Export("result", resultCh, netchan.Send) |
|||
⚫ | |||
fmt.Println(err) |
|||
return |
|||
} |
} |
||
*r = x * float64(taxRate) |
|||
⚫ | |||
} |
|||
⚫ | |||
// create a net connection on which to publish |
|||
c := TaxComputer(.05) |
|||
rpc.Register(c) |
|||
rpc.HandleHTTP() |
|||
⚫ | |||
if err != nil { |
if err != nil { |
||
log.Fatal(err) |
|||
return |
|||
} |
|||
ta, _ := net.ResolveTCPAddr("tcp", listener.Addr().String()) |
|||
fmt.Println("square, result on port:", ta.Port) |
|||
// publish channels |
|||
⚫ | |||
fmt.Println("Waiting for importer...") |
|||
// use channels as usual. here, just process a single transaction. |
|||
n := <-squareCh |
|||
resultCh <- n * n |
|||
// wait for communication to complete before allowing program to terminate |
|||
err = exp.Drain(1e8) |
|||
if err != nil { |
|||
fmt.Println(err) |
|||
} |
} |
||
⚫ | |||
}</lang> |
}</lang> |
||
⚫ | |||
⚫ | |||
<lang go>package main |
<lang go>package main |
||
import ( |
import ( |
||
"fmt" |
"fmt" |
||
" |
"log" |
||
" |
"net/rpc" |
||
⚫ | |||
) |
) |
||
func main() { |
func main() { |
||
client, err := rpc.DialHTTP("tcp", "localhost:1234") |
|||
if len(os.Args) != 2 { |
|||
fmt.Println("usage: imp <port>") |
|||
return |
|||
} |
|||
// make network connection to exporter |
|||
⚫ | |||
if err != nil { |
if err != nil { |
||
fmt.Println(err) |
fmt.Println(err) |
||
Line 541: | Line 514: | ||
} |
} |
||
amount := 3. |
|||
// create channel importer |
|||
var tax float64 |
|||
imp := netchan.NewImporter(conn) |
|||
err = client.Call("TaxComputer.Tax", amount, &tax) |
|||
// create channels of identical type as created in exporter process. |
|||
squareCh := make(chan int) |
|||
resultCh := make(chan int, 1) |
|||
// import connects channels in this process to matching exported |
|||
// channels in exporter process. |
|||
err = imp.Import("square", squareCh, netchan.Send, 1) |
|||
if err != nil { |
if err != nil { |
||
log.Fatal(err) |
|||
return |
|||
} |
} |
||
fmt.Printf("Tax on %.2f: %.2f\n", amount, tax) |
|||
err = imp.Import("result", resultCh, netchan.Recv, 1) |
|||
if err != nil { |
|||
fmt.Println(err) |
|||
return |
|||
} |
|||
// now use channels as usual |
|||
squareCh <- 12 |
|||
fmt.Println("12 squared is", <-resultCh) |
|||
}</lang> |
}</lang> |
||
Client output: |
|||
Exporter is started first. Output: |
|||
<pre> |
|||
square, result on port: 51951 |
|||
Waiting for importer... |
|||
</pre> |
|||
Importer session: |
|||
<pre> |
<pre> |
||
Tax on 3.00: 0.15 |
|||
> imp 51951 |
|||
12 squared is 144 |
|||
</pre> |
</pre> |
||
Revision as of 23:01, 18 November 2011
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.
Ada
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: <lang Ada>package Server is
pragma Remote_Call_Interface; procedure Foo; function Bar return Natural;
end Server;</lang>
server.adb: <lang Ada>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;</lang>
client.adb: <lang Ada>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;</lang>
required config (dsa.cfg): <lang Ada>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;</lang>
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:1.2@10.200.[...]
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
AutoHotkey
See Distributed program/AutoHotkey.
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. <lang C>#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; }</lang>Running it: (on PVM console, exe is /tmp/a.out)<lang>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</lang>
C#
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.
Server
<lang csharp> 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);
} } </lang>
Client
<lang csharp> 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); } } </lang>
D
Server
<lang D>module distributedserver ; import tango.net.ServerSocket, tango.text.convert.Integer,
tango.text.Util, tango.io.Stdout ;
void main() {
auto Ip = new InternetAddress("localhost", 12345) ; auto server = new ServerSocket(Ip) ; auto socket = server.accept ; auto buffer = new char[socket.bufferSize] ;
bool quit = false ; while(!quit) { bool error = false ; try { auto len = socket.input.read(buffer) ; auto cmd = (len > 0) ? delimit(buffer[0..len], " ") : [""] ; Stdout(cmd).newline.flush ; switch (cmd[0]) { case "square": socket.output.write(toString(toInt(cmd[1]) * toInt(cmd[1]))) ; break ; case"add": socket.output.write(toString(toInt(cmd[1]) + toInt(cmd[2]))) ; break ; case "quit": socket.output.write("Server Shut down") ; quit = true ; break ; default: error = true ; } } catch (Exception e) error = true ; if(error) socket.output.write("<Error>") ; if(socket) socket.close ; if(!quit) socket = server.accept ; }
if(socket) socket.close ;
}</lang>
Client
<lang D>module distributedclient ; import tango.net.SocketConduit, tango.net.InternetAddress,
tango.text.Util, tango.io.Stdout ;
void main(char[][] args) {
if(args.length> 1) { try { auto Ip = new InternetAddress("localhost", 12345) ; auto socket = new SocketConduit ; socket.connect(Ip) ; auto buffer = new char[socket.bufferSize] ; socket.output.write(join(args[1..$]," ")) ; auto len = socket.input.read(buffer) ; if(len > 0) Stdout(buffer[0..len]).newline ; if(socket) socket.close ; } catch(Exception e) Stdout(e.msg).newline ; } else Stdout("usage: supply argument as,\n\tquit\n" "\tsquare <number>\n\tadd <number> <number>").newline ;
}</lang>
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.
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.)
<lang E>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()</lang>
This will print the URL of the service and run it until aborted.
Client
The URL provided by the server is given as the argument to this program.
<lang E>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`) }
}</lang>
Erlang
The protocol is erlang's own
Server
srv.erl
<lang erlang>-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.</lang>
Client
client.erl
<lang erlang>-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.</lang>
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
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.
Server
<lang go>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)
}</lang>
Client
<lang go>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)
}</lang> Client output:
Tax on 3.00: 0.15
JavaScript
Server
<lang javascript>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') </lang>
Client
<lang javascript>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()) })</lang>
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. <lang Mathematica>LaunchKernels[2]; ParallelEvaluate[RandomReal[]] </lang>
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.
Server
The server vending the object with the name DistributedAction
ActionObjectProtocol.h <lang objc>#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</lang>
ActionObject.h <lang objc>#import <Foundation/Foundation.h>
- import "ActionObjectProtocol.h"
@interface ActionObject : NSObject <ActionObjectProtocol>
// we do not have much for this example!
@end</lang>
ActionObject.m <lang objc>#import <Foundation/Foundation.h>
- import "ActionObject.h"
@implementation ActionObject -(NSString *)sendMessage: (NSString *)msg {
NSLog(@"client sending message %@", msg); return @"server answers ...";
} @end</lang>
server.m <lang objc>#import <Foundation/Foundation.h>
- import "ActionObject.h"
int main (void) {
NSAutoreleasePool *pool; ActionObject *action; NSConnection *connect; NSSocketPort *port; pool = [[NSAutoreleasePool alloc] init]; action = [[ActionObject alloc] init];
port = (NSSocketPort *)[NSSocketPort port]; // initWithTCPPort: 1234 and other methods are not supported yet // by GNUstep 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] ] == NO)
{ NSLog(@"can't register the server DistributedAction"); exit(EXIT_FAILURE); } NSLog(@"waiting for messages...");
[[NSRunLoop currentRunLoop] run];
[pool release]; return 0;
}</lang>
Client
client.m <lang objc>#import <Foundation/Foundation.h>
- import "ActionObjectProtocol.h"
int main(void) {
NSAutoreleasePool *pool; NSArray *args; id <ActionObjectProtocol> action; NSString *msg, *backmsg;
pool = [[NSAutoreleasePool alloc] init];
action = (id <ActionObjectProtocol>) [NSConnection rootProxyForConnectionWithRegisteredName: @"DistributedAction" host: @"localhost" usingNameServer: [NSSocketPortNameServer sharedInstance] ];
if (action == nil) { NSLog(@"can't connect to the server"); exit(EXIT_FAILURE); } args = [[NSProcessInfo processInfo] arguments];
if ([args count] == 1) { NSLog(@"specify a message"); exit(EXIT_FAILURE); } msg = [args objectAtIndex: 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 backmsg = [action sendMessage: msg]; NSLog("%@", backmsg);
[pool release]; return 0;
}</lang>
OCaml
Minimalistic distributed logger with synchronous channels using the join calculus on top of OCaml.
Server
<lang ocaml>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 ()</lang>
Client
<lang ocaml>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"</lang>
Oz
We show a program that starts a server on a remote machine, exchanges two messages with that server and finally shuts it down.
<lang oz>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}</lang>
Perl
Using Data::Dumper and Safe to transmit arbitrary data structures as serialized text between hosts. Same code works as both sender and receiver. <lang Perl>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] }); }</lang>
PicoLisp
Server
<lang PicoLisp>(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</lang>
Client
<lang PicoLisp>(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</lang>
Output:
PID 18372 Result 12
Python
XML-RPC
Protocol: XML-RPC
Server
<lang python>#!/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()</lang>
Client
<lang python>#!/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'</lang>
HTTP
Protocol: HTTP
Server
<lang python>#!/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>
This is our body. You wanted to visit %s page
</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()</lang>
Client
<lang python>#!/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()</lang>
Socket, Plain Text
Protocol: Plain Text
Use with Pythons pickle module for data serialization into printable text would allow the transfer of arbitrary Python data, but as it stands, this method is too low level to fulfill the task.
Server
<lang python>#!/usr/bin/python
- -*- coding: utf-8 -*-
import SocketServer
HOST = "localhost" PORT = 8000
- our instance that will upper whatever it gets and send back to client
class UpperCaseHandler(SocketServer.StreamRequestHandler):
def handle(self): print '%s connected' % self.client_address[0] # get what client sends get = self.rfile.readline() # write back to client self.wfile.write(get.upper())
if __name__ == '__main__':
tcpserver = SocketServer.TCPServer((HOST, PORT), UpperCaseHandler) try: tcpserver.serve_forever() except KeyboardInterrupt: print 'Exiting...' tcpserver.server_close()</lang>
Client
<lang python>#!/usr/bin/python
- -*- coding: utf-8 -*-
import socket
HOST = "localhost" PORT = 8000
DATA = "my name is eren"
- connect to server and send data
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((HOST, PORT)) sock.send("%s\n" % DATA)
- get
response = sock.recv(256) sock.close()
print "We sent: %s" % DATA print 'Server responded: %s' % response</lang>
Pyro
Note: You should install Pyro (http://pyro.sourceforge.net) first and run pyro-ns binary to run code below.
Server
<lang python>#!/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()</lang>
Client
<lang python>#!/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)</lang>
Spread
Note: You should install Spread (http://www.spread.org) and its python bindings (http://www.python.org/other/spread/)
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
Client (Listener)
<lang python>#!/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</lang>
Client (Sender)
<lang python>#!/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()</lang>
Ruby
Uses the distributed Ruby (dRuby) from the standard library. The "druby:" protocol uses TCP/IP sockets for communication.
Server <lang ruby>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</lang>
Client <lang ruby>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</lang>
Tcl
A rudimentary IRC Server <lang tcl>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</lang> Client <lang tcl>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</lang>
UnixPipes
Uses netcat and a buffer to cycle the server shell's stdout back to netcat's stdin.
Server
<lang bash>: >/tmp/buffer tail -f /tmp/buffer | nc -l 127.0.0.1 1234 | sh >/tmp/buffer 2>&1</lang>
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.
Client
<lang bash>nc 127.0.0.1 1234</lang>
Now you can enter commands in the client terminal and get the output back through the same connection.
- Programming Tasks
- Networking and Web Interaction
- Ada
- AutoHotkey
- C
- C sharp
- C sharp examples needing attention
- Examples needing attention
- D
- D examples needing attention
- E
- Erlang
- Go
- JavaScript
- Mathematica
- Objective-C
- OCaml
- Oz
- Perl
- PicoLisp
- Python
- Python examples needing attention
- Ruby
- Tcl
- UnixPipes
- Nc
- Lotus 123 Macro Scripting/Omit
- PARI/GP/Omit
- Retro/Omit