Distributed programming: Difference between revisions
m (→{{header|Wren}}: Minor tidy) |
|||
(87 intermediate revisions by 50 users not shown) | |||
Line 1: | Line 1: | ||
{{task|Networking and Web Interaction}} |
{{task|Networking and Web Interaction}} |
||
Write two programs (or one program with two modes) which run on networked computers, and send some messages between them. |
|||
Given two computers on a network, send messages between them. The protocol used may be language-specific or not, and should be suitable for general distributed programming. |
|||
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. |
|||
=={{header|AutoHotkey}}== |
|||
WinSock2 library created by derRaphael. Basic code structure created by Trikster. Untested. If someone reading this has two computers and AutoHotkey installed on both, please test. My other computers are all in states of semi-decay. |
|||
<lang autohotkey>/* |
|||
WinSock2.ahk // a rewrite by derRaphael (w) Sep, 9 2008 |
|||
This task is intended to demonstrate high-level communication facilities beyond just creating [[sockets]]. |
|||
based on the WinLIRC Script from Chris |
|||
http://www.autohotkey.com/docs/scripts/WinLIRC.htm |
|||
and on the WinLIRC Rewrite by ZedGecko |
|||
http://www.autohotkey.com/forum/viewtopic.php?t=13829 |
|||
__WSA_GetHostByName - Parts based upon scripts from DarviK |
|||
and Tasman. Not much left of the origin source, but it was |
|||
their achievement by doing the neccessary research. |
|||
*/ |
|||
=={{header|Ada}}== |
|||
; WS2 Connect - This establishes a connection to a named resource |
|||
{{works with|GNAT GPL|2010}} |
|||
; The parameter is to be passed in an URI:Port manner. |
|||
{{works with|PolyORB}} |
|||
; Returns the socket upon successfull connection, otherwise it |
|||
; returns -1. In the latter case more Information is in the global |
|||
; variable __WSA_ErrMsg |
|||
; |
|||
; Usage-Example: |
|||
; Pop3_Socket := WS2_Connect("mail.isp.com:110") |
|||
; See the Doc for more Information. |
|||
WS2_Connect(lpszUrl) { |
|||
Ada defines facilities for distributed systems in its standard (Annex E, also called DSA). |
|||
Global |
|||
This example works with PolyORB and the GNAT GPL 2010 compiler from AdaCore. |
|||
; split our targetURI |
|||
__WinINet_InternetCrackURL("info://" lpszUrl,"__WSA") |
|||
server.ads: |
|||
; name our port |
|||
<syntaxhighlight lang="ada">package Server is |
|||
WS2_Port := __WSA_nPort |
|||
pragma Remote_Call_Interface; |
|||
procedure Foo; |
|||
; Init the Winsock connection |
|||
function Bar return Natural; |
|||
if ( !( __WSA_ScriptInit() ) ; Init the Scriptvariables |
|||
end Server;</syntaxhighlight> |
|||
|| !( __WSA_Startup() ) ) { ; Fire up the WSA |
|||
WS2_CleanUp() ; Do a premature cleanup |
|||
return -1 ; and return an error indication |
|||
} |
|||
; check the URI if it's valid |
|||
if (RegExMatch(__WSA_lpszHostName,"[^\d.]+")) ; Must be different than IP |
|||
{ |
|||
WS2_IPAddress := __WSA_GetHostByName(__WSA_lpszHostName) |
|||
} else { ; Let's check if the IP is valid |
|||
StringSplit,__WSA_tmpIPFragment, __WSA_lpszHostName,. |
|||
Loop,4 |
|||
If ( ( __WSA_tmpIPFragment%A_Index%<0 ) |
|||
|| ( __WSA_tmpIPFragment%A_Index%>255 ) |
|||
|| ( __WSA_tmpIPFragment0!=4 ) ) { |
|||
__WSA_IPerror = 1 |
|||
Break |
|||
} |
|||
If (__WSA_IPerror=1) |
|||
__WSA_ErrMsg .= "No valid IP Supplied" |
|||
else |
|||
WS2_IPAddress := __WSA_lpszHostName |
|||
} |
|||
server.adb: |
|||
; CONVERSIONS |
|||
<syntaxhighlight lang="ada">package body Server is |
|||
Count : Natural := 0; |
|||
procedure Foo is |
|||
; The htons function returns the value in TCP/IP network byte order. |
|||
begin |
|||
; http://msdn.microsoft.com/en-us/library/ms738557(VS.85).aspx |
|||
Count := Count + 1; |
|||
__WSA_Port := DllCall("Ws2_32\htons", "UShort", WS2_Port) |
|||
end Foo; |
|||
function Bar return Natural is |
|||
; The inet_addr function converts a string containing an IPv4 dotted-decimal |
|||
begin |
|||
; address into a proper address for the IN_ADDR structure. |
|||
return Count; |
|||
; inet_addr: http://msdn.microsoft.com/en-us/library/ms738563(VS.85).aspx |
|||
end Bar; |
|||
; IN_ADDR: http://msdn.microsoft.com/en-us/library/ms738571(VS.85).aspx |
|||
end Server;</syntaxhighlight> |
|||
__WSA_InetAddr := DllCall("Ws2_32\inet_addr", "Str", WS2_IPAddress) |
|||
client.adb: |
|||
If ( ( __WSA_Socket:=__WSA_Socket() ) |
|||
<syntaxhighlight lang="ada">with Server; |
|||
&& ( __WSA_Connect() ) ) |
|||
with Ada.Text_IO; |
|||
return __WSA_Socket ; All went OK, return the SocketID |
|||
Else { |
|||
WS2_CleanUp() ; Do a premature cleanup |
|||
return -1 ; and return an error indication |
|||
} |
|||
} |
|||
procedure Client is |
|||
; WS2 OnMessage - This function defines, whatever should happen when |
|||
begin |
|||
; a Message is received on the socket. |
|||
Ada.Text_IO.Put_Line ("Calling Foo..."); |
|||
; Expected Parameter: |
|||
Server.Foo; |
|||
; Ws2_Socket => Socket returned from WS2_Connect() Call |
|||
Ada.Text_IO.Put_Line ("Calling Bar: " & Integer'Image (Server.Bar)); |
|||
; UDF => An UserDefinedFunction to which the received |
|||
end Client;</syntaxhighlight> |
|||
; Data will be passed to |
|||
; Optional Parameter: |
|||
; WindowMessage => A number indicating upon which WM_Message to react |
|||
; |
|||
; Returns -1 on error, 0 on success |
|||
required config (dsa.cfg): |
|||
WS2_AsyncSelect(Ws2_Socket,UDF,WindowMessage="") { |
|||
<syntaxhighlight lang="ada">configuration DSA is |
|||
Global __WSA_ErrMsg |
|||
pragma Starter (None); |
|||
If ( ( StrLen(Ws2_Socket)=0 ) |
|||
|| ( StrLen(UDF)=0 ) ) { |
|||
res := -1 |
|||
} else { |
|||
If ( (StrLen(WindowMessage)=0) |
|||
|| (WindowMessage+0=0) ) |
|||
WindowMessage := 0x5000 |
|||
res := __WSA_AsyncSelect(Ws2_Socket, UDF, WindowMessage) |
|||
} |
|||
return res |
|||
} |
|||
-- Server |
|||
WS2_SendData(WS2_Socket,DataToSend) { |
|||
Server_Partition : Partition := (Server); |
|||
Global __WSA_ErrMsg |
|||
procedure Run_Server is in Server_Partition; |
|||
If (__WSA_send(WS2_Socket, DataToSend)=-1) { |
|||
MsgBox, 16, %A_ScriptName%: Send-Error, % __WSA_ErrMsg |
|||
} |
|||
} |
|||
-- Client |
|||
; WS2 Cleanup - This needs to be called whenever Your Script exits |
|||
Client_Partition : Partition; |
|||
; Usually this is invoked by some OnExit, Label subroutines. |
|||
for Client_Partition'Termination use Local_Termination; |
|||
WS2_CleanUp() { |
|||
procedure Client; |
|||
DllCall("Ws2_32\WSACleanup") |
|||
for Client_Partition'Main use Client; |
|||
} |
|||
end DSA;</syntaxhighlight> |
|||
compilation: |
|||
WS2_Disconnect(WS2_Socket) { |
|||
<pre>$po_gnatdist dsa.cfg |
|||
Global __WSA_ErrMsg |
|||
[...] |
|||
if (res := __WSA_CloseSocket(WS2_Socket)) |
|||
------------------------------ |
|||
MsgBox, 16, %A_ScriptName%: CloseSocket-Error, % __WSA_ErrMsg |
|||
---- Configuration report ---- |
|||
} |
|||
------------------------------ |
|||
Configuration : |
|||
Name : dsa |
|||
Main : run_server |
|||
Starter : none |
|||
Partition server_partition |
|||
; WS2 ScriptInit - for internal use only |
|||
Main : run_server |
|||
; Initializes neccessary variables for this Script. |
|||
Units : |
|||
__WSA_ScriptInit() |
|||
- server (rci) |
|||
{ |
|||
- run_server (normal) |
|||
; CONTANTS |
|||
- polyorb.dsa_p.partitions (rci, from PCS) |
|||
Environment variables : |
|||
; We're working with version 2 of Winsock |
|||
- "POLYORB_DSA_NAME_SERVICE" |
|||
Local VersionRequested := 2 |
|||
; from http://msdn.microsoft.com/en-us/library/ms742212(VS.85).aspx |
|||
Local AF_INET := 2 |
|||
Local SOCK_STREAM := 1 |
|||
Local IPPROTO_TCP := 6 |
|||
Local FD_READ := 0x1 |
|||
Local FD_CLOSE := 0x20 |
|||
Partition client_partition |
|||
__AI_PASSIVE := 1 |
|||
Main : client |
|||
Termination : local |
|||
Units : |
|||
- client (normal) |
|||
Environment variables : |
|||
__WSA_WSVersion := VersionRequested |
|||
- "POLYORB_DSA_NAME_SERVICE" |
|||
__WSA_SocketProtocol := IPPROTO_TCP |
|||
__WSA_SocketAF := AF_INET |
|||
__WSA_lEvent := FD_READ|FD_CLOSE |
|||
------------------------------- |
|||
__WSA_WOULDBLOCK := 10035 ; http://www.sockets.com/err_lst1.htm#WSAECONNRESET |
|||
[...]</pre> |
|||
__WSA_CONNRESET := 10054 ; http://www.sockets.com/err_lst1.htm#WSAECONNRESET |
|||
preparation (run PolyORB name service): |
|||
return 1 |
|||
<pre>$ po_ioc_naming |
|||
} |
|||
POLYORB_CORBA_NAME_SERVICE=IOR:010000002b00000049444[...] |
|||
POLYORB_CORBA_NAME_SERVICE=corbaloc:iiop:1.2@10.200.[...]</pre> |
|||
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. |
|||
; WS2 Startup - for internal use only |
|||
; Initializes the Winsock 2 Adapter |
|||
__WSA_Startup() |
|||
{ |
|||
Global WSAData, __WSA_ErrMsg, __WSA_WSVersion |
|||
running server: |
|||
; It's a good idea, to have a __WSA_ErrMsg Container, so any Error Msgs |
|||
<pre>$ ./server_partition</pre> |
|||
; may be catched by the script. |
|||
__WSA_ErrMsg := "" |
|||
running client: |
|||
; Generate Structure for the lpWSAData |
|||
<pre>$ ./client_partition |
|||
; as stated on http://msdn.microsoft.com/en-us/library/ms742213.aspx |
|||
Calling Foo... |
|||
; More on WSADATA (structure) to be found here: |
|||
Calling Bar: 1 |
|||
; http://msdn.microsoft.com/en-us/library/ms741563(VS.85).aspx |
|||
$ ./client_partition |
|||
VarSetCapacity(WSAData, 32) |
|||
Calling Foo... |
|||
result := DllCall("Ws2_32\WSAStartup", "UShort", __WSA_WSVersion, "UInt", &WSAData) |
|||
Calling Bar: 2</pre> |
|||
=={{header|AutoHotkey}}== |
|||
if (ErrorLevel) |
|||
See [[Distributed program/AutoHotkey]]. |
|||
__WSA_ErrMsg .= "Ws2_32\WSAStartup could not be called due to error " ErrorLevel "`n" |
|||
. "Winsock 2.0 or higher is required.`n" |
|||
if (result!=0) |
|||
__WSA_ErrMsg .= "Ws2_32\WSAStartup " __WSA_GetLastError() |
|||
=={{header|C}}== |
|||
If (StrLen(__WSA_ErrMsg)>0) |
|||
Using PVM [http://www.csm.ornl.gov/pvm/pvm_home.html] |
|||
Return -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. |
|||
Else |
|||
<syntaxhighlight lang="c">#include <stdio.h> |
|||
Return 1 |
|||
#include <stdlib.h> |
|||
} |
|||
#include <pvm3.h> |
|||
int main(int c, char **v) |
|||
; WS2 Socket Descriptor - for internal use only |
|||
{ |
|||
; Sets type and neccessary structures for a successfull connection |
|||
int tids[10]; |
|||
__WSA_Socket() |
|||
int parent, spawn; |
|||
{ |
|||
int i_data, i2; |
|||
Global __WSA_ErrMsg, __WSA_SocketProtocol, __WSA_SocketType, __WSA_SocketAF |
|||
double f_data; |
|||
if (c > 1) { |
|||
; Supposed to return a descriptor referencing the new socket |
|||
spawn = pvm_spawn("/tmp/a.out", 0, PvmTaskDefault, 0, 1, tids); |
|||
; http://msdn.microsoft.com/en-us/library/ms742212(VS.85).aspx |
|||
if (spawn <= 0) { |
|||
__WSA_Socket := DllCall("Ws2_32\socket" |
|||
printf("Can't spawn task\n"); |
|||
, "Int", __WSA_SocketAF |
|||
return 1; |
|||
, "Int", __WSA_SocketType |
|||
} |
|||
, "Int", __WSA_SocketProtocol) |
|||
if (socket = -1) |
|||
__WSA_ErrMsg .= "Ws2_32\socket " __WSA_GetLastError() |
|||
printf("Spawning successful\n"); |
|||
If (StrLen(__WSA_ErrMsg)>0) |
|||
Return -1 |
|||
Else |
|||
Return __WSA_Socket |
|||
/* 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); |
|||
; WS2 Connection call - for internal use only |
|||
pvm_unpackf("%d %lf", &i_data, &f_data); |
|||
; Establishes a connection to a foreign IP at the specified port |
|||
printf("got msg type 1: %d %f\n", i_data, f_data); |
|||
__WSA_Connect() |
|||
} else { |
|||
{ |
|||
parent = pvm_parent(); |
|||
Global __WSA_ErrMsg, __WSA_Port, __WSA_Socket, __WSA_InetAddr, __WSA_SocketAF |
|||
pvm_initsend(PvmDataDefault); |
|||
; Generate socketaddr structure for the connect() |
|||
i_data = rand(); |
|||
; http://msdn.microsoft.com/en-us/library/ms740496(VS.85).aspx |
|||
f_data = (double)rand() / RAND_MAX; |
|||
__WSA_SockAddrNameLen := 16 |
|||
pvm_packf("%d %lf", i_data, f_data); |
|||
VarSetCapacity(__WSA_SockAddr, __WSA_SockAddrNameLen) |
|||
pvm_send(parent, 1); /* send msg type 1 */ |
|||
NumPut(__WSA_SocketAF, __WSA_SockAddr, 0, "UShort") |
|||
NumPut(__WSA_Port, __WSA_SockAddr, 2, "UShort") |
|||
NumPut(__WSA_InetAddr, __WSA_SockAddr, 4) |
|||
pvm_initsend(PvmDataDefault); |
|||
; The connect function establishes a connection to a specified socket. |
|||
i2 = rand(); |
|||
; http://msdn.microsoft.com/en-us/library/ms737625(VS.85).aspx |
|||
pvm_packf("%d %d", i_data, i2); |
|||
result := DllCall("Ws2_32\connect" |
|||
pvm_send(parent, 2); /* send msg type 2 */ |
|||
, "UInt", __WSA_Socket |
|||
} |
|||
, "UInt", &__WSA_SockAddr |
|||
, "Int" , __WSA_SockAddrNameLen) |
|||
if (result) |
|||
__WSA_ErrMsg .= "Ws2_32\connect " __WSA_GetLastError() |
|||
pvm_exit(); |
|||
If (StrLen(__WSA_ErrMsg)>0) |
|||
return 0; |
|||
Return -1 |
|||
}</syntaxhighlight>{{out}}(running it on PVM console, exe is /tmp/a.out)<syntaxhighlight lang="text">pvm> spawn -> /tmp/a.out 1 |
|||
Else |
|||
spawn -> /tmp/a.out 1 |
|||
Return 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</syntaxhighlight> |
|||
=={{header|C sharp|C#}}== |
|||
Start the program with "server" parameter to start the server, and "client" to start the client. The client will send data to the server and receive a response. The server will wait for data, display the data received, and send a response. |
|||
<syntaxhighlight lang="csharp"> |
|||
/* |
|||
using System; |
|||
This code based originally upon an example by DarviK |
|||
using System.IO; |
|||
http://www.autohotkey.com/forum/topic8871.html |
|||
using System.Net; |
|||
and on the modifcations by Tasman |
|||
using System.Net.Sockets; |
|||
http://www.autohotkey.com/forum/viewtopic.php?t=9937 |
|||
using System.Runtime.Serialization.Formatters.Binary; |
|||
*/ |
|||
using System.Threading.Tasks; |
|||
; Resolves canonical domainname to IP |
|||
__WSA_GetHostByName(url) |
|||
{ |
|||
Global __WSA_ErrMsg |
|||
; gethostbyname returns information about a domainname into a Hostent Structure |
|||
; http://msdn.microsoft.com/en-us/library/ms738524(VS.85).aspx |
|||
IP := "" |
|||
if ((PtrHostent:=DllCall("Ws2_32\gethostbyname","str",url)) != 0) { |
|||
Loop, 1 ; 3 is max No of retrieved addresses |
|||
If (PtrTmpIP := NumGet(NumGet(PtrHostent+12)+(offset:=(A_Index-1)*4),offset)) { |
|||
IP := (IP) ? IP "|" : "" |
|||
Loop, 4 ; Read our IP address |
|||
IP .= NumGet(PtrTmpIP+offset,(A_Index-1 ),"UChar") "." |
|||
IP := SubStr(IP,1,-1) |
|||
} else ; No more IPs left |
|||
Break |
|||
result := IP |
|||
} else { |
|||
__WSA_ErrMsg .= "Ws2_32\gethostbyname failed`n " |
|||
result := -1 |
|||
} |
|||
return result |
|||
} |
|||
using static System.Console; |
|||
; Return the last Error with a lil bit o' text if neccessary |
|||
; Note: the txt variable is set to 0 when checking for received content |
|||
__WSA_GetLastError(txt=1) |
|||
{ |
|||
Err := DllCall("Ws2_32\WSAGetLastError") |
|||
ExtraInfo := __WSA_ErrLookUp(RegExReplace(Err,"[^\d]")) |
|||
If ((InStr(ExtraInfo,"Sorry, no")) || (txt!=1)) |
|||
ExtraInfo := "" |
|||
Return ( txt ? "indicated Winsock error " : "") |
|||
. Err |
|||
. ( txt ? "`n" ExtraInfo : "") |
|||
} |
|||
class DistributedProgramming |
|||
; WS2 AsyncSelect - for internal use only |
|||
{ |
|||
; Sets up an Notification Handler for Receiving Messages |
|||
const int Port = 555; |
|||
; Expected Parameters: Socket from Initialisation |
|||
; Optional: NotificationMsg - default 0x5000 |
|||
; WSA_DataReiceiver - an different Name to standard |
|||
; wm_* processor function. |
|||
; default __WSA_ReceiveData |
|||
; Returns -1 on Error, 0 on success |
|||
__WSA_AsyncSelect(__WSA_Socket, UDF, __WSA_NotificationMsg=0x5000 |
|||
,__WSA_DataReceiver="__WSA_recv") |
|||
{ |
|||
Global |
|||
async static Task RunClient() |
|||
__WSA_UDF := UDF |
|||
{ |
|||
WriteLine("Connecting"); |
|||
var client = new TcpClient(); |
|||
await client.ConnectAsync("localhost", Port); |
|||
using (var stream = client.GetStream()) |
|||
OnMessage(__WSA_NotificationMsg, __WSA_DataReceiver) |
|||
{ |
|||
; The WSAAsyncSelect function requests Windows message-based notification |
|||
WriteLine("Sending loot"); |
|||
; of network events for a socket. |
|||
var data = Serialize(new SampleData()); |
|||
; http://msdn.microsoft.com/en-us/library/ms741540(VS.85).aspx |
|||
await stream.WriteAsync(data, 0, data.Length); |
|||
Result := DllCall("Ws2_32\WSAAsyncSelect" |
|||
, "UInt", __WSA_Socket |
|||
, "UInt", __WSA_GetThisScriptHandle() |
|||
, "UInt", __WSA_NotificationMsg |
|||
, "Int", __WSA_lEvent) |
|||
if (Result) { |
|||
__WSA_ErrMsg .= "Ws2_32\WSAAsyncSelect() " . __WSA_GetLastError() |
|||
Result := -1 |
|||
} |
|||
Return Result |
|||
} |
|||
WriteLine("Receiving thanks"); |
|||
; WS2 Receive - for internal use only |
|||
var buffer = new byte[80000]; |
|||
; Triggers upon Notification Handler when Receiving Messages |
|||
var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length); |
|||
__WSA_recv(wParam, lParam) |
|||
var thanks = (string)Deserialize(buffer, bytesRead); |
|||
{ |
|||
WriteLine(thanks); |
|||
Global __WSA_UDF, __WSA_ErrMsg |
|||
} |
|||
; __WSA_UDF containes the name of the UserDefinedFunction to call when the event |
|||
; has been triggered and text may be processed (allthough the reveived text might |
|||
; be inclomplete, especially when receiving large chunks of data, like in eMail- |
|||
; attachments or sometimes in IRC). The UDF needs to accept two parameter: socket |
|||
; and the received buffer |
|||
__WSA_Socket := wParam |
|||
__WSA_BufferSize = 4096 |
|||
Loop |
|||
{ |
|||
VarSetCapacity(__WSA_Buffer, __WSA_BufferSize, 0) |
|||
__WSA_BufferLength := DllCall("Ws2_32\recv" |
|||
, "UInt", __WSA_Socket |
|||
, "Str", __WSA_Buffer |
|||
, "Int", __WSA_BufferSize |
|||
, "Int", 0 ) |
|||
if (__WSA_BufferLength = 0) |
|||
break |
|||
if (__WSA_BufferLength = -1) |
|||
{ |
|||
__WSA_Err := __WSA_GetLastError(0) |
|||
; __WSA_WOULDBLOCK (from http://www.sockets.com/) |
|||
; The socket is marked as non-blocking (non-blocking operation mode), and |
|||
; the requested operation is not complete at this time. The operation is |
|||
; underway, but as yet incomplete. |
|||
if (__WSA_Err = __WSA_WOULDBLOCK ) |
|||
return 1 |
|||
client.Close(); |
|||
; __WSA_CONNRESET: (from http://www.sockets.com/) |
|||
} |
|||
; A connection was forcibly closed by a peer. This normally results from |
|||
; a loss of the connection on the remote socket due to a timeout or a reboot. |
|||
if (__WSA_Err != __WSA_CONNRESET) |
|||
__WSA_ErrMsg .= "Ws2_32\recv indicated Winsock error " __WSA_Err "`n" |
|||
break |
|||
} |
|||
async static Task RunServer() |
|||
if (StrLen(__WSA_UDF)!=0) ; If set, call UserDefinedFunction and pass Buffer to it |
|||
{ |
|||
%__WSA_UDF%(__WSA_Socket,__WSA_Buffer) |
|||
WriteLine("Listening"); |
|||
} |
|||
var listener = new TcpListener(IPAddress.Any, Port); |
|||
return 1 |
|||
listener.Start(); |
|||
} |
|||
var client = await listener.AcceptTcpClientAsync(); |
|||
using (var stream = client.GetStream()) |
|||
{ |
|||
WriteLine("Receiving loot"); |
|||
var buffer = new byte[80000]; |
|||
var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length); |
|||
var data = (SampleData)Deserialize(buffer, bytesRead); |
|||
WriteLine($"{data.Loot} at {data.Latitude}, {data.Longitude}"); |
|||
WriteLine("Sending thanks"); |
|||
var thanks = Serialize("Thanks!"); |
|||
await stream.WriteAsync(thanks, 0, thanks.Length); |
|||
} |
|||
client.Close(); |
|||
; WSA Send - for internal use only |
|||
listener.Stop(); |
|||
; Users are encouraged to use the WS2_SendData() Function |
|||
Write("Press a key"); |
|||
__WSA_send(__WSA_Socket, __WSA_Data) |
|||
ReadKey(); |
|||
{ |
|||
} |
|||
Global __WSA_ErrMsg |
|||
static byte[] Serialize(object data) |
|||
Result := DllCall("Ws2_32\send" |
|||
{ |
|||
, "UInt", __WSA_Socket |
|||
using (var mem = new MemoryStream()) |
|||
{ |
|||
, "Int", StrLen(__WSA_Data) |
|||
new BinaryFormatter().Serialize(mem, data); |
|||
return mem.ToArray(); |
|||
If (Result = -1) |
|||
} |
|||
__WSA_ErrMsg .= "Ws2_32\send " __WSA_GetLastError() |
|||
} |
|||
} |
|||
static object Deserialize(byte[] data, int length) |
|||
; Closes Open Socket - for internal use only |
|||
{ |
|||
; Returns 0 on success |
|||
using (var mem = new MemoryStream(data, 0, length)) |
|||
__WSA_CloseSocket(__WSA_Socket) |
|||
{ |
|||
{ |
|||
return new BinaryFormatter().Deserialize(mem); |
|||
Global __WSA_ErrMsg |
|||
} |
|||
} |
|||
static void Main(string[] args) |
|||
Result := DllCall("Ws2_32\closesocket" |
|||
{ |
|||
, "UInt", __WSA_Socket) |
|||
if (args.Length == 0) return; |
|||
__WSA_ErrMsg .= "Ws2_32\closesocket " __WSA_GetLastError() |
|||
switch (args[0]) |
|||
Return result |
|||
{ |
|||
} |
|||
case "client": RunClient().Wait(); break; |
|||
case "server": RunServer().Wait(); break; |
|||
} |
|||
} |
|||
} |
|||
[Serializable] |
|||
; GetThisScriptHandle - for internal use only |
|||
class SampleData |
|||
; Returns the handle of the executing script |
|||
{ |
|||
__WSA_GetThisScriptHandle() |
|||
public decimal Latitude = 44.33190m; |
|||
{ |
|||
public decimal Longitude = 114.84129m; |
|||
public string Loot = "140 tonnes of jade"; |
|||
} |
|||
</syntaxhighlight> |
|||
=={{header|D}}== |
|||
HiddenWindowsSave := A_DetectHiddenWindows |
|||
Uses the <b>rpc</b> 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. |
|||
DetectHiddenWindows On |
|||
<syntaxhighlight lang="d">import arsd.rpc; |
|||
ScriptMainWindowId := WinExist("ahk_class AutoHotkey ahk_pid " DllCall("GetCurrentProcessId")) |
|||
DetectHiddenWindows %HiddenWindowsSave% |
|||
struct S1 { |
|||
Return ScriptMainWindowId |
|||
int number; |
|||
} |
|||
string name; |
|||
} |
|||
struct S2 { |
|||
; Lookup Winsock ErrCode - for internal use only |
|||
string name; |
|||
; This list is form http://www.sockets.com |
|||
int number; |
|||
__WSA_ErrLookUp(sNumber) { |
|||
} |
|||
WSA_ErrorList = |
|||
(LTrim Join`n |
|||
10004 - Interrupted system call |
|||
10009 - Bad file number |
|||
10013 - Permission denied |
|||
10014 - Bad address |
|||
10022 - Invalid argument |
|||
10024 - Too many open files |
|||
10035 - Operation would block |
|||
10036 - Operation now in progress |
|||
10037 - Operation already in progress |
|||
10038 - Socket operation on non-socket |
|||
10039 - D estination address required |
|||
10040 - Message too long |
|||
10041 - Protocol wrong type for socket |
|||
10042 - Bad protocol option |
|||
10043 - Protocol not supported |
|||
10044 - Socket type not supported |
|||
10045 - Operation not supported on socket |
|||
10046 - Protocol family not supported |
|||
10047 - Address family not supported by protocol family |
|||
10048 - Address already in use |
|||
10049 - Can't assign requested address |
|||
10050 - Network is down |
|||
10051 - Network is unreachable |
|||
10052 - Net dropped connection or reset |
|||
10053 - Software caused connection abort |
|||
10054 - Connection reset by peer |
|||
10055 - No buffer space available |
|||
10056 - Socket is already connected |
|||
10057 - Socket is not connected |
|||
10058 - Can't send after socket shutdown |
|||
10059 - Too many references, can't splice |
|||
10060 - Connection timed out |
|||
10061 - Connection refused |
|||
10062 - Too many levels of symbolic links |
|||
10063 - File name too long |
|||
10064 - Host is down |
|||
10065 - No Route to Host |
|||
10066 - Directory not empty |
|||
10067 - Too many processes |
|||
10068 - Too many users |
|||
10069 - Disc Quota Exceeded |
|||
10070 - Stale NFS file handle |
|||
10091 - Network SubSystem is unavailable |
|||
10092 - WINSOCK DLL Version out of range |
|||
10093 - Successful WSASTARTUP not yet performed |
|||
10071 - Too many levels of remote in path |
|||
11001 - Host not found |
|||
11002 - Non-Authoritative Host not found |
|||
11003 - Non-Recoverable errors: FORMERR, REFUSED, NOTIMP |
|||
11004 - Valid name, no data record of requested type |
|||
11004 - No address, look for MX record |
|||
) |
|||
ExNr := 0, ExErr := "Sorry, but no definition available." |
|||
Loop,Parse,WSA_ErrorList,`n |
|||
{ |
|||
RegExMatch(A_LoopField,"(?P<Nr>\d+) - (?P<Err>.*)",Ex) |
|||
if (sNumber = ExNr) |
|||
break |
|||
} |
|||
Return ExNr " means " ExErr "`n" |
|||
} |
|||
; WinINet InternetCrackURL - for internal use only |
|||
; v 0.1 / (w) 25.07.2008 by derRaphael / zLib-Style release |
|||
; This routine was originally posted here: |
|||
; http://www.autohotkey.com/forum/viewtopic.php?p=209957#209957 |
|||
__WinINet_InternetCrackURL(lpszUrl,arrayName="URL") |
|||
{ |
|||
local hModule, offset_name_length |
|||
hModule := DllCall("LoadLibrary", "Str", "WinINet.Dll") |
|||
interface ExampleNetworkFunctions { |
|||
; SetUpStructures for URL_COMPONENTS / needed for InternetCrackURL |
|||
string sayHello(string name); |
|||
; http://msdn.microsoft.com/en-us/library/aa385420(VS.85).aspx |
|||
int add(in int a, in int b) const pure nothrow; |
|||
offset_name_length:= "4-lpszScheme-255|16-lpszHostName-1024|28-lpszUserName-1024|" |
|||
S2 structTest(S1); |
|||
. "36-lpszPassword-1024|44-lpszUrlPath-1024|52-lpszExtrainfo-1024" |
|||
void die(); |
|||
} |
|||
// The server must implement the interface. |
|||
VarSetCapacity(URL_COMPONENTS,60,0) |
|||
class ExampleServer : ExampleNetworkFunctions { |
|||
; Struc Size ; Scheme Size ; Max Port Number |
|||
override string sayHello(string name) { |
|||
NumPut(60,URL_COMPONENTS,0), NumPut(255,URL_COMPONENTS,12), NumPut(0xffff,URL_COMPONENTS,24) |
|||
return "Hello, " ~ name; |
|||
Loop,Parse,offset_name_length,| |
|||
} |
|||
RegExMatch(A_LoopField,"(?P<Offset>\d+)-(?P<Name>[a-zA-Z]+)-(?P<Size>\d+)",iCU_) |
|||
override int add(in int a, in int b) const pure nothrow { |
|||
VarSetCapacity(%iCU_Name%,iCU_Size,0) |
|||
return a + b; |
|||
NumPut(&%iCU_Name%,URL_COMPONENTS,iCU_Offset) |
|||
} |
|||
NumPut(iCU_Size,URL_COMPONENTS,iCU_Offset+4) |
|||
} |
|||
override S2 structTest(S1 a) { |
|||
return S2(a.name, a.number); |
|||
} |
|||
override void die() { |
|||
throw new Exception("death requested"); |
|||
} |
|||
mixin NetworkServer!ExampleNetworkFunctions; |
|||
; Split the given URL; extract scheme, user, pass, authotity (host), port, path, and query (extrainfo) |
|||
; http://msdn.microsoft.com/en-us/library/aa384376(VS.85).aspx |
|||
DllCall("WinINet\InternetCrackUrlA","Str",lpszUrl,"uInt",StrLen(lpszUrl),"uInt",0,"uInt",&URL_COMPONENTS) |
|||
; Update variables to retrieve results |
|||
Loop,Parse,offset_name_length,| |
|||
{ |
|||
RegExMatch(A_LoopField,"-(?P<Name>[a-zA-Z]+)-",iCU_) |
|||
VarSetCapacity(%iCU_Name%,-1) |
|||
%arrayName%_%iCU_Name% := % %iCU_Name% |
|||
} |
|||
%arrayName%_nPort:=NumGet(URL_COMPONENTS,24,"uInt") |
|||
DllCall("FreeLibrary", "UInt", hModule) ; unload the library |
|||
} |
} |
||
#Persistent |
|||
#SingleInstance, force |
|||
#Include WinSock2.ahk ; derRaphaels WinSock Rewrite |
|||
SetBatchLines, -1 |
|||
SetWorkingDir, %A_ScriptDir% |
|||
Gui, Add, Button, gB1, Button number One! |
|||
Gui, Add, Edit, vE1, Type in me! |
|||
Gui, Add, Button, gB2, Press me after you're done typing! |
|||
Gui, Add, DropDownList, vDDL1 gDDL1, Select something!||One|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten |
|||
Gui, Add, Button, gB3, Click me! |
|||
class Client { |
|||
Server := "irc.freenode.net" |
|||
mixin NetworkClient!ExampleNetworkFunctions; |
|||
Port := "6667" |
|||
} |
|||
void main(in string[] args) { |
|||
Channel := "#UIHjbfilgbafLIEfbAIJCICBGncaiJBHiwehFIUHEIbnjKCINJEhiUAHEJcKLACui" ; Choose something long and random - a channel sure to be deserted. |
|||
import std.stdio; |
|||
if (args.length > 1) { |
|||
Nick := "somenick" ; Register this! You can drop it later if you want, but register it! |
|||
auto client = new Client("localhost", 5005); |
|||
Pass := "somepass" ; It can be any nick and any pass. |
|||
// 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; |
|||
} |
|||
}</syntaxhighlight> |
|||
=={{header|E}}== |
|||
Name := "distributed program" |
|||
'''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. |
|||
BotCmd := "!" |
|||
OtherBotCmd := "~" |
|||
=== Server === |
|||
OnExit, CleanUp |
|||
WS2_CleanUp() |
|||
If (!Socket := WS2_Connect(Connection := Server . ":" . Port)) |
|||
{ |
|||
MsgBox, 16, Error!, An error occured wilist connecting to %Connection% |
|||
} |
|||
(The protocol is symmetric; this program is the server only in that it is the one which is started first and exports an object.) |
|||
; Set OnMessage function |
|||
WS2_AsyncSelect(Socket, "DataProcess") |
|||
; Send AUTH info to the server |
|||
; USER A-trivial-nickname a-domain-like-google.com some-trivial-stuff :your-real-name |
|||
WS2_SendData(Socket, "USER " . Nick . " google.com AHKBOT :" . Name . "`n") ; All data send to the server must be |
|||
; followed by a newline. |
|||
; PASS A-trivial-pass |
|||
WS2_SendData(Socket, "PASS " . Pass . "`n") |
|||
; NICK A-trivial-nick |
|||
WS2_SendData(Socket, "NICK " . Nick . "`n") |
|||
; Join channel |
|||
; JOIN A-trivial-channel |
|||
WS2_SendData(Socket, "JOIN " . Channel . "`n") |
|||
Gui, Show |
|||
Return |
|||
<syntaxhighlight lang="e">def storage := [].diverge() |
|||
DataProcess(Socket, Data) ; OnMessage function |
|||
{ |
|||
def logService { |
|||
global Server,Port,Channel,Nick,Pass,Name,BotCMD |
|||
to log(line :String) { |
|||
StringSplit, Param, Data, %A_Space% |
|||
storage.push([timer.now(), line]) |
|||
Name := SubStr(Data, 2, InStr(Data, "!")-2) |
|||
} |
|||
StringReplace, Command, Param5, % Chr(10),, All |
|||
to search(substring :String) { |
|||
StringReplace, Command, Command, % Chr(13),, All |
|||
var matches := [] |
|||
for [time, line] ? (line.startOf(substring) != -1) in storage { |
|||
If (Param1 == "PING") |
|||
matches with= [time, line] |
|||
WS2_SendData(Socket, "PONG " . Param2 . "`n") |
|||
} |
|||
Else If (RegExMatch(Data, ":\" . BotCMD . " ")) |
|||
return matches |
|||
{ |
|||
} |
|||
If (Command == "B1" |
|||
MsgBox, The other person pressed Button Number One |
|||
Else If (Command == "B2E1") |
|||
{ |
|||
MsgBox, The other person typed in Edit One then pressed Button Two |
|||
Loop |
|||
{ |
|||
If (A_Index <= 6) |
|||
continue |
|||
If Param%A_Index% |
|||
out := out . " " . Param%A_Index% |
|||
Else |
|||
break |
|||
} |
|||
MsgBox, The other person typed:%out% |
|||
} |
|||
Else If (Command == "DDL1") |
|||
MsgBox, The other person selected %Param7% in the drop-down-list. |
|||
Else If (Command == "B3") |
|||
MsgBox, The other person clicked Button Three. |
|||
} |
|||
return |
|||
} |
} |
||
B1: |
|||
WS2_SendData(Socket, "PRIVMSG " . Channel . " :" . OtherBotCmd . " B1" |
|||
return |
|||
B2: |
|||
Gui, Submit, NoHide |
|||
WS2_SendData(Socket, "PRIVMSG " . Channel . " :" . OtherBotCmd . " B2E1 " . E1 . " | " . B2 |
|||
return |
|||
DDL1: |
|||
Gui, Submit, NoHide |
|||
WS2_SendData(Socket, "PRIVMSG " . Channel . " :" . OtherBotCmd . " DDL1 " . DDL1 |
|||
return |
|||
B3: |
|||
WS2_SendData(Socket, "PRIVMSG " . Channel . " :" . OtherBotCmd . " B3" |
|||
return |
|||
CleanUp: |
|||
GuiClose: |
|||
WS2_CleanUp() |
|||
ExitApp</lang> |
|||
introducer.onTheAir() |
|||
Put the above on one computer, and run it. Then put the below on a different computer (no need for a network, just add Internet!) and run it. |
|||
def sturdyRef := makeSturdyRef.temp(logService) |
|||
println(<captp>.sturdyToURI(sturdyRef)) |
|||
interp.blockAtTop()</syntaxhighlight> |
|||
This will print the URL of the service and run it until aborted. |
|||
<lang autohotkey>/* |
|||
WinSock2.ahk // a rewrite by derRaphael (w) Sep, 9 2008 |
|||
=== Client === |
|||
based on the WinLIRC Script from Chris |
|||
http://www.autohotkey.com/docs/scripts/WinLIRC.htm |
|||
and on the WinLIRC Rewrite by ZedGecko |
|||
http://www.autohotkey.com/forum/viewtopic.php?t=13829 |
|||
__WSA_GetHostByName - Parts based upon scripts from DarviK |
|||
and Tasman. Not much left of the origin source, but it was |
|||
their achievement by doing the neccessary research. |
|||
*/ |
|||
The URL provided by the server is given as the argument to this program. |
|||
; WS2 Connect - This establishes a connection to a named resource |
|||
; The parameter is to be passed in an URI:Port manner. |
|||
; Returns the socket upon successfull connection, otherwise it |
|||
; returns -1. In the latter case more Information is in the global |
|||
; variable __WSA_ErrMsg |
|||
; |
|||
; Usage-Example: |
|||
; Pop3_Socket := WS2_Connect("mail.isp.com:110") |
|||
; See the Doc for more Information. |
|||
WS2_Connect(lpszUrl) { |
|||
<syntaxhighlight lang="e">def [uri] := interp.getArgs() |
|||
Global |
|||
introducer.onTheAir() |
|||
def sturdyRef := <captp>.sturdyFromURI(uri) |
|||
def logService := sturdyRef.getRcvr() |
|||
logService <- log("foot") |
|||
; split our targetURI |
|||
logService <- log("shoe") |
|||
__WinINet_InternetCrackURL("info://" lpszUrl,"__WSA") |
|||
println("Searching...") |
|||
; name our port |
|||
when (def result := logService <- search("foo")) -> { |
|||
WS2_Port := __WSA_nPort |
|||
for [time, line] in result { |
|||
println(`At $time: $line`) |
|||
; Init the Winsock connection |
|||
} |
|||
if ( !( __WSA_ScriptInit() ) ; Init the Scriptvariables |
|||
}</syntaxhighlight> |
|||
|| !( __WSA_Startup() ) ) { ; Fire up the WSA |
|||
WS2_CleanUp() ; Do a premature cleanup |
|||
return -1 ; and return an error indication |
|||
} |
|||
; check the URI if it's valid |
|||
if (RegExMatch(__WSA_lpszHostName,"[^\d.]+")) ; Must be different than IP |
|||
{ |
|||
WS2_IPAddress := __WSA_GetHostByName(__WSA_lpszHostName) |
|||
} else { ; Let's check if the IP is valid |
|||
StringSplit,__WSA_tmpIPFragment, __WSA_lpszHostName,. |
|||
Loop,4 |
|||
If ( ( __WSA_tmpIPFragment%A_Index%<0 ) |
|||
|| ( __WSA_tmpIPFragment%A_Index%>255 ) |
|||
|| ( __WSA_tmpIPFragment0!=4 ) ) { |
|||
__WSA_IPerror = 1 |
|||
Break |
|||
} |
|||
If (__WSA_IPerror=1) |
|||
__WSA_ErrMsg .= "No valid IP Supplied" |
|||
else |
|||
WS2_IPAddress := __WSA_lpszHostName |
|||
} |
|||
=={{header|Erlang}}== |
|||
; CONVERSIONS |
|||
The protocol is erlang's own |
|||
=== Server === |
|||
srv.erl |
|||
<syntaxhighlight lang="erlang">-module(srv). |
|||
; The htons function returns the value in TCP/IP network byte order. |
|||
-export([start/0, wait/0]). |
|||
; http://msdn.microsoft.com/en-us/library/ms738557(VS.85).aspx |
|||
__WSA_Port := DllCall("Ws2_32\htons", "UShort", WS2_Port) |
|||
start() -> |
|||
; The inet_addr function converts a string containing an IPv4 dotted-decimal |
|||
net_kernel:start([srv,shortnames]), |
|||
; address into a proper address for the IN_ADDR structure. |
|||
erlang:set_cookie(node(), rosetta), |
|||
; inet_addr: http://msdn.microsoft.com/en-us/library/ms738563(VS.85).aspx |
|||
Pid = spawn(srv,wait,[]), |
|||
; IN_ADDR: http://msdn.microsoft.com/en-us/library/ms738571(VS.85).aspx |
|||
register(srv,Pid), |
|||
__WSA_InetAddr := DllCall("Ws2_32\inet_addr", "Str", WS2_IPAddress) |
|||
io:fwrite("~p ready~n",[node(Pid)]), |
|||
ok. |
|||
wait() -> |
|||
If ( ( __WSA_Socket:=__WSA_Socket() ) |
|||
receive |
|||
&& ( __WSA_Connect() ) ) |
|||
{echo, Pid, Any} -> |
|||
return __WSA_Socket ; All went OK, return the SocketID |
|||
io:fwrite("-> ~p from ~p~n", [Any, node(Pid)]), |
|||
Else { |
|||
Pid ! {hello, Any}, |
|||
wait(); |
|||
return -1 ; and return an error indication |
|||
Any -> io:fwrite("Error ~p~n", [Any]) |
|||
} |
|||
end.</syntaxhighlight> |
|||
} |
|||
=== Client === |
|||
; WS2 OnMessage - This function defines, whatever should happen when |
|||
client.erl |
|||
; a Message is received on the socket. |
|||
; Expected Parameter: |
|||
; Ws2_Socket => Socket returned from WS2_Connect() Call |
|||
; UDF => An UserDefinedFunction to which the received |
|||
; Data will be passed to |
|||
; Optional Parameter: |
|||
; WindowMessage => A number indicating upon which WM_Message to react |
|||
; |
|||
; Returns -1 on error, 0 on success |
|||
<syntaxhighlight lang="erlang">-module(client). |
|||
WS2_AsyncSelect(Ws2_Socket,UDF,WindowMessage="") { |
|||
-export([start/0, wait/0]). |
|||
Global __WSA_ErrMsg |
|||
If ( ( StrLen(Ws2_Socket)=0 ) |
|||
|| ( StrLen(UDF)=0 ) ) { |
|||
res := -1 |
|||
} else { |
|||
If ( (StrLen(WindowMessage)=0) |
|||
|| (WindowMessage+0=0) ) |
|||
WindowMessage := 0x5000 |
|||
res := __WSA_AsyncSelect(Ws2_Socket, UDF, WindowMessage) |
|||
} |
|||
return res |
|||
} |
|||
start() -> |
|||
WS2_SendData(WS2_Socket,DataToSend) { |
|||
net_kernel:start([client,shortnames]), |
|||
Global __WSA_ErrMsg |
|||
erlang:set_cookie(node(), rosetta), |
|||
If (__WSA_send(WS2_Socket, DataToSend)=-1) { |
|||
{ok,[[Srv]]} = init:get_argument(server), |
|||
MsgBox, 16, %A_ScriptName%: Send-Error, % __WSA_ErrMsg |
|||
io:fwrite("connecting to ~p~n", [Srv]), |
|||
} |
|||
{srv, list_to_atom(Srv)} ! {echo,self(), hi}, |
|||
} |
|||
wait(), |
|||
ok. |
|||
wait() -> |
|||
; WS2 Cleanup - This needs to be called whenever Your Script exits |
|||
receive |
|||
; Usually this is invoked by some OnExit, Label subroutines. |
|||
{hello, Any} -> io:fwrite("Received ~p~n", [Any]); |
|||
WS2_CleanUp() { |
|||
Any -> io:fwrite("Error ~p~n", [Any]) |
|||
DllCall("Ws2_32\WSACleanup") |
|||
end.</syntaxhighlight> |
|||
} |
|||
running it (*comes later) |
|||
WS2_Disconnect(WS2_Socket) { |
|||
|erlc srv.erl |
|||
Global __WSA_ErrMsg |
|||
|erl -run srv start -noshell |
|||
if (res := __WSA_CloseSocket(WS2_Socket)) |
|||
srv@agneyam ready |
|||
MsgBox, 16, %A_ScriptName%: CloseSocket-Error, % __WSA_ErrMsg |
|||
*-> hi from client@agneyam |
|||
} |
|||
|erlc client.erl |
|||
; WS2 ScriptInit - for internal use only |
|||
|erl -run client start -run init stop -noshell -server srv@agneyam |
|||
; Initializes neccessary variables for this Script. |
|||
connecting to "srv@agneyam" |
|||
__WSA_ScriptInit() |
|||
Received hi |
|||
{ |
|||
; CONTANTS |
|||
=={{header|Factor}}== |
|||
; We're working with version 2 of Winsock |
|||
The protocol is the one provided by Factor (concurrency.distributed, concurrency.messaging) |
|||
Local VersionRequested := 2 |
|||
; from http://msdn.microsoft.com/en-us/library/ms742212(VS.85).aspx |
|||
Local AF_INET := 2 |
|||
Local SOCK_STREAM := 1 |
|||
Local IPPROTO_TCP := 6 |
|||
Local FD_READ := 0x1 |
|||
Local FD_CLOSE := 0x20 |
|||
Example summary: |
|||
__AI_PASSIVE := 1 |
|||
- A server node is listening for messages made of natural data types and structures, and simply prettyprint them. |
|||
__WSA_WSVersion := VersionRequested |
|||
__WSA_SocketType := SOCK_STREAM |
|||
__WSA_SocketProtocol := IPPROTO_TCP |
|||
__WSA_SocketAF := AF_INET |
|||
__WSA_lEvent := FD_READ|FD_CLOSE |
|||
- A client node is sending such data structure: an array of one string and one hashtable (with one key/value pair). |
|||
__WSA_WOULDBLOCK := 10035 ; http://www.sockets.com/err_lst1.htm#WSAECONNRESET |
|||
__WSA_CONNRESET := 10054 ; http://www.sockets.com/err_lst1.htm#WSAECONNRESET |
|||
===Server=== |
|||
return 1 |
|||
<syntaxhighlight lang="factor">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</syntaxhighlight> |
|||
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. |
|||
; WS2 Startup - for internal use only |
|||
; Initializes the Winsock 2 Adapter |
|||
__WSA_Startup() |
|||
{ |
|||
Global WSAData, __WSA_ErrMsg, __WSA_WSVersion |
|||
===Client=== |
|||
; It's a good idea, to have a __WSA_ErrMsg Container, so any Error Msgs |
|||
<syntaxhighlight lang="factor">USING: concurrency.distributed io.sockets ; |
|||
; may be catched by the script. |
|||
QUALIFIED: concurrency.messaging |
|||
__WSA_ErrMsg := "" |
|||
{ "Hello Remote Factor!" H{ { "key1" "value1" } } } |
|||
"127.0.0.1" 9000 <inet4> "logger" <remote-thread> concurrency.messaging:send</syntaxhighlight> |
|||
How to Run: |
|||
; Generate Structure for the lpWSAData |
|||
; as stated on http://msdn.microsoft.com/en-us/library/ms742213.aspx |
|||
; More on WSADATA (structure) to be found here: |
|||
; http://msdn.microsoft.com/en-us/library/ms741563(VS.85).aspx |
|||
VarSetCapacity(WSAData, 32) |
|||
result := DllCall("Ws2_32\WSAStartup", "UShort", __WSA_WSVersion, "UInt", &WSAData) |
|||
- Copy/Paste the server code in an instance of Factor Listener |
|||
if (ErrorLevel) |
|||
__WSA_ErrMsg .= "Ws2_32\WSAStartup could not be called due to error " ErrorLevel "`n" |
|||
. "Winsock 2.0 or higher is required.`n" |
|||
if (result!=0) |
|||
__WSA_ErrMsg .= "Ws2_32\WSAStartup " __WSA_GetLastError() |
|||
- Copy/Paste the client code in another instance of Factor Listener. |
|||
If (StrLen(__WSA_ErrMsg)>0) |
|||
Return -1 |
|||
Else |
|||
Return 1 |
|||
} |
|||
The server node should prettyprint the data structure send by the client: { "Hello Remote Factor!" H{ { "key1" "value1" } } } |
|||
; WS2 Socket Descriptor - for internal use only |
|||
; Sets type and neccessary structures for a successfull connection |
|||
__WSA_Socket() |
|||
{ |
|||
Global __WSA_ErrMsg, __WSA_SocketProtocol, __WSA_SocketType, __WSA_SocketAF |
|||
=={{header|Go}}== |
|||
; Supposed to return a descriptor referencing the new socket |
|||
===Standard library net/rpc=== |
|||
; http://msdn.microsoft.com/en-us/library/ms742212(VS.85).aspx |
|||
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. |
|||
__WSA_Socket := DllCall("Ws2_32\socket" |
|||
, "Int", __WSA_SocketAF |
|||
, "Int", __WSA_SocketType |
|||
, "Int", __WSA_SocketProtocol) |
|||
if (socket = -1) |
|||
__WSA_ErrMsg .= "Ws2_32\socket " __WSA_GetLastError() |
|||
'''Server:''' |
|||
If (StrLen(__WSA_ErrMsg)>0) |
|||
<syntaxhighlight lang="go">package main |
|||
Return -1 |
|||
Else |
|||
Return __WSA_Socket |
|||
import ( |
|||
} |
|||
"errors" |
|||
"log" |
|||
"net" |
|||
"net/http" |
|||
"net/rpc" |
|||
) |
|||
type TaxComputer float64 |
|||
; WS2 Connection call - for internal use only |
|||
; Establishes a connection to a foreign IP at the specified port |
|||
__WSA_Connect() |
|||
{ |
|||
Global __WSA_ErrMsg, __WSA_Port, __WSA_Socket, __WSA_InetAddr, __WSA_SocketAF |
|||
func (taxRate TaxComputer) Tax(x float64, r *float64) error { |
|||
; Generate socketaddr structure for the connect() |
|||
if x < 0 { |
|||
; http://msdn.microsoft.com/en-us/library/ms740496(VS.85).aspx |
|||
return errors.New("Negative values not allowed") |
|||
__WSA_SockAddrNameLen := 16 |
|||
} |
|||
VarSetCapacity(__WSA_SockAddr, __WSA_SockAddrNameLen) |
|||
*r = x * float64(taxRate) |
|||
NumPut(__WSA_SocketAF, __WSA_SockAddr, 0, "UShort") |
|||
return nil |
|||
NumPut(__WSA_Port, __WSA_SockAddr, 2, "UShort") |
|||
} |
|||
NumPut(__WSA_InetAddr, __WSA_SockAddr, 4) |
|||
func main() { |
|||
; The connect function establishes a connection to a specified socket. |
|||
c := TaxComputer(.05) |
|||
; http://msdn.microsoft.com/en-us/library/ms737625(VS.85).aspx |
|||
rpc.Register(c) |
|||
result := DllCall("Ws2_32\connect" |
|||
rpc.HandleHTTP() |
|||
, "UInt", __WSA_Socket |
|||
listener, err := net.Listen("tcp", ":1234") |
|||
, "UInt", &__WSA_SockAddr |
|||
if err != nil { |
|||
, "Int" , __WSA_SockAddrNameLen) |
|||
log.Fatal(err) |
|||
} |
|||
__WSA_ErrMsg .= "Ws2_32\connect " __WSA_GetLastError() |
|||
http.Serve(listener, nil) |
|||
}</syntaxhighlight> |
|||
'''Client:''' |
|||
<syntaxhighlight lang="go">package main |
|||
import ( |
|||
If (StrLen(__WSA_ErrMsg)>0) |
|||
"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 |
|||
This code based originally upon an example by DarviK |
|||
err = client.Call("TaxComputer.Tax", amount, &tax) |
|||
http://www.autohotkey.com/forum/topic8871.html |
|||
if err != nil { |
|||
and on the modifcations by Tasman |
|||
log.Fatal(err) |
|||
http://www.autohotkey.com/forum/viewtopic.php?t=9937 |
|||
} |
|||
*/ |
|||
fmt.Printf("Tax on %.2f: %.2f\n", amount, tax) |
|||
; Resolves canonical domainname to IP |
|||
}</syntaxhighlight> |
|||
__WSA_GetHostByName(url) |
|||
{{out | Client output}} |
|||
{ |
|||
<pre> |
|||
Global __WSA_ErrMsg |
|||
Tax on 3.00: 0.15 |
|||
; gethostbyname returns information about a domainname into a Hostent Structure |
|||
</pre> |
|||
; http://msdn.microsoft.com/en-us/library/ms738524(VS.85).aspx |
|||
===gRPC=== |
|||
IP := "" |
|||
See http://www.grpc.io/ |
|||
if ((PtrHostent:=DllCall("Ws2_32\gethostbyname","str",url)) != 0) { |
|||
Loop, 1 ; 3 is max No of retrieved addresses |
|||
If (PtrTmpIP := NumGet(NumGet(PtrHostent+12)+(offset:=(A_Index-1)*4),offset)) { |
|||
IP := (IP) ? IP "|" : "" |
|||
Loop, 4 ; Read our IP address |
|||
IP .= NumGet(PtrTmpIP+offset,(A_Index-1 ),"UChar") "." |
|||
IP := SubStr(IP,1,-1) |
|||
} else ; No more IPs left |
|||
Break |
|||
result := IP |
|||
} else { |
|||
__WSA_ErrMsg .= "Ws2_32\gethostbyname failed`n " |
|||
result := -1 |
|||
} |
|||
return result |
|||
} |
|||
The default serialization for gRPC is "protocol buffers." gRPC uses a .proto file to define an interface for the client and server. The .proto has its own syntax, independent of client and server implementation languages. Server and client programs here are Go however. |
|||
; Return the last Error with a lil bit o' text if neccessary |
|||
; Note: the txt variable is set to 0 when checking for received content |
|||
__WSA_GetLastError(txt=1) |
|||
{ |
|||
Err := DllCall("Ws2_32\WSAGetLastError") |
|||
ExtraInfo := __WSA_ErrLookUp(RegExReplace(Err,"[^\d]")) |
|||
If ((InStr(ExtraInfo,"Sorry, no")) || (txt!=1)) |
|||
ExtraInfo := "" |
|||
Return ( txt ? "indicated Winsock error " : "") |
|||
. Err |
|||
. ( txt ? "`n" ExtraInfo : "") |
|||
} |
|||
'''.proto:''' |
|||
; WS2 AsyncSelect - for internal use only |
|||
<syntaxhighlight lang="proto">syntax = "proto3"; |
|||
; Sets up an Notification Handler for Receiving Messages |
|||
; Expected Parameters: Socket from Initialisation |
|||
; Optional: NotificationMsg - default 0x5000 |
|||
; WSA_DataReiceiver - an different Name to standard |
|||
; wm_* processor function. |
|||
; default __WSA_ReceiveData |
|||
; Returns -1 on Error, 0 on success |
|||
__WSA_AsyncSelect(__WSA_Socket, UDF, __WSA_NotificationMsg=0x5000 |
|||
,__WSA_DataReceiver="__WSA_recv") |
|||
{ |
|||
Global |
|||
service TaxComputer { |
|||
__WSA_UDF := UDF |
|||
rpc Tax(Amount) returns (Amount) {} |
|||
} |
|||
message Amount { |
|||
OnMessage(__WSA_NotificationMsg, __WSA_DataReceiver) |
|||
int32 cents = 1; |
|||
; The WSAAsyncSelect function requests Windows message-based notification |
|||
}</syntaxhighlight> |
|||
; of network events for a socket. |
|||
'''Server:''' |
|||
; http://msdn.microsoft.com/en-us/library/ms741540(VS.85).aspx |
|||
<syntaxhighlight lang="go">package main |
|||
Result := DllCall("Ws2_32\WSAAsyncSelect" |
|||
, "UInt", __WSA_Socket |
|||
, "UInt", __WSA_GetThisScriptHandle() |
|||
, "UInt", __WSA_NotificationMsg |
|||
, "Int", __WSA_lEvent) |
|||
if (Result) { |
|||
__WSA_ErrMsg .= "Ws2_32\WSAAsyncSelect() " . __WSA_GetLastError() |
|||
Result := -1 |
|||
} |
|||
Return Result |
|||
} |
|||
import ( |
|||
; WS2 Receive - for internal use only |
|||
"errors" |
|||
; Triggers upon Notification Handler when Receiving Messages |
|||
"net" |
|||
__WSA_recv(wParam, lParam) |
|||
{ |
|||
Global __WSA_UDF, __WSA_ErrMsg |
|||
; __WSA_UDF containes the name of the UserDefinedFunction to call when the event |
|||
; has been triggered and text may be processed (allthough the reveived text might |
|||
; be inclomplete, especially when receiving large chunks of data, like in eMail- |
|||
; attachments or sometimes in IRC). The UDF needs to accept two parameter: socket |
|||
; and the received buffer |
|||
__WSA_Socket := wParam |
|||
__WSA_BufferSize = 4096 |
|||
Loop |
|||
{ |
|||
VarSetCapacity(__WSA_Buffer, __WSA_BufferSize, 0) |
|||
__WSA_BufferLength := DllCall("Ws2_32\recv" |
|||
, "UInt", __WSA_Socket |
|||
, "Str", __WSA_Buffer |
|||
, "Int", __WSA_BufferSize |
|||
, "Int", 0 ) |
|||
if (__WSA_BufferLength = 0) |
|||
break |
|||
if (__WSA_BufferLength = -1) |
|||
{ |
|||
__WSA_Err := __WSA_GetLastError(0) |
|||
; __WSA_WOULDBLOCK (from http://www.sockets.com/) |
|||
; The socket is marked as non-blocking (non-blocking operation mode), and |
|||
; the requested operation is not complete at this time. The operation is |
|||
; underway, but as yet incomplete. |
|||
if (__WSA_Err = __WSA_WOULDBLOCK ) |
|||
return 1 |
|||
"golang.org/x/net/context" |
|||
; __WSA_CONNRESET: (from http://www.sockets.com/) |
|||
"google.golang.org/grpc" |
|||
; A connection was forcibly closed by a peer. This normally results from |
|||
"google.golang.org/grpc/grpclog" |
|||
; a loss of the connection on the remote socket due to a timeout or a reboot. |
|||
if (__WSA_Err != __WSA_CONNRESET) |
|||
__WSA_ErrMsg .= "Ws2_32\recv indicated Winsock error " __WSA_Err "`n" |
|||
break |
|||
} |
|||
"taxcomputer" |
|||
if (StrLen(__WSA_UDF)!=0) ; If set, call UserDefinedFunction and pass Buffer to it |
|||
) |
|||
%__WSA_UDF%(__WSA_Socket,__WSA_Buffer) |
|||
} |
|||
return 1 |
|||
} |
|||
type taxServer struct { |
|||
rate float64 |
|||
} |
|||
func (s *taxServer) Tax(ctx context.Context, |
|||
amt *taxcomputer.Amount) (*taxcomputer.Amount, error) { |
|||
if amt.Cents < 0 { |
|||
return nil, errors.New("Negative amounts not allowed") |
|||
} |
|||
return &taxcomputer.Amount{int32(float64(amt.Cents)*s.rate + .5)}, nil |
|||
} |
|||
func main() { |
|||
; WSA Send - for internal use only |
|||
listener, err := net.Listen("tcp", ":1234") |
|||
; Users are encouraged to use the WS2_SendData() Function |
|||
if err != nil { |
|||
__WSA_send(__WSA_Socket, __WSA_Data) |
|||
grpclog.Fatalf(err.Error()) |
|||
{ |
|||
} |
|||
Global __WSA_ErrMsg |
|||
grpcServer := grpc.NewServer() |
|||
taxcomputer.RegisterTaxComputerServer(grpcServer, &taxServer{.05}) |
|||
grpcServer.Serve(listener) |
|||
}</syntaxhighlight> |
|||
'''Client:''' |
|||
<syntaxhighlight lang="go">package main |
|||
import ( |
|||
Result := DllCall("Ws2_32\send" |
|||
"fmt" |
|||
, "UInt", __WSA_Socket |
|||
, "Str", __WSA_Data |
|||
, "Int", StrLen(__WSA_Data) |
|||
, "Int", 0) |
|||
If (Result = -1) |
|||
__WSA_ErrMsg .= "Ws2_32\send " __WSA_GetLastError() |
|||
Return Result |
|||
} |
|||
"golang.org/x/net/context" |
|||
; Closes Open Socket - for internal use only |
|||
"google.golang.org/grpc" |
|||
; Returns 0 on success |
|||
"google.golang.org/grpc/grpclog" |
|||
__WSA_CloseSocket(__WSA_Socket) |
|||
{ |
|||
Global __WSA_ErrMsg |
|||
"taxcomputer" |
|||
Result := DllCall("Ws2_32\closesocket" |
|||
) |
|||
, "UInt", __WSA_Socket) |
|||
If (Result != 0) |
|||
__WSA_ErrMsg .= "Ws2_32\closesocket " __WSA_GetLastError() |
|||
func main() { |
|||
Return result |
|||
conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure()) |
|||
} |
|||
if err != nil { |
|||
grpclog.Fatalf(err.Error()) |
|||
} |
|||
defer conn.Close() |
|||
client := taxcomputer.NewTaxComputerClient(conn) |
|||
amt := &taxcomputer.Amount{300} |
|||
tax, err := client.Tax(context.Background(), amt) |
|||
if err != nil { |
|||
grpclog.Fatalf(err.Error()) |
|||
} |
|||
fmt.Println("Tax on", amt.Cents, "cents is", tax.Cents, "cents") |
|||
}</syntaxhighlight> |
|||
{{out | Client output}} |
|||
<pre> |
|||
Tax on 300 cents is 15 cents |
|||
</pre> |
|||
===Apache Thrift=== |
|||
; GetThisScriptHandle - for internal use only |
|||
See https://thrift.apache.org/ |
|||
; Returns the handle of the executing script |
|||
__WSA_GetThisScriptHandle() |
|||
{ |
|||
'''.thrift''' |
|||
HiddenWindowsSave := A_DetectHiddenWindows |
|||
Like gRPC, Thrift requires a language independent interface definition file: |
|||
DetectHiddenWindows On |
|||
<syntaxhighlight lang="thrift">service TaxService { |
|||
ScriptMainWindowId := WinExist("ahk_class AutoHotkey ahk_pid " DllCall("GetCurrentProcessId")) |
|||
i32 tax(1: i32 amt) |
|||
DetectHiddenWindows %HiddenWindowsSave% |
|||
}</syntaxhighlight> |
|||
'''Server:''' |
|||
<syntaxhighlight lang="go">package main |
|||
import ( |
|||
Return ScriptMainWindowId |
|||
"errors" |
|||
} |
|||
"log" |
|||
"git.apache.org/thrift.git/lib/go/thrift" |
|||
; Lookup Winsock ErrCode - for internal use only |
|||
; This list is form http://www.sockets.com |
|||
__WSA_ErrLookUp(sNumber) { |
|||
WSA_ErrorList = |
|||
(LTrim Join`n |
|||
10004 - Interrupted system call |
|||
10009 - Bad file number |
|||
10013 - Permission denied |
|||
10014 - Bad address |
|||
10022 - Invalid argument |
|||
10024 - Too many open files |
|||
10035 - Operation would block |
|||
10036 - Operation now in progress |
|||
10037 - Operation already in progress |
|||
10038 - Socket operation on non-socket |
|||
10039 - D estination address required |
|||
10040 - Message too long |
|||
10041 - Protocol wrong type for socket |
|||
10042 - Bad protocol option |
|||
10043 - Protocol not supported |
|||
10044 - Socket type not supported |
|||
10045 - Operation not supported on socket |
|||
10046 - Protocol family not supported |
|||
10047 - Address family not supported by protocol family |
|||
10048 - Address already in use |
|||
10049 - Can't assign requested address |
|||
10050 - Network is down |
|||
10051 - Network is unreachable |
|||
10052 - Net dropped connection or reset |
|||
10053 - Software caused connection abort |
|||
10054 - Connection reset by peer |
|||
10055 - No buffer space available |
|||
10056 - Socket is already connected |
|||
10057 - Socket is not connected |
|||
10058 - Can't send after socket shutdown |
|||
10059 - Too many references, can't splice |
|||
10060 - Connection timed out |
|||
10061 - Connection refused |
|||
10062 - Too many levels of symbolic links |
|||
10063 - File name too long |
|||
10064 - Host is down |
|||
10065 - No Route to Host |
|||
10066 - Directory not empty |
|||
10067 - Too many processes |
|||
10068 - Too many users |
|||
10069 - Disc Quota Exceeded |
|||
10070 - Stale NFS file handle |
|||
10091 - Network SubSystem is unavailable |
|||
10092 - WINSOCK DLL Version out of range |
|||
10093 - Successful WSASTARTUP not yet performed |
|||
10071 - Too many levels of remote in path |
|||
11001 - Host not found |
|||
11002 - Non-Authoritative Host not found |
|||
11003 - Non-Recoverable errors: FORMERR, REFUSED, NOTIMP |
|||
11004 - Valid name, no data record of requested type |
|||
11004 - No address, look for MX record |
|||
) |
|||
ExNr := 0, ExErr := "Sorry, but no definition available." |
|||
Loop,Parse,WSA_ErrorList,`n |
|||
{ |
|||
RegExMatch(A_LoopField,"(?P<Nr>\d+) - (?P<Err>.*)",Ex) |
|||
if (sNumber = ExNr) |
|||
break |
|||
} |
|||
Return ExNr " means " ExErr "`n" |
|||
} |
|||
; WinINet InternetCrackURL - for internal use only |
|||
; v 0.1 / (w) 25.07.2008 by derRaphael / zLib-Style release |
|||
; This routine was originally posted here: |
|||
; http://www.autohotkey.com/forum/viewtopic.php?p=209957#209957 |
|||
__WinINet_InternetCrackURL(lpszUrl,arrayName="URL") |
|||
{ |
|||
local hModule, offset_name_length |
|||
hModule := DllCall("LoadLibrary", "Str", "WinINet.Dll") |
|||
"gen-go/tax" |
|||
; SetUpStructures for URL_COMPONENTS / needed for InternetCrackURL |
|||
) |
|||
; http://msdn.microsoft.com/en-us/library/aa385420(VS.85).aspx |
|||
offset_name_length:= "4-lpszScheme-255|16-lpszHostName-1024|28-lpszUserName-1024|" |
|||
. "36-lpszPassword-1024|44-lpszUrlPath-1024|52-lpszExtrainfo-1024" |
|||
type taxHandler float64 |
|||
VarSetCapacity(URL_COMPONENTS,60,0) |
|||
; Struc Size ; Scheme Size ; Max Port Number |
|||
NumPut(60,URL_COMPONENTS,0), NumPut(255,URL_COMPONENTS,12), NumPut(0xffff,URL_COMPONENTS,24) |
|||
Loop,Parse,offset_name_length,| |
|||
{ |
|||
RegExMatch(A_LoopField,"(?P<Offset>\d+)-(?P<Name>[a-zA-Z]+)-(?P<Size>\d+)",iCU_) |
|||
VarSetCapacity(%iCU_Name%,iCU_Size,0) |
|||
NumPut(&%iCU_Name%,URL_COMPONENTS,iCU_Offset) |
|||
NumPut(iCU_Size,URL_COMPONENTS,iCU_Offset+4) |
|||
} |
|||
func (r taxHandler) Tax(amt int32) (int32, error) { |
|||
; Split the given URL; extract scheme, user, pass, authotity (host), port, path, and query (extrainfo) |
|||
if amt < 0 { |
|||
; http://msdn.microsoft.com/en-us/library/aa384376(VS.85).aspx |
|||
return 0, errors.New("Negative amounts not allowed") |
|||
DllCall("WinINet\InternetCrackUrlA","Str",lpszUrl,"uInt",StrLen(lpszUrl),"uInt",0,"uInt",&URL_COMPONENTS) |
|||
} |
|||
; Update variables to retrieve results |
|||
return int32(float64(amt)*float64(r) + .5), nil |
|||
Loop,Parse,offset_name_length,| |
|||
{ |
|||
RegExMatch(A_LoopField,"-(?P<Name>[a-zA-Z]+)-",iCU_) |
|||
VarSetCapacity(%iCU_Name%,-1) |
|||
%arrayName%_%iCU_Name% := % %iCU_Name% |
|||
} |
|||
%arrayName%_nPort:=NumGet(URL_COMPONENTS,24,"uInt") |
|||
DllCall("FreeLibrary", "UInt", hModule) ; unload the library |
|||
} |
} |
||
#Persistent |
|||
#SingleInstance, force |
|||
#Include WinSock2.ahk ; derRaphaels WinSock Rewrite |
|||
SetBatchLines, -1 |
|||
SetWorkingDir, %A_ScriptDir% |
|||
Gui, Add, Button, gB1, Button number One! |
|||
Gui, Add, Edit, vE1, Type in me! |
|||
Gui, Add, Button, gB2, Press me after you're done typing! |
|||
Gui, Add, DropDownList, vDDL1 gDDL1, Select something!||One|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten |
|||
Gui, Add, Button, gB3, Click me! |
|||
func main() { |
|||
Server := "irc.freenode.net" |
|||
transport, err := thrift.NewTServerSocket("localhost:3141") |
|||
Port := "6667" |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
transFac := thrift.NewTTransportFactory() |
|||
protoFac := thrift.NewTCompactProtocolFactory() |
|||
proc := tax.NewTaxServiceProcessor(taxHandler(.05)) |
|||
s := thrift.NewTSimpleServer4(proc, transport, transFac, protoFac) |
|||
if err := s.Serve(); err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
}</syntaxhighlight> |
|||
'''Client:''' |
|||
<syntaxhighlight lang="go">package main |
|||
import ( |
|||
Channel := "#UIHjbfilgbafLIEfbAIJCICBGncaiJBHiwehFIUHEIbnjKCINJEhiUAHEJcKLACui" ; Choose something long and random - a channel sure to be deserted. |
|||
"fmt" |
|||
"log" |
|||
"git.apache.org/thrift.git/lib/go/thrift" |
|||
Nick := "someothernick" ; Register this! You can drop it later if you want, but register it! |
|||
Pass := "someotherpass" ; It can be any nick and any pass. |
|||
"gen-go/tax" |
|||
Name := "distributed program" |
|||
) |
|||
func main() { |
|||
BotCmd := "~" |
|||
transport, err := thrift.NewTSocket("localhost:3141") |
|||
OtherBotCmd := "!" |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
if err := transport.Open(); err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
protoFac := thrift.NewTCompactProtocolFactory() |
|||
client := tax.NewTaxServiceClientFactory(transport, protoFac) |
|||
amt := int32(300) |
|||
t, err := client.Tax(amt) |
|||
if err != nil { |
|||
log.Print(err) |
|||
} else { |
|||
fmt.Println("tax on", amt, "is", t) |
|||
} |
|||
transport.Close() |
|||
}</syntaxhighlight> |
|||
{{out | Client output}} |
|||
<pre> |
|||
tax on 300 is 15 |
|||
</pre> |
|||
=={{header|Haskell}}== |
|||
OnExit, CleanUp |
|||
See: |
|||
WS2_CleanUp() |
|||
If (!Socket := WS2_Connect(Connection := Server . ":" . Port)) |
|||
{ |
|||
MsgBox, 16, Error!, An error occured wilist connecting to %Connection% |
|||
} |
|||
* http://www.haskell.org/haskellwiki/HaXR#Server |
|||
; Set OnMessage function |
|||
* http://www.haskell.org/haskellwiki/HaXR#Client |
|||
WS2_AsyncSelect(Socket, "DataProcess") |
|||
; Send AUTH info to the server |
|||
; USER A-trivial-nickname a-domain-like-google.com some-trivial-stuff :your-real-name |
|||
WS2_SendData(Socket, "USER " . Nick . " google.com AHKBOT :" . Name . "`n") ; All data send to the server must be |
|||
; followed by a newline. |
|||
; PASS A-trivial-pass |
|||
WS2_SendData(Socket, "PASS " . Pass . "`n") |
|||
; NICK A-trivial-nick |
|||
WS2_SendData(Socket, "NICK " . Nick . "`n") |
|||
; Join channel |
|||
; JOIN A-trivial-channel |
|||
WS2_SendData(Socket, "JOIN " . Channel . "`n") |
|||
Gui, Show |
|||
Return |
|||
Check license: |
|||
DataProcess(Socket, Data) ; OnMessage function |
|||
http://www.haskell.org/haskellwiki/HaskellWiki:Copyrights |
|||
{ |
|||
global Server,Port,Channel,Nick,Pass,Name,BotCMD |
|||
StringSplit, Param, Data, %A_Space% |
|||
Name := SubStr(Data, 2, InStr(Data, "!")-2) |
|||
StringReplace, Command, Param5, % Chr(10),, All |
|||
StringReplace, Command, Command, % Chr(13),, All |
|||
If (Param1 == "PING") |
|||
WS2_SendData(Socket, "PONG " . Param2 . "`n") |
|||
Else If (RegExMatch(Data, ":\" . BotCMD . " ")) |
|||
{ |
|||
If (Command == "B1" |
|||
MsgBox, The other person pressed Button Number One |
|||
Else If (Command == "B2E1") |
|||
{ |
|||
MsgBox, The other person typed in Edit One then pressed Button Two |
|||
Loop |
|||
{ |
|||
If (A_Index <= 6) |
|||
continue |
|||
If Param%A_Index% |
|||
out := out . " " . Param%A_Index% |
|||
Else |
|||
break |
|||
} |
|||
MsgBox, The other person typed:%out% |
|||
} |
|||
Else If (Command == "DDL1") |
|||
MsgBox, The other person selected %Param7% in the drop-down-list. |
|||
Else If (Command == "B3") |
|||
MsgBox, The other person clicked Button Three. |
|||
} |
|||
return |
|||
} |
|||
B1: |
|||
WS2_SendData(Socket, "PRIVMSG " . Channel . " :" . OtherBotCmd . " B1" |
|||
return |
|||
B2: |
|||
Gui, Submit, NoHide |
|||
WS2_SendData(Socket, "PRIVMSG " . Channel . " :" . OtherBotCmd . " B2E1 " . E1 . " | " . B2 |
|||
return |
|||
DDL1: |
|||
Gui, Submit, NoHide |
|||
WS2_SendData(Socket, "PRIVMSG " . Channel . " :" . OtherBotCmd . " DDL1 " . DDL1 |
|||
return |
|||
B3: |
|||
WS2_SendData(Socket, "PRIVMSG " . Channel . " :" . OtherBotCmd . " B3" |
|||
return |
|||
CleanUp: |
|||
GuiClose: |
|||
WS2_CleanUp() |
|||
ExitApp</lang> |
|||
={{header| |
=={{header|JavaScript}}== |
||
==Socket== |
|||
{{works with|Tango}} |
|||
=== Server === |
|||
<lang d>module distributedserver ; |
|||
import tango.net.ServerSocket, tango.text.convert.Integer, |
|||
tango.text.Util, tango.io.Stdout ; |
|||
{{works with|node.js}} |
|||
void main() { |
|||
auto Ip = new InternetAddress("localhost", 12345) ; |
|||
auto server = new ServerSocket(Ip) ; |
|||
auto socket = server.accept ; |
|||
auto buffer = new char[socket.bufferSize] ; |
|||
===Server=== |
|||
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 ; |
|||
} |
|||
<syntaxhighlight lang="javascript">var net = require('net') |
|||
if(socket) socket.close ; |
|||
}</lang> |
|||
=== Client === |
|||
<lang d>module distributedclient ; |
|||
import tango.net.SocketConduit, tango.net.InternetAddress, |
|||
tango.text.Util, tango.io.Stdout ; |
|||
var server = net.createServer(function (c){ |
|||
void main(char[][] args) { |
|||
c.write('hello\r\n') |
|||
c.pipe(c) // echo messages back |
|||
}) |
|||
server.listen(3000, 'localhost') |
|||
if(args.length> 1) { |
|||
</syntaxhighlight> |
|||
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> |
|||
={{header|E}}= |
|||
'''Protocol:''' Pluribus |
|||
===Client=== |
|||
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. |
|||
<syntaxhighlight lang="javascript">var net = require('net') |
|||
conn = net.createConnection(3000, '192.168.1.x') |
|||
=== Server === |
|||
conn.on('connect', function(){ |
|||
(The protocol is symmetric; this program is the server only in that it is the one which is started first and exports an object.) |
|||
console.log('connected') |
|||
conn.write('test') |
|||
}) |
|||
conn.on('data', function(msg){ |
|||
<lang e> def storage := [].diverge() |
|||
console.log(msg.toString()) |
|||
})</syntaxhighlight> |
|||
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> |
|||
=={{header|Julia}}== |
|||
This will print the URL of the service and run it until aborted. |
|||
Julia was designed with distributed conmputing. in particular cluster computing, as a primary use target. |
|||
If a group of CPUs, including multiple cores on a single machine or a cluster running with paswordless ssh login, is used, |
|||
the following can be set up as an example: |
|||
<syntaxhighlight lang="julia"># From Julia 1.0's online docs. File countheads.jl available to all machines: |
|||
function count_heads(n) |
|||
=== Client === |
|||
c::Int = 0 |
|||
for i = 1:n |
|||
c += rand(Bool) |
|||
end |
|||
c |
|||
end</syntaxhighlight> |
|||
We then run the following on the primary client: |
|||
<syntaxhighlight lang="julia"> |
|||
using Distributed |
|||
@everywhere include_string(Main, $(read("count_heads.jl", String)), "count_heads.jl") |
|||
a = @spawn count_heads(100000000) # runs on an available processor |
|||
The URL provided by the server is given as the argument to this program. |
|||
b = @spawn count_heads(100000000) # runs on another available processor |
|||
println(fetch(a)+fetch(b)) # total heads of 200 million coin flips, half on each CPU |
|||
<lang e> def [uri] := interp.getArgs() |
|||
</syntaxhighlight> {{output}} <pre> |
|||
introducer.onTheAir() |
|||
100001564 |
|||
def sturdyRef := <captp>.sturdyFromURI(uri) |
|||
</pre> |
|||
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> |
|||
={{header| |
=={{header|LFE}}== |
||
The protocol is erlang's own |
|||
=== Server === |
|||
srv.erl |
|||
The protocol used is the one native to Erlang (and thus native to LFE, Lisp Flavored Erlang). |
|||
<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> |
|||
These examples are done completely in the LFE REPL. |
|||
=== Client === |
|||
client.erl |
|||
===Server=== |
|||
<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("conencting 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> |
|||
In one terminal window, start up the REPL |
|||
running it (*comes later) |
|||
|erlc srv.erl |
|||
<syntaxhighlight lang="bash"> |
|||
|erl -run srv start -noshell |
|||
$ ./bin/lfe |
|||
srv@agneyam ready |
|||
Erlang/OTP 17 [erts-6.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] |
|||
*-> hi from client@agneyam |
|||
LFE Shell V6.2 (abort with ^G) |
|||
> |
|||
</syntaxhighlight> |
|||
And then enter the following code |
|||
<syntaxhighlight lang="lisp"> |
|||
> (defun get-server-name () |
|||
(list_to_atom (++ "exampleserver@" (element 2 (inet:gethostname))))) |
|||
> (defun start () |
|||
|erlc client.erl |
|||
(net_kernel:start `(,(get-server-name) shortnames)) |
|||
|erl -run client start -run init stop -noshell -server srv@agneyam |
|||
(erlang:set_cookie (node) 'rosettaexample) |
|||
conencting to "srv@agneyam" |
|||
(let ((pid (spawn #'listen/0))) |
|||
Received hi |
|||
(register 'serverref pid) |
|||
(io:format "~p ready~n" (list (node pid))) |
|||
'ok)) |
|||
> (defun listen () |
|||
(receive |
|||
(`#(echo ,pid ,data) |
|||
(io:format "Got ~p from ~p~n" (list data (node pid))) |
|||
(! pid `#(hello ,data)) |
|||
(listen)) |
|||
(x |
|||
(io:format "Unexpected pattern: ~p~n" `(,x))))) |
|||
</syntaxhighlight> |
|||
===Client=== |
|||
In another terminal window, start up another LFE REPL and ender the following code: |
|||
<syntaxhighlight lang="lisp"> |
|||
> (defun get-server-name () |
|||
(list_to_atom (++ "exampleserver@" (element 2 (inet:gethostname))))) |
|||
> (defun send (data) |
|||
(net_kernel:start '(exampleclient shortnames)) |
|||
(erlang:set_cookie (node) 'rosettaexample) |
|||
(io:format "connecting to ~p~n" `(,(get-server-name))) |
|||
(! `#(serverref ,(get-server-name)) `#(echo ,(self) ,data)) |
|||
(receive |
|||
(`#(hello ,data) |
|||
(io:format "Received ~p~n" `(,data))) |
|||
(x |
|||
(io:format "Unexpected pattern: ~p~n" (list x)))) |
|||
'ok) |
|||
</syntaxhighlight> |
|||
To use this code, simply start the server in the server terminal: |
|||
<syntaxhighlight lang="lisp"> |
|||
> (start) |
|||
exampleserver@yourhostname ready |
|||
ok |
|||
(exampleserver@yourhostname)> |
|||
</syntaxhighlight> |
|||
Send some messages from the client terminal: |
|||
<syntaxhighlight lang="lisp"> |
|||
> (send "hi there") |
|||
connecting to exampleserver@yourhostname |
|||
Received "hi there" |
|||
ok |
|||
(exampleclient@yourhostname)> (send 42) |
|||
connecting to exampleserver@yourhostname |
|||
Received 42 |
|||
ok |
|||
(exampleclient@yourhostname)> (send #(key value)) |
|||
connecting to exampleserver@yourhostname |
|||
Received {key,value} |
|||
ok |
|||
(exampleclient@yourhostname)> |
|||
</syntaxhighlight> |
|||
And check out the results back in the server terminal window: |
|||
<syntaxhighlight lang="lisp"> |
|||
Got "hi there" from exampleclient@yourhostname |
|||
Got 42 from exampleclient@yourhostname |
|||
Got {key,value} from exampleclient@yourhostname |
|||
</syntaxhighlight> |
|||
=={{header|Mathematica}} / {{header|Wolfram Language}}== |
|||
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. |
|||
<syntaxhighlight lang="mathematica">LaunchKernels[2]; |
|||
ParallelEvaluate[RandomReal[]] |
|||
</syntaxhighlight> |
|||
=={{header|Nim}}== |
|||
{{libheader|nanomsg}} |
|||
<syntaxhighlight lang="nim">import os, nanomsg |
|||
proc sendMsg(s: cint, msg: string) = |
|||
echo "SENDING \"",msg,"\"" |
|||
let bytes = s.send(msg.cstring, msg.len + 1, 0) |
|||
assert bytes == msg.len + 1 |
|||
proc recvMsg(s: cint) = |
|||
var buf: cstring |
|||
let bytes = s.recv(addr buf, MSG, 0) |
|||
if bytes > 0: |
|||
echo "RECEIVED \"",buf,"\"" |
|||
discard freemsg buf |
|||
proc sendRecv(s: cint, msg: string) = |
|||
var to: cint = 100 |
|||
discard s.setSockOpt(SOL_SOCKET, RCVTIMEO, addr to, sizeof to) |
|||
while true: |
|||
s.recvMsg |
|||
sleep 1000 |
|||
s.sendMsg msg |
|||
proc node0(url: string) = |
|||
var s = socket(AF_SP, nanomsg.PAIR) |
|||
assert s >= 0 |
|||
let res = s.bindd url |
|||
assert res >= 0 |
|||
s.sendRecv "node0" |
|||
discard s.shutdown 0 |
|||
proc node1(url: string) = |
|||
var s = socket(AF_SP, nanomsg.PAIR) |
|||
assert s >= 0 |
|||
let res = s.connect url |
|||
assert res >= 0 |
|||
s.sendRecv "node1" |
|||
discard s.shutdown 0 |
|||
if paramStr(1) == "node0": |
|||
node0 paramStr(2) |
|||
elif paramStr(1) == "node1": |
|||
node1 paramStr(2)</syntaxhighlight> |
|||
Usage: |
|||
<pre>./pair node0 tcp://127.0.0.1:25000 |
|||
./pair node1 tcp://127.0.0.1:25000</pre> |
|||
={{header|Objective-C}}= |
=={{header|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. |
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. |
||
Line 1,408: | Line 945: | ||
<tt>ActionObjectProtocol.h</tt> |
<tt>ActionObjectProtocol.h</tt> |
||
< |
<syntaxhighlight lang="objc">#import <Foundation/Foundation.h> |
||
// our protocol allows "sending" "strings", but we can implement |
// our protocol allows "sending" "strings", but we can implement |
||
// everything we could for a "local" object |
// everything we could for a "local" object |
||
@protocol ActionObjectProtocol |
@protocol ActionObjectProtocol |
||
- (NSString *)sendMessage: (NSString *)msg; |
- (NSString *)sendMessage: (NSString *)msg; |
||
@end</ |
@end</syntaxhighlight> |
||
<tt>ActionObject.h</tt> |
<tt>ActionObject.h</tt> |
||
< |
<syntaxhighlight lang="objc">#import <Foundation/Foundation.h> |
||
#import "ActionObjectProtocol.h" |
#import "ActionObjectProtocol.h" |
||
@interface ActionObject : NSObject <ActionObjectProtocol> |
@interface ActionObject : NSObject <ActionObjectProtocol> |
||
// we do not have much for this example! |
// we do not have much for this example! |
||
@end</ |
@end</syntaxhighlight> |
||
<tt>ActionObject.m</tt> |
<tt>ActionObject.m</tt> |
||
< |
<syntaxhighlight lang="objc">#import <Foundation/Foundation.h> |
||
#import "ActionObject.h" |
#import "ActionObject.h" |
||
Line 1,433: | Line 970: | ||
return @"server answers ..."; |
return @"server answers ..."; |
||
} |
} |
||
@end</ |
@end</syntaxhighlight> |
||
<tt>server.m</tt> |
<tt>server.m</tt> |
||
< |
<syntaxhighlight lang="objc">#import <Foundation/Foundation.h> |
||
#import "ActionObject.h" |
#import "ActionObject.h" |
||
int main (void) |
int main (void) |
||
{ |
{ |
||
@autoreleasepool { |
|||
NSAutoreleasePool *pool; |
|||
ActionObject *action; |
|||
NSConnection *connect; |
|||
NSSocketPort *port; |
|||
ActionObject *action = [[ActionObject alloc] init]; |
|||
action = [[ActionObject alloc] init]; |
|||
port = (NSSocketPort *)[NSSocketPort port]; |
NSSocketPort *port = (NSSocketPort *)[NSSocketPort port]; |
||
// initWithTCPPort: 1234 and other methods are not supported yet |
// initWithTCPPort: 1234 and other methods are not supported yet |
||
// by GNUstep |
// by GNUstep |
||
connect = [NSConnection |
NSConnection *connect = [NSConnection |
||
connectionWithReceivePort: port |
connectionWithReceivePort: port |
||
sendPort: port]; // or sendPort: nil |
sendPort: port]; // or sendPort: nil |
||
[connect setRootObject: action]; |
[connect setRootObject: action]; |
||
/* "vend" the object ActionObject as DistributedAction; on GNUstep |
/* "vend" the object ActionObject as DistributedAction; on GNUstep |
||
the Name Server that allows the resolution of the registered name |
the Name Server that allows the resolution of the registered name |
||
is bound to port 538 */ |
is bound to port 538 */ |
||
if ([connect registerName:@"DistributedAction" |
if (![connect registerName:@"DistributedAction" |
||
withNameServer: [NSSocketPortNameServer sharedInstance] ] |
withNameServer: [NSSocketPortNameServer sharedInstance] ]) |
||
{ |
{ |
||
NSLog(@"can't register the server DistributedAction"); |
NSLog(@"can't register the server DistributedAction"); |
||
exit(EXIT_FAILURE); |
exit(EXIT_FAILURE); |
||
} |
} |
||
NSLog(@"waiting for messages..."); |
NSLog(@"waiting for messages..."); |
||
[[NSRunLoop currentRunLoop] run]; |
[[NSRunLoop currentRunLoop] run]; |
||
} |
|||
[pool release]; |
|||
return 0; |
return 0; |
||
}</ |
}</syntaxhighlight> |
||
===Client=== |
===Client=== |
||
<tt>client.m</tt> |
<tt>client.m</tt> |
||
< |
<syntaxhighlight lang="objc">#import <Foundation/Foundation.h> |
||
#import "ActionObjectProtocol.h" |
#import "ActionObjectProtocol.h" |
||
int main(void) |
int main(void) |
||
{ |
{ |
||
@autoreleasepool { |
|||
NSAutoreleasePool *pool; |
|||
NSArray *args; |
|||
id <ActionObjectProtocol> action; |
|||
NSString *msg, *backmsg; |
|||
id <ActionObjectProtocol> action = (id <ActionObjectProtocol>) |
|||
pool = [[NSAutoreleasePool alloc] init]; |
|||
[NSConnection |
|||
rootProxyForConnectionWithRegisteredName: @"DistributedAction" |
|||
host: @"localhost" |
|||
usingNameServer: [NSSocketPortNameServer sharedInstance] ]; |
|||
if (action == nil) |
|||
action = (id <ActionObjectProtocol>) |
|||
{ |
|||
NSLog(@"can't connect to the server"); |
|||
rootProxyForConnectionWithRegisteredName: @"DistributedAction" |
|||
exit(EXIT_FAILURE); |
|||
} |
|||
usingNameServer: [NSSocketPortNameServer sharedInstance] ]; |
|||
if (action == nil) |
|||
{ |
|||
NSLog(@"can't connect to the server"); |
|||
exit(EXIT_FAILURE); |
|||
} |
|||
args = [[NSProcessInfo processInfo] arguments]; |
NSArray *args = [[NSProcessInfo processInfo] arguments]; |
||
if ([args count] == 1) |
if ([args count] == 1) |
||
{ |
{ |
||
NSLog(@"specify a message"); |
NSLog(@"specify a message"); |
||
exit(EXIT_FAILURE); |
exit(EXIT_FAILURE); |
||
} |
} |
||
msg = |
NSString *msg = args[1]; |
||
// "send" (call the selector "sendMessage:" of the (remote) object |
// "send" (call the selector "sendMessage:" of the (remote) object |
||
// action) the first argument's text as msg, store the message "sent |
// action) the first argument's text as msg, store the message "sent |
||
// back" and then show it in the log |
// back" and then show it in the log |
||
backmsg = [action sendMessage: msg]; |
NSString *backmsg = [action sendMessage: msg]; |
||
NSLog(backmsg); |
NSLog("%@", backmsg); |
||
} |
|||
[pool release]; |
|||
return 0; |
return 0; |
||
}</ |
}</syntaxhighlight> |
||
={{header|OCaml}}= |
=={{header|OCaml}}== |
||
{{works with|JoCaml}} |
{{works with|JoCaml}} |
||
Line 1,529: | Line 1,056: | ||
=== Server === |
=== Server === |
||
<lang |
<syntaxhighlight 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 ()</syntaxhighlight> |
|||
=== Client === |
=== Client === |
||
<lang |
<syntaxhighlight 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"</syntaxhighlight> |
|||
={{header| |
=={{header|Oz}}== |
||
We show a program that starts a server on a remote machine, exchanges two messages with that server and finally shuts it down. |
|||
<syntaxhighlight 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}</syntaxhighlight> |
|||
=={{header|Perl}}== |
|||
Using Data::Dumper and Safe to transmit arbitrary data structures as serialized text between hosts. Same code works as both sender and receiver. |
|||
<syntaxhighlight lang="perl">use strict; |
|||
use warnings; |
|||
use Data::Dumper; |
|||
use IO::Socket::INET; |
|||
use Safe; |
|||
sub get_data { |
|||
my $sock = IO::Socket::INET->new( |
|||
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 = Safe->new; |
|||
my $x = $safe->reval(join("", <$cli>)); |
|||
close $cli; |
|||
close $sock; |
|||
return $x; |
|||
} |
|||
sub send_data { |
|||
my $host = shift; |
|||
my $data = shift; |
|||
my $sock = IO::Socket::INET->new( |
|||
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] }); |
|||
}</syntaxhighlight> |
|||
=={{header|Phix}}== |
|||
From/using [http://phix.x10.mx/pmwiki/pmwiki.php?n=Main.Libzmq the ZeroMQ wrapper from PCAN], a suitable simple publish/subscriber pair. |
|||
There is also a server/client/broker example. |
|||
Obviously you can trivially serialize() and deserialize() any Phix data to and from a string. |
|||
<!--<syntaxhighlight lang="phix">(notonline)--> |
|||
<span style="color: #008080;">without</span> <span style="color: #008080;">js</span> <span style="color: #000080;font-style:italic;">-- (zmq dll/so)</span> |
|||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"durapub:\n"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">include</span> <span style="color: #000000;">zmq</span><span style="color: #0000FF;">/</span><span style="color: #000000;">zmq</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">context</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">zmq_init</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">zmq_assert</span><span style="color: #0000FF;">(</span><span style="color: #000000;">context</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"zmq_init"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000080;font-style:italic;">--// subscriber tells us when it's ready here</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">sync</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">zmq_socket</span><span style="color: #0000FF;">(</span><span style="color: #000000;">context</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">ZMQ_PULL</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">zmq_bind</span><span style="color: #0000FF;">(</span><span style="color: #000000;">sync</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"tcp://*:5564"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000080;font-style:italic;">--// send update via this socket</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">publisher</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">zmq_socket</span><span style="color: #0000FF;">(</span><span style="color: #000000;">context</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">ZMQ_PUB</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">zmq_bind</span><span style="color: #0000FF;">(</span><span style="color: #000000;">publisher</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"tcp://*:5565"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000080;font-style:italic;">--// broadcast 10 updates, with pause</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">update_nbr</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">10</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #000000;">s</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Update %d"</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">{</span> <span style="color: #000000;">update_nbr</span> <span style="color: #0000FF;">})</span> |
|||
<span style="color: #000000;">zmq_s_send</span><span style="color: #0000FF;">(</span><span style="color: #000000;">publisher</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">sleep</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #000000;">zmq_s_send</span><span style="color: #0000FF;">(</span><span style="color: #000000;">publisher</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"END"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">sleep</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">zmq_close</span><span style="color: #0000FF;">(</span><span style="color: #000000;">sync</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">zmq_close</span><span style="color: #0000FF;">(</span><span style="color: #000000;">publisher</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">zmq_term</span><span style="color: #0000FF;">(</span><span style="color: #000000;">context</span><span style="color: #0000FF;">)</span> |
|||
<!--</syntaxhighlight>--> |
|||
<!--<syntaxhighlight lang="phix">(notonline)--> |
|||
<span style="color: #008080;">without</span> <span style="color: #008080;">js</span> <span style="color: #000080;font-style:italic;">-- (zmq dll/so)</span> |
|||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"durasub:\n"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">include</span> <span style="color: #000000;">zmq</span><span style="color: #0000FF;">/</span><span style="color: #000000;">zmq</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">context</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">zmq_init</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">zmq_assert</span><span style="color: #0000FF;">(</span><span style="color: #000000;">context</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"zmq_init"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000080;font-style:italic;">--// connect our subscriber socket</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">subscriber</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">zmq_socket</span><span style="color: #0000FF;">(</span><span style="color: #000000;">context</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">ZMQ_SUB</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">id</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">allocate_string</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Hello"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">zmq_setsockopt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">subscriber</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">ZMQ_IDENTITY</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">id</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">5</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">zmq_setsockopt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">subscriber</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">ZMQ_SUBSCRIBE</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">0</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">0</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">zmq_connect</span><span style="color: #0000FF;">(</span><span style="color: #000000;">subscriber</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"tcp://localhost:5565"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">free</span><span style="color: #0000FF;">(</span><span style="color: #000000;">id</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000080;font-style:italic;">--// synchronise with publisher</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">sync</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">zmq_socket</span><span style="color: #0000FF;">(</span><span style="color: #000000;">context</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">ZMQ_PUSH</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">zmq_connect</span><span style="color: #0000FF;">(</span><span style="color: #000000;">sync</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"tcp://localhost:5564"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">zmq_s_send</span><span style="color: #0000FF;">(</span><span style="color: #000000;">sync</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">""</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000080;font-style:italic;">--// get updates, Ctrl-C break</span> |
|||
<span style="color: #008080;">while</span> <span style="color: #004600;">true</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #000000;">s</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">zmq_s_recv</span><span style="color: #0000FF;">(</span><span style="color: #000000;">subscriber</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"%s\n"</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">s</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">==</span><span style="color: #008000;">"END"</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span> |
|||
<span style="color: #000000;">zmq_close</span><span style="color: #0000FF;">(</span><span style="color: #000000;">sync</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">zmq_close</span><span style="color: #0000FF;">(</span><span style="color: #000000;">subscriber</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">zmq_term</span><span style="color: #0000FF;">(</span><span style="color: #000000;">context</span><span style="color: #0000FF;">)</span> |
|||
<!--</syntaxhighlight>--> |
|||
=={{header|PicoLisp}}== |
|||
===Server=== |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
===Client=== |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
Output: |
|||
<pre>PID 18372 |
|||
Result 12</pre> |
|||
=={{header|Python}}== |
|||
{{works with|Python|2.4 and 2.6}} |
{{works with|Python|2.4 and 2.6}} |
||
==XML-RPC== |
|||
=== XML-RPC === |
|||
'''Protocol:''' XML-RPC |
'''Protocol:''' XML-RPC |
||
=== Server === |
==== Server ==== |
||
<syntaxhighlight lang="python">#!/usr/bin/env python |
|||
<lang python> |
|||
#!/usr/bin/env python |
|||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||
Line 1,621: | Line 1,330: | ||
except KeyboardInterrupt: |
except KeyboardInterrupt: |
||
print 'Exiting...' |
print 'Exiting...' |
||
server.server_close() |
server.server_close()</syntaxhighlight> |
||
</lang> |
|||
=== Client === |
==== Client ==== |
||
<syntaxhighlight lang="python">#!/usr/bin/env python |
|||
<lang python> |
|||
#!/usr/bin/env python |
|||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||
Line 1,648: | Line 1,355: | ||
# control if foo_function returns True |
# control if foo_function returns True |
||
if rpc.foo_function(): |
if rpc.foo_function(): |
||
print 'Server says: foo_function returned True' |
print 'Server says: foo_function returned True'</syntaxhighlight> |
||
===HTTP=== |
|||
</lang> |
|||
==HTTP== |
|||
'''Protocol:''' HTTP |
'''Protocol:''' HTTP |
||
=== Server === |
==== Server ==== |
||
<syntaxhighlight lang="python">#!/usr/bin/python |
|||
<lang python> |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||
Line 1,686: | Line 1,390: | ||
except KeyboardInterrupt: |
except KeyboardInterrupt: |
||
print 'Exiting...' |
print 'Exiting...' |
||
server.server_close() |
server.server_close()</syntaxhighlight> |
||
</lang> |
|||
=== Client === |
==== Client ==== |
||
<syntaxhighlight lang="python">#!/usr/bin/python |
|||
<lang python> |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||
Line 1,705: | Line 1,407: | ||
print 'Server Status: %d' % response.status |
print 'Server Status: %d' % response.status |
||
print 'Server Message: %s' % response.read() |
print 'Server Message: %s' % response.read()</syntaxhighlight> |
||
</lang> |
|||
==Socket, |
===Socket, Pickle format=== |
||
'''Protocol:''' Plain Text |
|||
'''Protocol:''' raw socket / pickle format |
|||
=== Server === |
|||
<lang python> |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
|||
This example builds a very basic RPC mechanism on top of sockets and the [http://docs.python.org/library/pickle.html#module-pickle 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 [http://docs.python.org/library/json.html json module] could be used as a substitute, but we lose the ability to transfer arbitrary Python objects that way. |
|||
import SocketServer |
|||
==== Server ==== |
|||
<syntaxhighlight lang="python">#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
|||
import SocketServer |
|||
import pickle |
|||
HOST = "localhost" |
HOST = "localhost" |
||
PORT = 8000 |
PORT = 8000 |
||
class RPCServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): |
|||
# our instance that will upper whatever it gets and send back to client |
|||
# The object_to_proxy member should be set to the object we want |
|||
class UpperCaseHandler(SocketServer.StreamRequestHandler): |
|||
# 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): |
def handle(self): |
||
in_channel = pickle.Unpickler(self.rfile) |
|||
print '%s connected' % self.client_address[0] |
|||
out_channel = pickle.Pickler(self.wfile, protocol=2) |
|||
# get what client sends |
|||
while True: |
|||
try: |
|||
name, args, kwargs = in_channel.load() |
|||
self.wfile.write(get.upper()) |
|||
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__': |
if __name__ == '__main__': |
||
rpcserver = RPCServer((HOST, PORT), RPCHandler) |
|||
rpcserver.object_to_proxy = MyHandlerInstance() |
|||
try: |
try: |
||
rpcserver.serve_forever() |
|||
except KeyboardInterrupt: |
except KeyboardInterrupt: |
||
print 'Exiting...' |
print 'Exiting...' |
||
rpcserver.server_close() |
|||
</syntaxhighlight> |
|||
</lang> |
|||
=== Client === |
==== Client ==== |
||
<syntaxhighlight lang="python">#!/usr/bin/python |
|||
<lang python> |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||
import socket |
import socket |
||
import pickle |
|||
HOST = "localhost" |
HOST = "localhost" |
||
PORT = 8000 |
PORT = 8000 |
||
class RPCClient(object): |
|||
DATA = "my name is eren" |
|||
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): |
|||
# connect to server and send data |
|||
self.socket.close() |
|||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|||
self.rfile.close() |
|||
sock.connect((HOST, PORT)) |
|||
self.wfile.close() |
|||
sock.send("%s\n" % DATA) |
|||
# Make calling remote methods easy by overriding attribute access. |
|||
# get |
|||
# Accessing any attribute on our instances will give a proxy method that |
|||
response = sock.recv(256) |
|||
# calls the method with the same name on the remote machine. |
|||
sock.close() |
|||
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 |
|||
print "We sent: %s" % DATA |
|||
print 'Server responded: %s' % response |
|||
if __name__ == '__main__': |
|||
</lang> |
|||
# 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</syntaxhighlight> |
|||
==Pyro== |
===Pyro=== |
||
'''Note:''' You should install Pyro (http://pyro.sourceforge.net) first and run '''pyro-ns''' binary to run code below. |
'''Note:''' You should install Pyro (http://pyro.sourceforge.net) first and run '''pyro-ns''' binary to run code below. |
||
=== Server === |
==== Server ==== |
||
<syntaxhighlight lang="python">#!/usr/bin/python |
|||
<lang python> |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||
Line 1,795: | Line 1,561: | ||
except KeyboardInterrupt: |
except KeyboardInterrupt: |
||
print 'Exiting...' |
print 'Exiting...' |
||
server.shutdown() |
server.shutdown()</syntaxhighlight> |
||
</lang> |
|||
=== Client === |
==== Client ==== |
||
<syntaxhighlight lang="python">#!/usr/bin/python |
|||
<lang python> |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||
Line 1,816: | Line 1,580: | ||
print 'We sent two numbers to divide: %d and %d' % (NUM1, NUM2) |
print 'We sent two numbers to divide: %d and %d' % (NUM1, NUM2) |
||
print 'Server responded the result: %s' % math.div(NUM1, NUM2) |
print 'Server responded the result: %s' % math.div(NUM1, NUM2)</syntaxhighlight> |
||
</lang> |
|||
== Spread == |
=== Spread === |
||
'''Note:''' You should install Spread (http://www.spread.org) and its python bindings (http://www.python.org/other/spread/) |
'''Note:''' You should install Spread (http://www.spread.org) and its python bindings (http://www.python.org/other/spread/) |
||
=== Server === |
==== 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. |
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'' |
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) === |
==== Client (Listener) ==== |
||
<syntaxhighlight lang="python">#!/usr/bin/python |
|||
<lang python> |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||
Line 1,846: | Line 1,608: | ||
if hasattr(recv, 'sender') and hasattr(recv, 'message'): |
if hasattr(recv, 'sender') and hasattr(recv, 'message'): |
||
print 'Sender: %s' % recv.sender |
print 'Sender: %s' % recv.sender |
||
print 'Message: %s' % recv.message |
print 'Message: %s' % recv.message</syntaxhighlight> |
||
</lang> |
|||
=== Client (Sender) === |
==== Client (Sender) ==== |
||
<syntaxhighlight lang="python">#!/usr/bin/python |
|||
<lang python> |
|||
#!/usr/bin/python |
|||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||
Line 1,862: | Line 1,622: | ||
conn.multicast(spread.RELIABLE_MESS, 'test', 'hello, this is message sent from python') |
conn.multicast(spread.RELIABLE_MESS, 'test', 'hello, this is message sent from python') |
||
conn.disconnect() |
conn.disconnect()</syntaxhighlight> |
||
</lang> |
|||
={{header| |
=={{header|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). |
|||
Uses {{libheader|dRuby}} |
|||
The "druby:" protocol uses TCP/IP sockets for communication. |
|||
<syntaxhighlight lang="racket"> |
|||
'''Server''' |
|||
#lang racket/base |
|||
<lang ruby> require 'drb/drb' |
|||
(require racket/place/distributed racket/place) |
|||
(define (fib n) |
|||
# The URI for the server to connect to |
|||
(if (<= n 1) n (+ (fib (- n 1)) (fib (- n 2))))) |
|||
URI="druby://localhost:8787" |
|||
(provide work) |
|||
class TimeServer |
|||
(define (work) |
|||
(place ch |
|||
(place-channel-put ch (fib (place-channel-get ch))))) |
|||
(module+ main |
|||
def get_current_time |
|||
(define places |
|||
return Time.now |
|||
(for/list ([host '("localhost" "localhost" "localhost" "localhost")] |
|||
end |
|||
[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)))) |
|||
</syntaxhighlight> |
|||
=={{header|Raku}}== |
|||
(formerly Perl 6) |
|||
Server listens for JSON encoded messages. It processes requests for set|get|dump. 'set' stores a message, 'get' returns message, 'dump' returns all stored messages. Optional parameters for ip address and port. |
|||
Server.raku: |
|||
<pre>./server.raku --usage |
|||
Usage: |
|||
server.p6 [--server=<Any>] [--port=<Any>]</pre> |
|||
<syntaxhighlight lang="raku" line>#!/usr/bin/env raku |
|||
use JSON::Fast ; |
|||
sub MAIN( :$server='0.0.0.0' , :$port=3333 ) { |
|||
my %db ; |
|||
react { |
|||
whenever IO::Socket::Async.listen( $server , $port ) -> $conn { |
|||
whenever $conn.Supply.lines -> $line { |
|||
my %response = 'status' => '' ; |
|||
my $msg = from-json $line ; |
|||
say $msg.raku; |
|||
given $msg{"function"} { |
|||
when 'set' { |
|||
%db{ $msg<topic> } = $msg<message> ; |
|||
%response<status> = 'ok' ; |
|||
} |
|||
when 'get' { |
|||
%response<topic> = $msg<topic> ; |
|||
%response<message> = %db{ $msg<topic> } ; |
|||
%response<status> = 'ok' ; |
|||
} |
|||
when 'dump' { |
|||
%response = %db ; |
|||
} |
|||
when 'delete' { |
|||
%db{ $msg<topic> }:delete; |
|||
%response<status> = 'ok' ; |
|||
} |
|||
} |
|||
$conn.print( to-json(%response, :!pretty) ~ "\n" ) ; |
|||
LAST { $conn.close ; } |
|||
QUIT { default { $conn.close ; say "oh no, $_";}} |
|||
CATCH { default { say .^name, ': ', .Str , " handled in $?LINE";}} |
|||
} |
|||
} |
|||
} |
|||
}</syntaxhighlight> |
|||
client.raku: |
|||
<pre>Usage: |
|||
client.raku [--server=<Any>] [--port=<Any>] [--json=<Any>] set <topic> [<message>] |
|||
client.raku [--server=<Any>] [--port=<Any>] get <topic> |
|||
client.raku [--server=<Any>] [--port=<Any>] dump</pre> |
|||
<syntaxhighlight lang="raku" line>#!/usr/bin/env raku |
|||
use JSON::Fast ; |
|||
multi MAIN('set', $topic, $message='', :$server='localhost', :$port='3333', :$json='') { |
|||
my %msg = function => 'set' , topic=> $topic , message=> $message ; |
|||
%msg{"message"} = from-json( $json ) if $json ; |
|||
sendmsg( %msg , $server, $port) ; |
|||
} |
|||
multi MAIN('get', $topic, :$server='localhost', :$port='3333') { |
|||
my %msg = function => 'get' , topic=> $topic ; |
|||
sendmsg( %msg , $server, $port) ; |
|||
} |
|||
multi MAIN('delete', $topic, :$server='localhost', :$port='3333') { |
|||
my %msg = function => 'delete' , topic=> $topic ; |
|||
sendmsg( %msg , $server, $port) ; |
|||
} |
|||
multi MAIN('dump', :$server='localhost', :$port='3333') { |
|||
my %msg = function => 'dump' ; |
|||
sendmsg( %msg , $server, $port) ; |
|||
} |
|||
sub sendmsg( %msg , $server, $port){ |
|||
my $conn = await IO::Socket::Async.connect( $server , $port ); |
|||
$conn.print: to-json( %msg,:!pretty)~"\n"; |
|||
react { |
|||
whenever $conn.Supply -> $data { |
|||
print $data; |
|||
$conn.close; |
|||
} |
|||
} |
|||
}</syntaxhighlight> |
|||
examples: |
|||
<pre>echo '{"function":"set","topic":"push","message":["perl5","raku","rakudo"]}' | nc localhost 3333 |
|||
./client.raku set version raku |
|||
{"status": "ok"} |
|||
./client.raku get version |
|||
{"status": "ok","topic": "version","message": "raku"} |
|||
./client.raku --json='["one","two","three"]' set mylist |
|||
{"status": "ok"} |
|||
./client.raku dump |
|||
{"push": ["perl5","raku","rakudo"],"version": "raku","mylist": ["one","two","three"]} |
|||
./client.raku delete version |
|||
{"status": "ok"} |
|||
server output: |
|||
${:function("set"), :message($["perl5", "raku", "rakudo"]), :topic("push")} |
|||
${:function("set"), :message("raku"), :topic("version")} |
|||
${:function("get"), :topic("version")} |
|||
${:function("set"), :message($["one", "two", "three"]), :topic("mylist")} |
|||
${:function("dump")} |
|||
${:function("delete"), :topic("version")}</pre> |
|||
=={{header|Ruby}}== |
|||
Uses the distributed Ruby (dRuby) from the standard library. The "druby:" protocol uses TCP/IP sockets for communication. |
|||
'''Server''' |
|||
<syntaxhighlight 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 |
||
end |
|||
# The object that handles requests on the server |
|||
FRONT_OBJECT = TimeServer.new |
|||
# 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</syntaxhighlight> |
|||
'''Client''' |
'''Client''' |
||
<lang |
<syntaxhighlight 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</syntaxhighlight> |
|||
={{header|Tcl}}= |
=={{header|Tcl}}== |
||
A rudimentary IRC Server |
A rudimentary IRC Server |
||
< |
<syntaxhighlight lang="tcl">proc main {} { |
||
global connections |
global connections |
||
set connections [dict create] |
set connections [dict create] |
||
Line 1,972: | Line 1,861: | ||
} |
} |
||
main</ |
main</syntaxhighlight> |
||
Client |
Client |
||
< |
<syntaxhighlight lang="tcl">proc main {} { |
||
global argv argc |
global argv argc |
||
if {$argc != 2} { |
if {$argc != 2} { |
||
Line 2,009: | Line 1,898: | ||
} |
} |
||
main</ |
main</syntaxhighlight> |
||
=={{header|UnixPipes}}== |
|||
{{libheader|nc}} |
|||
Uses netcat and a buffer to cycle the server shell's stdout back to netcat's stdin. |
|||
===Server=== |
|||
{{alertbox|yellow|'''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.}} |
|||
<syntaxhighlight lang="bash">: >/tmp/buffer |
|||
tail -f /tmp/buffer | nc -l 127.0.0.1 1234 | sh >/tmp/buffer 2>&1</syntaxhighlight> |
|||
Limitations: |
|||
* The server can accept only one connection (but continues to run, not exit, after this connection dies). |
|||
* With some systems, <code>tail -f</code> might be slow to notice changes to /tmp/buffer. |
|||
===Client=== |
|||
<syntaxhighlight lang="bash">nc 127.0.0.1 1234</syntaxhighlight> |
|||
Now you can enter commands in the client terminal and get the output back through the same connection. |
|||
=={{header|Wren}}== |
|||
{{trans|Go}} |
|||
{{libheader|WrenGo}} |
|||
{{libheader|Wren-fmt}} |
|||
As Wren has no networking support at present, we use embedded programs for both the server and client with a Go host using the net/rpc package in its standard library. |
|||
Moreover, as Wren's VM is not re-entrant, we need to run two VMs from the server side, one to call Go from Wren and the other to call Wren from Go. |
|||
'''Server:''' |
|||
<br> |
|||
We need two Wren scripts one for each VM: |
|||
<syntaxhighlight lang="wren">/* Distributed_programming_server.wren */ |
|||
class Rpc { |
|||
foreign static register() |
|||
foreign static handleHTTP() |
|||
} |
|||
foreign class Listener { |
|||
construct listen(network, address) {} |
|||
} |
|||
class HTTP { |
|||
foreign static serve(listener) |
|||
} |
|||
Rpc.register() |
|||
Rpc.handleHTTP() |
|||
var listener = Listener.listen("tcp", ":1234") |
|||
HTTP.serve(listener)</syntaxhighlight> |
|||
<br> |
|||
<syntaxhighlight lang="wren">/* Distributed_programming_server_2.wren */ |
|||
class TaxComputer { |
|||
static tax(amount, rate) { |
|||
if (amount < 0) Fiber.abort("Negative values not allowed.") |
|||
return amount * rate |
|||
} |
|||
}</syntaxhighlight> |
|||
<br> |
|||
We now embed these scripts in the following Go program and run it on one terminal. |
|||
<syntaxhighlight lang="go">/* go run Distributed_programming_server.go */ |
|||
package main |
|||
import( |
|||
wren "github.com/crazyinfin8/WrenGo" |
|||
"log" |
|||
"net" |
|||
"net/http" |
|||
"net/rpc" |
|||
) |
|||
type any = interface{} |
|||
type TaxComputer float64 |
|||
var vm2 *wren.VM |
|||
var fileName = "Distributed_programming_server.wren" |
|||
var fileName2 = "Distributed_programming_server_2.wren" |
|||
func (taxRate TaxComputer) Tax(x float64, r *float64) error { |
|||
wrenVar, _ := vm2.GetVariable(fileName2, "TaxComputer") |
|||
wrenClass, _ := wrenVar.(*wren.Handle) |
|||
defer wrenClass.Free() |
|||
wrenMethod, _ := wrenClass.Func("tax(_,_)") |
|||
defer wrenMethod.Free() |
|||
ret, _ := wrenMethod.Call(x, float64(taxRate)) |
|||
*r = ret.(float64) |
|||
return nil |
|||
} |
|||
func register(vm *wren.VM, parameters []any) (any, error) { |
|||
c := TaxComputer(0.05) // 5% tax rate |
|||
rpc.Register(c) |
|||
return nil, nil |
|||
} |
|||
func handleHTTP(vm *wren.VM, parameters []any) (any, error) { |
|||
rpc.HandleHTTP() |
|||
return nil, nil |
|||
} |
|||
func serve(vm *wren.VM, parameters []any) (any, error) { |
|||
handle := parameters[1].(*wren.ForeignHandle) |
|||
ifc, _ := handle.Get() |
|||
listener := ifc.(*net.Listener) |
|||
http.Serve(*listener, nil) |
|||
return nil, nil |
|||
} |
|||
func listen(vm *wren.VM, parameters []any) (any, error) { |
|||
network := parameters[1].(string) |
|||
address := parameters[2].(string) |
|||
listener, err := net.Listen(network, address) |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
return &listener, nil |
|||
} |
|||
func main() { |
|||
vm := wren.NewVM() |
|||
vm2 = wren.NewVM() |
|||
vm2.InterpretFile(fileName2) |
|||
rpcMethodMap := wren.MethodMap { |
|||
"static register()": register, |
|||
"static handleHTTP()": handleHTTP, |
|||
} |
|||
httpMethodMap := wren.MethodMap { "static serve(_)":serve } |
|||
classMap := wren.ClassMap { |
|||
"Listener": wren.NewClass(listen, nil, nil), |
|||
"Rpc" : wren.NewClass(nil, nil, rpcMethodMap), |
|||
"HTTP" : wren.NewClass(nil, nil, httpMethodMap), |
|||
} |
|||
module := wren.NewModule(classMap) |
|||
vm.SetModule(fileName, module) |
|||
vm.InterpretFile(fileName) |
|||
vm.Free() |
|||
vm2.Free() |
|||
}</syntaxhighlight> |
|||
<br> |
|||
'''Client:''' |
|||
<br> |
|||
Just one Wren script needed here: |
|||
<syntaxhighlight lang="wren">/* Distributed_programming_client.wren */ |
|||
import "./fmt" for Fmt |
|||
foreign class Client { |
|||
construct dialHTTP(network, address) {} |
|||
foreign call(serviceMethod, arg) |
|||
} |
|||
var client = Client.dialHTTP("tcp", "localhost:1234") |
|||
var amounts = [3, 5.6] |
|||
for (amount in amounts) { |
|||
var tax = client.call("TaxComputer.Tax", amount) |
|||
Fmt.print("Tax on $0.2f = $0.2f", amount, tax) |
|||
}</syntaxhighlight> |
|||
<br> |
|||
which we embed in the following Go program and run it on a different terminal. |
|||
<syntaxhighlight lang="go">/* go run Distributed_programming_client.go */ |
|||
package main |
|||
import( |
|||
wren "github.com/crazyinfin8/WrenGo" |
|||
"log" |
|||
"net/rpc" |
|||
"strings" |
|||
) |
|||
type any = interface{} |
|||
func dialHTTP(vm *wren.VM, parameters []any) (any, error) { |
|||
network := parameters[1].(string) |
|||
address := parameters[2].(string) |
|||
client, err := rpc.DialHTTP(network, address) |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
return &client, nil |
|||
} |
|||
func call(vm *wren.VM, parameters []any) (any, error) { |
|||
handle := parameters[0].(*wren.ForeignHandle) |
|||
ifc, _ := handle.Get() |
|||
client := ifc.(**rpc.Client) |
|||
serviceMethod := parameters[1].(string) |
|||
amount := parameters[2].(float64) |
|||
var tax float64 |
|||
err := (*client).Call(serviceMethod, amount, &tax) |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
return tax, nil |
|||
} |
|||
func moduleFn(vm *wren.VM, name string) (string, bool) { |
|||
if name != "meta" && name != "random" && !strings.HasSuffix(name, ".wren") { |
|||
name += ".wren" |
|||
} |
|||
return wren.DefaultModuleLoader(vm, name) |
|||
} |
|||
func main() { |
|||
={{header|UnixPipes}}= |
|||
cfg := wren.NewConfig() |
|||
Uses netcat |
|||
cfg.LoadModuleFn = moduleFn |
|||
vm := cfg.NewVM() |
|||
fileName := "Distributed_programming_client.wren" |
|||
clientMethodMap := wren.MethodMap { "call(_,_)": call } |
|||
classMap := wren.ClassMap { "Client": wren.NewClass(dialHTTP, nil, clientMethodMap) } |
|||
module := wren.NewModule(classMap) |
|||
vm.SetModule(fileName, module) |
|||
vm.InterpretFile(fileName) |
|||
vm.Free() |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
server |
|||
Output on the client terminal: |
|||
(echo 1; echo 2; echo 3) | nc -l 1024 |
|||
<pre> |
|||
Tax on 3.00 = 0.15 |
|||
Tax on 5.60 = 0.28 |
|||
</pre> |
|||
{{omit from|Lotus 123 Macro Scripting}} |
|||
client |
|||
{{omit from|Maxima}} |
|||
nc 192.168.0.1 1024 | wc -l |
|||
{{omit from|PARI/GP}} |
|||
{{omit from|Retro}} |
Latest revision as of 11:48, 26 November 2023
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:
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: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.
#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;
}
- Output:
(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
C#
Start the program with "server" parameter to start the server, and "client" to start the client. The client will send data to the server and receive a response. The server will wait for data, display the data received, and send a response.
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading.Tasks;
using static System.Console;
class DistributedProgramming
{
const int Port = 555;
async static Task RunClient()
{
WriteLine("Connecting");
var client = new TcpClient();
await client.ConnectAsync("localhost", Port);
using (var stream = client.GetStream())
{
WriteLine("Sending loot");
var data = Serialize(new SampleData());
await stream.WriteAsync(data, 0, data.Length);
WriteLine("Receiving thanks");
var buffer = new byte[80000];
var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
var thanks = (string)Deserialize(buffer, bytesRead);
WriteLine(thanks);
}
client.Close();
}
async static Task RunServer()
{
WriteLine("Listening");
var listener = new TcpListener(IPAddress.Any, Port);
listener.Start();
var client = await listener.AcceptTcpClientAsync();
using (var stream = client.GetStream())
{
WriteLine("Receiving loot");
var buffer = new byte[80000];
var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
var data = (SampleData)Deserialize(buffer, bytesRead);
WriteLine($"{data.Loot} at {data.Latitude}, {data.Longitude}");
WriteLine("Sending thanks");
var thanks = Serialize("Thanks!");
await stream.WriteAsync(thanks, 0, thanks.Length);
}
client.Close();
listener.Stop();
Write("Press a key");
ReadKey();
}
static byte[] Serialize(object data)
{
using (var mem = new MemoryStream())
{
new BinaryFormatter().Serialize(mem, data);
return mem.ToArray();
}
}
static object Deserialize(byte[] data, int length)
{
using (var mem = new MemoryStream(data, 0, length))
{
return new BinaryFormatter().Deserialize(mem);
}
}
static void Main(string[] args)
{
if (args.Length == 0) return;
switch (args[0])
{
case "client": RunClient().Wait(); break;
case "server": RunServer().Wait(); break;
}
}
}
[Serializable]
class SampleData
{
public decimal Latitude = 44.33190m;
public decimal Longitude = 114.84129m;
public string Loot = "140 tonnes of jade";
}
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;
}
}
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.)
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.
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`)
}
}
Erlang
The protocol is erlang's own
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.
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
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).
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.
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" } } }
Go
Standard library net/rpc
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:
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)
}
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
gRPC
The default serialization for gRPC is "protocol buffers." gRPC uses a .proto file to define an interface for the client and server. The .proto has its own syntax, independent of client and server implementation languages. Server and client programs here are Go however.
.proto:
syntax = "proto3";
service TaxComputer {
rpc Tax(Amount) returns (Amount) {}
}
message Amount {
int32 cents = 1;
}
Server:
package main
import (
"errors"
"net"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
"taxcomputer"
)
type taxServer struct {
rate float64
}
func (s *taxServer) Tax(ctx context.Context,
amt *taxcomputer.Amount) (*taxcomputer.Amount, error) {
if amt.Cents < 0 {
return nil, errors.New("Negative amounts not allowed")
}
return &taxcomputer.Amount{int32(float64(amt.Cents)*s.rate + .5)}, nil
}
func main() {
listener, err := net.Listen("tcp", ":1234")
if err != nil {
grpclog.Fatalf(err.Error())
}
grpcServer := grpc.NewServer()
taxcomputer.RegisterTaxComputerServer(grpcServer, &taxServer{.05})
grpcServer.Serve(listener)
}
Client:
package main
import (
"fmt"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
"taxcomputer"
)
func main() {
conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
if err != nil {
grpclog.Fatalf(err.Error())
}
defer conn.Close()
client := taxcomputer.NewTaxComputerClient(conn)
amt := &taxcomputer.Amount{300}
tax, err := client.Tax(context.Background(), amt)
if err != nil {
grpclog.Fatalf(err.Error())
}
fmt.Println("Tax on", amt.Cents, "cents is", tax.Cents, "cents")
}
- Client output:
Tax on 300 cents is 15 cents
Apache Thrift
See https://thrift.apache.org/
.thrift
Like gRPC, Thrift requires a language independent interface definition file:
service TaxService {
i32 tax(1: i32 amt)
}
Server:
package main
import (
"errors"
"log"
"git.apache.org/thrift.git/lib/go/thrift"
"gen-go/tax"
)
type taxHandler float64
func (r taxHandler) Tax(amt int32) (int32, error) {
if amt < 0 {
return 0, errors.New("Negative amounts not allowed")
}
return int32(float64(amt)*float64(r) + .5), nil
}
func main() {
transport, err := thrift.NewTServerSocket("localhost:3141")
if err != nil {
log.Fatal(err)
}
transFac := thrift.NewTTransportFactory()
protoFac := thrift.NewTCompactProtocolFactory()
proc := tax.NewTaxServiceProcessor(taxHandler(.05))
s := thrift.NewTSimpleServer4(proc, transport, transFac, protoFac)
if err := s.Serve(); err != nil {
log.Fatal(err)
}
}
Client:
package main
import (
"fmt"
"log"
"git.apache.org/thrift.git/lib/go/thrift"
"gen-go/tax"
)
func main() {
transport, err := thrift.NewTSocket("localhost:3141")
if err != nil {
log.Fatal(err)
}
if err := transport.Open(); err != nil {
log.Fatal(err)
}
protoFac := thrift.NewTCompactProtocolFactory()
client := tax.NewTaxServiceClientFactory(transport, protoFac)
amt := int32(300)
t, err := client.Tax(amt)
if err != nil {
log.Print(err)
} else {
fmt.Println("tax on", amt, "is", t)
}
transport.Close()
}
- Client output:
tax on 300 is 15
Haskell
See:
Check license: http://www.haskell.org/haskellwiki/HaskellWiki:Copyrights
JavaScript
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')
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())
})
Julia
Julia was designed with distributed conmputing. in particular cluster computing, as a primary use target. If a group of CPUs, including multiple cores on a single machine or a cluster running with paswordless ssh login, is used, the following can be set up as an example:
# From Julia 1.0's online docs. File countheads.jl available to all machines:
function count_heads(n)
c::Int = 0
for i = 1:n
c += rand(Bool)
end
c
end
We then run the following on the primary client:
using Distributed
@everywhere include_string(Main, $(read("count_heads.jl", String)), "count_heads.jl")
a = @spawn count_heads(100000000) # runs on an available processor
b = @spawn count_heads(100000000) # runs on another available processor
println(fetch(a)+fetch(b)) # total heads of 200 million coin flips, half on each CPU
- Output:
100001564
LFE
The protocol used is the one native to Erlang (and thus native to LFE, Lisp Flavored Erlang).
These examples are done completely in the LFE REPL.
Server
In one terminal window, start up the REPL
$ ./bin/lfe
Erlang/OTP 17 [erts-6.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
LFE Shell V6.2 (abort with ^G)
>
And then enter the following code
> (defun get-server-name ()
(list_to_atom (++ "exampleserver@" (element 2 (inet:gethostname)))))
> (defun start ()
(net_kernel:start `(,(get-server-name) shortnames))
(erlang:set_cookie (node) 'rosettaexample)
(let ((pid (spawn #'listen/0)))
(register 'serverref pid)
(io:format "~p ready~n" (list (node pid)))
'ok))
> (defun listen ()
(receive
(`#(echo ,pid ,data)
(io:format "Got ~p from ~p~n" (list data (node pid)))
(! pid `#(hello ,data))
(listen))
(x
(io:format "Unexpected pattern: ~p~n" `(,x)))))
Client
In another terminal window, start up another LFE REPL and ender the following code:
> (defun get-server-name ()
(list_to_atom (++ "exampleserver@" (element 2 (inet:gethostname)))))
> (defun send (data)
(net_kernel:start '(exampleclient shortnames))
(erlang:set_cookie (node) 'rosettaexample)
(io:format "connecting to ~p~n" `(,(get-server-name)))
(! `#(serverref ,(get-server-name)) `#(echo ,(self) ,data))
(receive
(`#(hello ,data)
(io:format "Received ~p~n" `(,data)))
(x
(io:format "Unexpected pattern: ~p~n" (list x))))
'ok)
To use this code, simply start the server in the server terminal:
> (start)
exampleserver@yourhostname ready
ok
(exampleserver@yourhostname)>
Send some messages from the client terminal:
> (send "hi there")
connecting to exampleserver@yourhostname
Received "hi there"
ok
(exampleclient@yourhostname)> (send 42)
connecting to exampleserver@yourhostname
Received 42
ok
(exampleclient@yourhostname)> (send #(key value))
connecting to exampleserver@yourhostname
Received {key,value}
ok
(exampleclient@yourhostname)>
And check out the results back in the server terminal window:
Got "hi there" from exampleclient@yourhostname
Got 42 from exampleclient@yourhostname
Got {key,value} from exampleclient@yourhostname
Mathematica / Wolfram Language
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[]]
Nim
import os, nanomsg
proc sendMsg(s: cint, msg: string) =
echo "SENDING \"",msg,"\""
let bytes = s.send(msg.cstring, msg.len + 1, 0)
assert bytes == msg.len + 1
proc recvMsg(s: cint) =
var buf: cstring
let bytes = s.recv(addr buf, MSG, 0)
if bytes > 0:
echo "RECEIVED \"",buf,"\""
discard freemsg buf
proc sendRecv(s: cint, msg: string) =
var to: cint = 100
discard s.setSockOpt(SOL_SOCKET, RCVTIMEO, addr to, sizeof to)
while true:
s.recvMsg
sleep 1000
s.sendMsg msg
proc node0(url: string) =
var s = socket(AF_SP, nanomsg.PAIR)
assert s >= 0
let res = s.bindd url
assert res >= 0
s.sendRecv "node0"
discard s.shutdown 0
proc node1(url: string) =
var s = socket(AF_SP, nanomsg.PAIR)
assert s >= 0
let res = s.connect url
assert res >= 0
s.sendRecv "node1"
discard s.shutdown 0
if paramStr(1) == "node0":
node0 paramStr(2)
elif paramStr(1) == "node1":
node1 paramStr(2)
Usage:
./pair node0 tcp://127.0.0.1:25000 ./pair node1 tcp://127.0.0.1:25000
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
#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;
}
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;
}
OCaml
Minimalistic distributed logger with synchronous channels using the join calculus on top of OCaml.
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 ()
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"
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}
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 strict;
use warnings;
use Data::Dumper;
use IO::Socket::INET;
use Safe;
sub get_data {
my $sock = IO::Socket::INET->new(
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 = Safe->new;
my $x = $safe->reval(join("", <$cli>));
close $cli;
close $sock;
return $x;
}
sub send_data {
my $host = shift;
my $data = shift;
my $sock = IO::Socket::INET->new(
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] });
}
Phix
From/using the ZeroMQ wrapper from PCAN, a suitable simple publish/subscriber pair. There is also a server/client/broker example. Obviously you can trivially serialize() and deserialize() any Phix data to and from a string.
without js -- (zmq dll/so) puts(1, "durapub:\n") include zmq/zmq.e atom context = zmq_init(1) zmq_assert(context, "zmq_init") --// subscriber tells us when it's ready here atom sync = zmq_socket(context, ZMQ_PULL) zmq_bind(sync, "tcp://*:5564") --// send update via this socket atom publisher = zmq_socket(context, ZMQ_PUB) zmq_bind(publisher, "tcp://*:5565") --// broadcast 10 updates, with pause for update_nbr = 1 to 10 do string s = sprintf("Update %d", { update_nbr }) zmq_s_send(publisher, s) sleep(1) end for zmq_s_send(publisher, "END") sleep(1) zmq_close(sync) zmq_close(publisher) zmq_term(context)
without js -- (zmq dll/so) puts(1, "durasub:\n") include zmq/zmq.e atom context = zmq_init(1) zmq_assert(context, "zmq_init") --// connect our subscriber socket atom subscriber = zmq_socket(context, ZMQ_SUB) atom id = allocate_string("Hello") zmq_setsockopt(subscriber, ZMQ_IDENTITY, id, 5) zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, 0, 0) zmq_connect(subscriber, "tcp://localhost:5565") free(id) --// synchronise with publisher atom sync = zmq_socket(context, ZMQ_PUSH) zmq_connect(sync, "tcp://localhost:5564") zmq_s_send(sync, "") --// get updates, Ctrl-C break while true do string s = zmq_s_recv(subscriber) printf(1, "%s\n", {s}) if s=="END" then exit end if end while zmq_close(sync) zmq_close(subscriber) zmq_term(context)
PicoLisp
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
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
Python
XML-RPC
Protocol: XML-RPC
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()
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'
HTTP
Protocol: HTTP
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()
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()
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.
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()
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
Pyro
Note: You should install Pyro (http://pyro.sourceforge.net) first and run pyro-ns binary to run code below.
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()
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)
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)
#!/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
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()
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))))
Raku
(formerly Perl 6)
Server listens for JSON encoded messages. It processes requests for set|get|dump. 'set' stores a message, 'get' returns message, 'dump' returns all stored messages. Optional parameters for ip address and port.
Server.raku:
./server.raku --usage Usage: server.p6 [--server=<Any>] [--port=<Any>]
#!/usr/bin/env raku
use JSON::Fast ;
sub MAIN( :$server='0.0.0.0' , :$port=3333 ) {
my %db ;
react {
whenever IO::Socket::Async.listen( $server , $port ) -> $conn {
whenever $conn.Supply.lines -> $line {
my %response = 'status' => '' ;
my $msg = from-json $line ;
say $msg.raku;
given $msg{"function"} {
when 'set' {
%db{ $msg<topic> } = $msg<message> ;
%response<status> = 'ok' ;
}
when 'get' {
%response<topic> = $msg<topic> ;
%response<message> = %db{ $msg<topic> } ;
%response<status> = 'ok' ;
}
when 'dump' {
%response = %db ;
}
when 'delete' {
%db{ $msg<topic> }:delete;
%response<status> = 'ok' ;
}
}
$conn.print( to-json(%response, :!pretty) ~ "\n" ) ;
LAST { $conn.close ; }
QUIT { default { $conn.close ; say "oh no, $_";}}
CATCH { default { say .^name, ': ', .Str , " handled in $?LINE";}}
}
}
}
}
client.raku:
Usage: client.raku [--server=<Any>] [--port=<Any>] [--json=<Any>] set <topic> [<message>] client.raku [--server=<Any>] [--port=<Any>] get <topic> client.raku [--server=<Any>] [--port=<Any>] dump
#!/usr/bin/env raku
use JSON::Fast ;
multi MAIN('set', $topic, $message='', :$server='localhost', :$port='3333', :$json='') {
my %msg = function => 'set' , topic=> $topic , message=> $message ;
%msg{"message"} = from-json( $json ) if $json ;
sendmsg( %msg , $server, $port) ;
}
multi MAIN('get', $topic, :$server='localhost', :$port='3333') {
my %msg = function => 'get' , topic=> $topic ;
sendmsg( %msg , $server, $port) ;
}
multi MAIN('delete', $topic, :$server='localhost', :$port='3333') {
my %msg = function => 'delete' , topic=> $topic ;
sendmsg( %msg , $server, $port) ;
}
multi MAIN('dump', :$server='localhost', :$port='3333') {
my %msg = function => 'dump' ;
sendmsg( %msg , $server, $port) ;
}
sub sendmsg( %msg , $server, $port){
my $conn = await IO::Socket::Async.connect( $server , $port );
$conn.print: to-json( %msg,:!pretty)~"\n";
react {
whenever $conn.Supply -> $data {
print $data;
$conn.close;
}
}
}
examples:
echo '{"function":"set","topic":"push","message":["perl5","raku","rakudo"]}' | nc localhost 3333 ./client.raku set version raku {"status": "ok"} ./client.raku get version {"status": "ok","topic": "version","message": "raku"} ./client.raku --json='["one","two","three"]' set mylist {"status": "ok"} ./client.raku dump {"push": ["perl5","raku","rakudo"],"version": "raku","mylist": ["one","two","three"]} ./client.raku delete version {"status": "ok"} server output: ${:function("set"), :message($["perl5", "raku", "rakudo"]), :topic("push")} ${:function("set"), :message("raku"), :topic("version")} ${:function("get"), :topic("version")} ${:function("set"), :message($["one", "two", "three"]), :topic("mylist")} ${:function("dump")} ${:function("delete"), :topic("version")}
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
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
UnixPipes
Uses netcat and a buffer to cycle the server shell's stdout back to netcat's stdin.
Server
: >/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.
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.
Wren
As Wren has no networking support at present, we use embedded programs for both the server and client with a Go host using the net/rpc package in its standard library.
Moreover, as Wren's VM is not re-entrant, we need to run two VMs from the server side, one to call Go from Wren and the other to call Wren from Go.
Server:
We need two Wren scripts one for each VM:
/* Distributed_programming_server.wren */
class Rpc {
foreign static register()
foreign static handleHTTP()
}
foreign class Listener {
construct listen(network, address) {}
}
class HTTP {
foreign static serve(listener)
}
Rpc.register()
Rpc.handleHTTP()
var listener = Listener.listen("tcp", ":1234")
HTTP.serve(listener)
/* Distributed_programming_server_2.wren */
class TaxComputer {
static tax(amount, rate) {
if (amount < 0) Fiber.abort("Negative values not allowed.")
return amount * rate
}
}
We now embed these scripts in the following Go program and run it on one terminal.
/* go run Distributed_programming_server.go */
package main
import(
wren "github.com/crazyinfin8/WrenGo"
"log"
"net"
"net/http"
"net/rpc"
)
type any = interface{}
type TaxComputer float64
var vm2 *wren.VM
var fileName = "Distributed_programming_server.wren"
var fileName2 = "Distributed_programming_server_2.wren"
func (taxRate TaxComputer) Tax(x float64, r *float64) error {
wrenVar, _ := vm2.GetVariable(fileName2, "TaxComputer")
wrenClass, _ := wrenVar.(*wren.Handle)
defer wrenClass.Free()
wrenMethod, _ := wrenClass.Func("tax(_,_)")
defer wrenMethod.Free()
ret, _ := wrenMethod.Call(x, float64(taxRate))
*r = ret.(float64)
return nil
}
func register(vm *wren.VM, parameters []any) (any, error) {
c := TaxComputer(0.05) // 5% tax rate
rpc.Register(c)
return nil, nil
}
func handleHTTP(vm *wren.VM, parameters []any) (any, error) {
rpc.HandleHTTP()
return nil, nil
}
func serve(vm *wren.VM, parameters []any) (any, error) {
handle := parameters[1].(*wren.ForeignHandle)
ifc, _ := handle.Get()
listener := ifc.(*net.Listener)
http.Serve(*listener, nil)
return nil, nil
}
func listen(vm *wren.VM, parameters []any) (any, error) {
network := parameters[1].(string)
address := parameters[2].(string)
listener, err := net.Listen(network, address)
if err != nil {
log.Fatal(err)
}
return &listener, nil
}
func main() {
vm := wren.NewVM()
vm2 = wren.NewVM()
vm2.InterpretFile(fileName2)
rpcMethodMap := wren.MethodMap {
"static register()": register,
"static handleHTTP()": handleHTTP,
}
httpMethodMap := wren.MethodMap { "static serve(_)":serve }
classMap := wren.ClassMap {
"Listener": wren.NewClass(listen, nil, nil),
"Rpc" : wren.NewClass(nil, nil, rpcMethodMap),
"HTTP" : wren.NewClass(nil, nil, httpMethodMap),
}
module := wren.NewModule(classMap)
vm.SetModule(fileName, module)
vm.InterpretFile(fileName)
vm.Free()
vm2.Free()
}
Client:
Just one Wren script needed here:
/* Distributed_programming_client.wren */
import "./fmt" for Fmt
foreign class Client {
construct dialHTTP(network, address) {}
foreign call(serviceMethod, arg)
}
var client = Client.dialHTTP("tcp", "localhost:1234")
var amounts = [3, 5.6]
for (amount in amounts) {
var tax = client.call("TaxComputer.Tax", amount)
Fmt.print("Tax on $0.2f = $0.2f", amount, tax)
}
which we embed in the following Go program and run it on a different terminal.
/* go run Distributed_programming_client.go */
package main
import(
wren "github.com/crazyinfin8/WrenGo"
"log"
"net/rpc"
"strings"
)
type any = interface{}
func dialHTTP(vm *wren.VM, parameters []any) (any, error) {
network := parameters[1].(string)
address := parameters[2].(string)
client, err := rpc.DialHTTP(network, address)
if err != nil {
log.Fatal(err)
}
return &client, nil
}
func call(vm *wren.VM, parameters []any) (any, error) {
handle := parameters[0].(*wren.ForeignHandle)
ifc, _ := handle.Get()
client := ifc.(**rpc.Client)
serviceMethod := parameters[1].(string)
amount := parameters[2].(float64)
var tax float64
err := (*client).Call(serviceMethod, amount, &tax)
if err != nil {
log.Fatal(err)
}
return tax, nil
}
func moduleFn(vm *wren.VM, name string) (string, bool) {
if name != "meta" && name != "random" && !strings.HasSuffix(name, ".wren") {
name += ".wren"
}
return wren.DefaultModuleLoader(vm, name)
}
func main() {
cfg := wren.NewConfig()
cfg.LoadModuleFn = moduleFn
vm := cfg.NewVM()
fileName := "Distributed_programming_client.wren"
clientMethodMap := wren.MethodMap { "call(_,_)": call }
classMap := wren.ClassMap { "Client": wren.NewClass(dialHTTP, nil, clientMethodMap) }
module := wren.NewModule(classMap)
vm.SetModule(fileName, module)
vm.InterpretFile(fileName)
vm.Free()
}
- Output:
Output on the client terminal:
Tax on 3.00 = 0.15 Tax on 5.60 = 0.28