You are on page 1of 36

Lamports Logical Clock

Inherent Limitations of a Distributed System Absence of Global clock


difficult to make temporal order of events difficult to collect up-to-date information on the state of the entire system

Absence of Shared Memory

no up-to-date state of the entire system to any individual process as there's no shared memory coherent view -- all observations of different processes ( computers ) are made at the same physical time we can obtain a coherent but partial view of the system or incoherent view of the system

complete view ( global state ) -- local views ( local states ) + messages in transit difficult to obtain a coherent global state Leslie Lamport proposed this scheme to provide ordering of events in a distributed environment using logical clocks. Because it is impossible to have perfectly synchronized clocks and global time in a distributed system, it is often necessary to use logical clocks instead. Definitions: Happened Before Relation (->). This relation captures causal dependencies between events, that is, whether or not events have a cause and effect relation. This relation (->) is defined as follows: a -> b, if a and b are in the same process and a occurred before b. a -> b, if a is the event of sending a message and b is the receipt of that message by another process. If a -> b and b -> c, then a -> c - that is, the relation has the property of transitivity.

Causally Related Events: If event a -> event b, then a causally affects b.

Concurrent Events: Two distinct events a and b are concurrent (a || b) if (not) a -> b and (not) b -> a. That is, the events have no causal relationship. This is equivalent to b || a.

For any two events a and b in a system, only one of the following is true: a -> b, b -> a, or a || b.

e11 e12 e21 e13 , e14 || e24

, e12 e22

Lamport introduced a system of logical clocks in order to make the -> relation possible. It works like this: Each process Pi in the system has its own clock Ci. Ci can be looked at as a function that assigns a number, Ci(a) to an event a. This is the timestamp of the event a in process Pi. These numbers are not in any way related to physical time -- that is why they are called logical clocks. These are generally implemented using counters, which increase each time an event occurs. Generally, an event's timestamp is the value of the clock at that time it occurs.

Conditions Satisfied by the Logical Clock system: For any events a and b, if a -> b, then C(a) < C(b). This is true if two conditions are met: If a occurs before b, then Ci(a) < Ci(b). If a is a message sent from Pi and b is the recept of that same message in Pj, then Ci(a) < Cj(b).

Implementation Rules Required: Clock Ci is incremented for each event: Ci := Ci + d (d > 0) if a is the event of sending a message from one process to another, then the receiver sets its clock to the max of its current clock and the sender's clock - that is, Cj := max(Cj, tm + d) (d > 0) . Limitation of Lamport's Clocks: if a b but C(a) < C(b) does not necessarily imply a b then C(a) < C(b)

Distributed Mutual Exclusion


Distributed mutual exclusion (DME) coordinates software on different computers so that they agree upon assigning a resource or section of code to one particular task.

Requirement Mutual Exclusion Freedom from deadlock Eventual entry(freedom from starvation) All processes must participate equally. Only interested processes participate. Assumptions N nodes randomly request access. Messages are not lost or corrupted. Message transmissions take a finite, variable, non-zero time. Messages arrive in order. Transmission time might not be transitive. Network is fully connected.

Mutual Exclusion Goals Minimize the number of messages sent. Grant permission in order of request. Fault tolerant Fair to all systems Scalable

Distributed Mutual Exclusion Algorithms Assertion Based

Lamport algorithm Ricart-Agrawala algorithm Maekawaa algorithm

Token Based Raymonds Tree-based algorithm Simple 2 process algorithm

Lamports DME Algorithm Processes are granted mutual exclusion in the order in which they make the request. Each process maintains a request queue sorted in timestamp order. Assertion based algorithm. Uses the Lamport time numbers. 1.To request a resource, process Pi sends a timestamped request message to every other process and puts the request on its own request queue. 2.When process Pk receives a request message, it sends a timestamped reply and puts the request on its request queue. 3.Process Pi can access the resource when Its own request is at the top of the request queue. It has received a reply from every other process. 4.To release a resource, Piremoves its request from its queue and sends a release message to all other processes. When process Pkreceives a release message, it removes Pis request from its queue.

Ricartand AgrawalasAlgorithm Similar to LamportsAlgorithm but slightly more efficient. 1.To request a resource, process Pi sends a time stamped request message to every other process. 2. When process Pk receives a request msg if Pkis not currently requesting the resource it sends a time stamped reply else if timestamp of Pi request is < the timestamp of Pks request it sends a timestamped reply else it keeps Pis request. 3.Process Pi has received a reply from every other process, it can access the resource. 4.To release a resource, Pireturns a reply message to all pending processes. Simple Two Process Algorithm Assume only two processes are competing for a sigle resource. The two processes communicate by message passing.

Distributed Chat Server using TCP Sockets in C


Receiver:
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include<string.h>

#include <iostream> #include <stdio.h> #include <stdlib.h>

using namespace std ; //To use cout

#ifndef INADDR_NONE #define INADDR_NONE ((unsigned long)-1) INADDR_NONE if not already defined. #endif //This defines a constant

class receiver { int portNo ;

//Class that contains members and methods to implement the receiver

int msgCount ; //Private Data Members

public:

receiver(int x) 0 to msgCount { portNo = x ;

//Constructor that initialzes portNO to the passed value and

msgCount = 0 ; //It counts the no. of messages recived bby a perticular sender }

void connection_oriented(void) ; void connection_less(void) ; };

//FOR TCP CONNECTION //FOR UDP SOCKET

int main() { int tempVar ; //for temporary input purpose int connectionType = 0 ; //to store the value for the type of Connection - 1 for TCP or 2 for UDP

cout<<"\nEnter the Port No to be used for connecton:" ; cin>>tempVar ;

if(tempVar < 1024 || tempVar > 65535) { ; cout<<"\nPlease Enter Port No. in proper range (1024 to 65535)!!!\nSystem exting..." //portNo less than 1024 are reserved return 0 ; }

receiver obj(tempVar) ; //creating an object of a class and calling a constructor cout<<"\nEnter the Type of connection\nPress 1 for TCP (Connection Oreinted) or 2 for UDP (Connection Less):" ;

cin>>connectionType ; if(connectionType == 1) obj.connection_oriented() ; //i.e TCP socket is choosed by the user else if(connectionType == 2) obj.connection_less() ; else { cout<<"\nWrong Option selected!!!\nSystem exiting..." ; option is selected return 0 ; } //Error Msg if any other //i.e UDP socket is choosed by the user

return 1 ; }

//Function of TCP Socket i.e connection-oriented /****************CODE TAKEN FROM CLASS NOTES - CLASS SITE************************/ void receiver::connection_oriented(void) { u_long ResolveAddress(const char*) ; //Calling function to resolve adddress string remoteHost = "127.0.0.1"; //LOCALHOST

int mySocket; sockaddr_in sinRemote;//C defined Structure for socket address u_long address = ResolveAddress(remoteHost.c_str()); // Create a stream socket mySocket = socket(AF_INET, SOCK_STREAM, 0); if (mySocket != -1 ) {

sinRemote.sin_family = AF_INET; sinRemote.sin_addr.s_addr = address; sinRemote.sin_port = htons(portNo); //htons means converting to Network Byte Addressing Mode

int c = bind( mySocket, (sockaddr*)&sinRemote, sizeof(sockaddr_in)); if ( c == -1 ) { cout << "Error connecting to remote process:"<<endl; } }

socklen_t *addressLength = new socklen_t; *addressLength = 100;

while(1) {

//Receiver should never stops

int listenReturn = listen( mySocket, 5); int acceptReturn = accept( mySocket, (sockaddr*)&sinRemote, addressLength );

msgCount = 0 ; while(msgCount < 5) //5 msgs { char *buffer = new char[1000]; for ( int i=0; i<1000; i++ ) { buffer[i] = '\0'; //Deleting contents of buffer } msgCount++ ; int nNewBytes = recv( acceptReturn, buffer, 1000, 0);

cout<<"Message "<<msgCount<<" : "<<buffer<<endl; } }

//printing msg

return ; }

//Function of UDP Socket i.e connection-less /****************CODE TAKEN FROM CLASS NOTES - CLASS SITE************************/ void receiver::connection_less(void) { u_long ResolveAddress(const char*) ; adddress string remoteHost = "127.0.0.1"; int mySocket; sockaddr_in sinRemote; //C defined Structure for socket address //Calling function to resolve //LOCALHOST

u_long address = ResolveAddress(remoteHost.c_str());

// Create a data gram socket mySocket = socket(AF_INET, SOCK_DGRAM, 0); if (mySocket != -1 ) { sinRemote.sin_family = AF_INET; sinRemote.sin_addr.s_addr = address; sinRemote.sin_port = htons(portNo);

int c = bind( mySocket, (sockaddr*)&sinRemote, sizeof(sockaddr_in)); if ( c == -1 ) {

cout << "Error connecting to remote process:"<<endl; return ; } } int fromSize = sizeof(struct sockaddr_in); while(1) { msgCount = 0 ; while(msgCount < 5) { msgCount++ ; char *buffer = new char[1000]; for ( int i=0; i<1000; i++ ) { buffer[i] = '\0'; } //5 msgs //Receiver should never stops

int nNewBytes = recvfrom( mySocket, buffer, 1000, 0, (struct sockaddr *)&sinRemote, (socklen_t*)&fromSize);

cout<<"Message "<<msgCount<<" : "<<buffer<<endl; } }

return ;

/***********CODE TAKEN FROM CLASS NOTES - CLASS SITE********************/ /* Resolve hostname into a usable address */

u_long ResolveAddress(const char *remoteHost) { // Assume ip address (x.x.x.x) was passed u_long address = inet_addr(remoteHost); // remoteHost was not a valid ip address, try DNS resolution if (address == INADDR_NONE) { hostent* hostEntry = gethostbyname(remoteHost); if (hostEntry == 0) { //GetLastErrorMessage("Error"); return INADDR_NONE; } address = *((u_long*)hostEntry->h_addr); }

return address; }

SENDER:
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include<string.h>

#include <iostream> #include <stdio.h> #include <stdlib.h>

using namespace std ;//To use cout #ifndef INADDR_NONE #define INADDR_NONE ((unsigned long)-1) constant INADDR_NONE if not already defined. #endif //This defines a

class sender sender { int portNo ; int msgCount ;

//Class that contains members and methods to implement the

//Private Data Members

public: sender(int x) passed value and 0 to msgCount { portNo = x ; msgCount = 0 ; } //Constructor that initialzes portNO to the

void connection_oriented(void) ; void connection_less(void) ; SOCKET };

//FOR TCP CONNECTION //FOR UDP

int main() { int tempVar ; int connectionType = 0 ; Connection - 1 for TCP or 2 for UDP //for temporary input purpose //to store the value for the type of

cout<<"\nEnter the Port No to be used for connecton:" ; cin>>tempVar ;

if(tempVar < 1024 || tempVar > 65535) { ; cout<<"\nPlease Enter Port No. in proper range (1024 to 65535)!!!\nSystem exting..." //portNo less than 1024 are reserved return 0 ; }

sender obj(tempVar) ; constructor

//creating an object of a class and calling a

cout<<"\nEnter the Type of connection\nPress 1 for TCP (Connection Oreinted) or 2 for UDP (Connection Less):" ; cin>>connectionType ; if(connectionType == 1) obj.connection_oriented() ; user else if(connectionType == 2) obj.connection_less() ; choosed by the user else { cout<<"\nWrong Option selected!!!\nSystem exiting..." ; //Error Msg if any other option is selected return 0 ; } return 1 ; } //Function of TCP Socket i.e connection-oriented //i.e UDP socket is //i.e TCP socket is choosed by the

/****************CODE TAKEN FROM CLASS NOTES - CLASS SITE************************/ void sender::connection_oriented(void) { u_long ResolveAddress(const char*) ; string remoteHost = "127.0.0.1"; int mySocket; //Calling function to resolve adddress //LOCALHOST

u_long address = ResolveAddress(remoteHost.c_str());

mySocket = socket(AF_INET, SOCK_STREAM, 0); if (mySocket != -1 ) {

sockaddr_in sinRemote;

//C defined Structure for socket address

sinRemote.sin_family = AF_INET; sinRemote.sin_addr.s_addr = address; sinRemote.sin_port = htons(portNo); Network Byte Addressing Mode //htons means converting to

if ( connect( mySocket, (sockaddr*)&sinRemote, sizeof(sockaddr_in)) == -1 ) { cout << "Error connecting to remote process\n"; } else { cout<<"valid connection\n"; } } else { cout<<"invalid socket\n"; }

msgCount = 0 ; while(msgCount < 5) { msgCount++ ; string message = "" ; cout<<"Enter Message "<<msgCount<<" : " ; cin>>message ; message += '\n' ; //5 msgs

int sendValue = send( mySocket, message.c_str(), message.length(), 0); if ( sendValue == -1 ) { cout<<"send didn't work\n"; } }

//Function of UDP Socket i.e connection-less /****************CODE TAKEN FROM CLASS NOTES - CLASS SITE************************/ void sender::connection_less(void) { u_long ResolveAddress(const char*) ; adddress string remoteHost = "127.0.0.1"; int mySocket; sockaddr_in sinRemote; //C defined Structure for socket address //Calling function to resolve //LOCALHOST

u_long address = ResolveAddress(remoteHost.c_str());

mySocket = socket(AF_INET, SOCK_DGRAM, 0);

if (mySocket != -1 ) { sinRemote.sin_family = AF_INET; sinRemote.sin_addr.s_addr = address; sinRemote.sin_port = htons(portNo); } else { cout<<"invalid socket\n"; return ; }

msgCount = 0 ; while(msgCount < 5) { msgCount++ ; string message = "" ; //5 msgs

cout<<"Enter Message "<<msgCount<<" : " ; cin>>message ;

message += '\n' ;

int sendValue = sendto( mySocket, message.c_str(), message.length(), 0, (const sockaddr*)&sinRemote, sizeof(sinRemote));

if ( sendValue == -1 ) {

cout<<"send didn't work\n"; } }

/***********CODE TAKEN FROM CLASS NOTES - CLASS SITE********************/ /* Resolve hostname into a usable address */

/* Resolve hostname into a usable address */ u_long ResolveAddress(const char *remoteHost) { // Assume ip address (x.x.x.x) was passed u_long address = inet_addr(remoteHost);

// remoteHost was not a valid ip address, try DNS resolution if (address == INADDR_NONE) { hostent* hostEntry = gethostbyname(remoteHost); if (hostEntry == 0) { //GetLastErrorMessage("Error"); return INADDR_NONE; } address = *((u_long*)hostEntry->h_addr); } return address; }

RPC mechanism for a file transfer across a network

Remote procedure call (RPC) is an Inter-process communication technology that allows a computer program to cause a subroutine or procedure to execute in another address space(commonly on another computer on a shared network) without the programmer explicitly coding the details for this remote interaction. That is, the programmer would write essentially the same code whether the subroutine is local to the executing program, or remote. How RPC Works An RPC is analogous to a function call. Like a function call, when an RPC is made, the calling arguments are passed to the remote procedure and the caller waits for a response to be returned from the remote procedure. Figure shows the flow of activity that takes place during an RPC call between two networked systems. The client makes a procedure call that sends a request to the server and waits. The thread is blocked from processing until either a reply is received, or it times out. When the request arrives, the server calls a dispatch routine that performs the requested service, and sends the reply to the client. After the RPC call is completed, the client program continues. RPC specifically supports network applications.

Fig Remote Procedure Calling Mechanism A remote procedure is uniquely identified by the triple: (program number, version number, procedure number) The program number identifies a group of related remote procedures, each of which has a unique procedure number. A program may consist of one or more versions. Each version consists of a collection of procedures which are available to be called remotely. Version numbers enable multiple versions of an RPC protocol to be available simultaneously. Each version contains a a number of procedures that can be called remotely. Each procedure has a procedure number. RPC Application Development Consider an example:A client/server lookup in a personal database on a remote machine. Assuming that we cannot access the database from the local machine (via NFS). We use UNIX to run a remote shell and execute the command this way. There are some problems with this method:

the command may be slow to execute. You require an login account on the remote machine.

The RPC alternative is to


establish an server on the remote machine that can repond to queries. Retrieve information by calling a query which will be quicker than previous approach.

To develop an RPC application the following steps are needed:


Specify the protocol for client server communication Develop the client program Develop the server program

The programs will be compiled seperately. The communication protocol is achieved by generated stubs and these stubs and rpc (and other libraries) will need to be linked in. Defining the Protocol The easiest way to define and generate the protocol is to use a protocol complier such as rpcgen. For the protocol you must identify the name of the service procedures, and data types of parameters and return arguments. The protocol compiler reads a definitio and automatically generates client and server stubs. rpcgen uses its own language (RPC language or RPCL) which looks very similar to preprocessor directives. rpcgen exists as a standalone executable compiler that reads special files denoted by a .x prefix. So to compile a RPCL file you simply do rpcgen rpcprog.x This will generate possibly four files:

rpcprog_clnt.c -- the client stub rpcprog_svc.c -- the server stub rpcprog_xdr.c -- If necessary XDR (external data representation) filters rpcprog.h -- the header file needed for any XDR filters.

The external data representation (XDR) is an data abstraction needed for machine independent communication. The client and server need not be machines of the same type. Defining Client and Server Application Code We must now write the the client and application code. They must communicate via procedures and data types specified in the Protocol. The service side will have to register the procedures that may be called by the client and receive and return any data required for processing.

The client application call the remote procedure pass any required data and will receive the retruned data. There are several levels of application interfaces that may be used to develop RPC applications. We will briefly disuss these below before exapnading thhe most common of these in later chapters. Compliling and running the application Let us consider the full compilation model required to run a RPC application. Makefiles are useful for easing the burden of compiling RPC applications but it is necessary to understand the complete model before one can assemble a convenient makefile. Assume the the client program is called rpcprog.c, the service program is rpcsvc.c and that the protocol has been defined in rpcprog.x and that rpcgen has been used to produce the stub and filter files: rpcprog_clnt.c, rpcprog_svc.c, rpcprog_xdr.c, rpcprog.h. The client and server program must include (#include "rpcprog.h" You must then:

compile the client code: cc -c rpcprog.c compile the client stub: cc -c rpcprog_clnt.c compile the XDR filter: cc -c rpcprog_xdr.c build the client executable: cc -o rpcprog rpcprog.o rpcprog_clnt.o rpcprog_xdr.c compile the service procedures: cc -c rpcsvc.c compile the server stub: cc -c rpcprog_svc.c build the server executable: cc -o rpcsvc rpcsvc.o rpcprog_svc.o rpcprog_xdr.c

Now simply run the programs rpcprog and rpcsvc on the client and server respectively. The server procedures must be registered before the client can call them. Passing Arbitrary Data Types Data types passed to and received from remote procedures can be any of a set of predefined types, or can be programmer-defined types. RPC handles arbitrary data structures, regardless of different machines' byte orders or structure layout conventions, by always converting them to a standard transfer format called external data representation (XDR) before sending them over the transport. The conversion from a machine representation to XDR is called serializing, and the reverse process is called deserializing. The translator arguments of rpc_call() andrpc_reg() can specify an XDR primitive procedure, like xdr_u_long(), or a programmer-supplied routine that processes a complete argument structure. Argument processing routines must take only two arguments: a pointer to the result and a pointer to the XDR handle. The following XDR Primitive Routines are available:

xdr_int() xdr_netobj() xdr_u_long() xdr_enum() xdr_long() xdr_float() xdr_u_int() xdr_bool() xdr_short() xdr_double() xdr_u_short() xdr_wrapstring() xdr_char() xdr_quadruple() xdr_u_char() xdr_void() The nonprimitive xdr_string(), which takes more than two parameters, is called from xdr_wrapstring(). For an example of a programmer-supplied routine, the structure: struct simple { int a; short b; } simple; contains the calling arguments of a procedure. The XDR routine xdr_simple() translates the argument structure as shown below: #include <rpc/rpc.h> #include "simple.h" bool_t xdr_simple(XDR *xdrsp, struct simple *simplep) { if (!xdr_int(xdrsp, &simplep->a)) return (FALSE); if (!xdr_short(xdrsp, &simplep->b)) return (FALSE); return (TRUE);

} An equivalent routine can be generated automatically by rpcgen . An XDR routine returns nonzero (a C TRUE) if it completes successfully, and zero otherwise. For more complex data structures use the XDR prefabricated routines: xdr_array() xdr_bytes() xdr_reference() xdr_vector() xdr_union() xdr_pointer() xdr_string() xdr_opaque() For example, to send a variable-sized array of integers, it is packaged in a structure containing the array and its length: struct varintarr { int *data; int arrlnth; } arr; Translate the array with xdr_array(), as shown below: bool_t xdr_varintarr(XDR *xdrsp, struct varintarr *arrp)

return(xdr_array(xdrsp, (caddr_t)&arrp->data, (u_int *)&arrp->arrlnth, MAXLEN, sizeof(int), xdr_int)); } The arguments of xdr_array() are the XDR handle, a pointer to the array, a pointer to the size of the array, the maximum array size, the size of each array element, and a pointer to the XDR routine to translate each array element. If the size of the array is known in advance, use xdr_vector() instread as is more efficient: int intarr[SIZE]; bool_t xdr_intarr(XDR *xdrsp, int intarr[]) { return (xdr_vector(xdrsp, intarr, SIZE, sizeof(int), xdr_int)); } XDR converts quantities to 4-byte multiples when serializing. For arrays of characters, each character occupies 32 bits. xdr_bytes() packs characters. It has four parameters similar to the first four parameters of xdr_array(). Null-terminated strings are translated by xdr_string(). It is like xdr_bytes() with no length parameter. On serializing it gets the string length from strlen(), and on deserializing it creates a null-terminated string. xdr_reference() calls the built-in functions xdr_string() and xdr_reference(), which translates pointers to pass a string, and struct simple from the previous examples. An example use of xdr_reference() is as follows: struct finalexample { char *string; struct simple *simplep; } finalexample; bool_t xdr_finalexample(XDR *xdrsp, struct finalexample *finalp) { if (!xdr_string(xdrsp, &finalp->string, MAXSTRLEN)) return (FALSE); if (!xdr_reference( xdrsp, &finalp->simplep, sizeof(struct simple), xdr_simple)) return (FALSE); return (TRUE); } Note that xdr_simple() could have been called here instead of xdr_reference() .

Java RMI mechanism


Helloclient.java
import java.io.*;

import java.rmi.*; public class HelloClient { public static void main(String args[]) { try { int RMIPort; String hostName;

InputStreamReader is = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(is);

System.out.println("Enter the RMI registry port number: "); String portNumber = br.readLine(); RMIPort = Integer.parseInt(portNumber);

String registryURL = "rmi://localhost:"+portNumber+"/hello"; System.out.println(registryURL); HelloInterface h = (HelloInterface)Naming.lookup(registryURL); System.out.print("Enter ur name : "); String abc ; abc = br.readLine() ; String message12 = h.sayHello(abc); System.out.println(message12); } catch (Exception e) {

System.out.println("Exception in HelloClient: "+e); } } }

Helloimpl.java
import java.rmi.*; import java.rmi.server.*;

public class HelloImpl extends UnicastRemoteObject implements HelloInterface { public HelloImpl() throws RemoteException { super(); }

public String sayHello(String name) throws RemoteException {

return "Hello, World! "+name; } }

Hellointerface.java
import java.rmi.*;

public interface HelloInterface extends Remote { public String sayHello(String name) throws java.rmi.RemoteException; }

helloserver.java
import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.Registry; import java.rmi.registry.LocateRegistry; import java.net.*; import java.io.*;

public class HelloServer { public static void main(String args[]) { InputStreamReader is = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(is); String portNum, registryURL;

try { System.out.println("Enter the RMIregistry port number: "); portNum = (br.readLine()).trim(); int RMIPortNum = Integer.parseInt(portNum); startRegistry(RMIPortNum); HelloImpl exportedObj = new HelloImpl(); registryURL = "rmi://localhost:"+portNum+"/hello"; Naming.rebind(registryURL,exportedObj); listRegistry(registryURL); System.out.println("server registeres. Registy in the registry");

System.out.println("Hello serever Ready:");

} catch (Exception e) { System.out.println("Exception"+e); } }

private static void startRegistry(int RMIPortNum) throws RemoteException { try { Registry registry = LocateRegistry.getRegistry(RMIPortNum); registry.list(); System.out.println("startRegistry Successful") ; } catch (RemoteException ee) { System.out.println("RMI registry not found on port: "+RMIPortNum); Registry registry = LocateRegistry.createRegistry(RMIPortNum); System.out.println("RMI registry created on port: "+RMIPortNum); } }

private static void listRegistry(String registryURL) throws RemoteException,MalformedURLException

{ System.out.println("Registry: "+registryURL + " contains: "); String [] names = Naming.list(registryURL);

for(int i=0; i<names.length; i++) System.out.println(names[i]); }

Balanced Sliding Window Protocol


Definitions Two processes, p and q, each sending an infinite array of words to the other For Process p: inp: An infinite array of words to be sent to process q

outp: An infinite array of words being received from process q Initially for all i, out outp[i] = udef Sp: The lowest numbered word that p still expects to receive from q

At any time, p has already written out outp[0] [through out outp[i]

Required Properties Safe delivery In every reachable configuration of the protocol outp [0 sp - 1] = inq[0 sp - 1] and outq [0 sq - 1] = inp[0 sq - 1]

Eventual delivery For every integer k 0, a configuration with , sp k and and sq k is eventually reached

The protocol The packet, < pack, w, i > transmits the word w = inp[i] to q. The processes use constants ip and iq as follows: - Process p can send the word w=inp[i] (as the packet, <pack, w, i>) only after storing all the words outp[0] through outp[i lp], that is, i < sp + lp.

- When p receives < pack, w, i >, retransmission of words from inp [0] through inp [ i lq ] is no longer necessary. The Sliding Window

The Protocol Invariant i < sp : outp[i] udef i < sq : outq[i] udef < pack, w, i > < pack, w, i > outp[i] udef outq[i] udef ap sq aq sp Qp Qq w = inq[i] w = inp[i] outp[i] = inq[i] outq[i] = inp[i] ( i < sq + lq ) ( i < sp + lp ) ( ap > i lq ) ( aq > i lp )

Result Safety: The protocol satisfies the requirement of safe delivery

Liveness: P implies sp lq ap sq aq + lp sp + lp P implies that the sending of <pack, inp[sq], sq> by p or the sending of <pack, inq[sp], sp> by q is applicable. Hence no deadlock is possible

The protocol satisfies the requirement of eventual delivery

CORBA mechanism
CORBA

CORBA, the Common Object Request Broker Architecture, is a powerful tool for distributed programming. It is a language-independent standard specified by the Object Management Group (OMG). Many CORBA implementations, both free and commercial, exist for a wide variety of languages (e.g., C, C++, Java, Perl, Python, and Smalltalk). CORBA allows communication between software written in any programming language running on any operating system on any hardware architecture. It handles all serialization and de-serialization of objects and method parameters so that programmers do not have to worry about endian issues and other system incompatibilities.

Overview
CORBA is a mechanism in software for normalizing the method-call semantics between application objects that reside either in the same address space (application) or remote address space (same host, or remote host on a network). Version 1.0 was released in October 1991. CORBA uses an interface definition language (IDL) to specify the interfaces that objects will present to the outside world. CORBA then specifies a mapping from IDL to a specific implementation language like C+ + or Java. Standard mappings exist for Ada, C, C+ +, Lisp, Ruby,Smalltalk, Java, COBOL, PL/I and Python. There are also non-standard mappings for Perl, Visual Basic, Erlang, and Tcl implemented by object request brokers (ORBs) written for those languages. The CORBA specification dictates that there shall be an ORB through which the application interacts with other objects. In practice, the application simply initializes the ORB, and accesses an internal Object Adapter which maintains such issues as reference counting, object (and reference) instantiation policies, object lifetime policies, etc. The Object Adapter is used to register instances of the generated code classes. Generated code classes are the result of compiling the user IDL code, which translates the high-level interface definition into an OS- and language-specific class base for use by the user application. This step is necessary in order to enforce the CORBA semantics and provide a clean user process for interfacing with the CORBA infrastructure. Some IDL language mappings are "more hostile" than others. For example, due to the very nature of Java, the IDL-Java Mapping is rather straightforward and makes usage of CORBA very simple in a Java application. The C++ mapping is not trivial, but accounts for all the features of CORBA (e.g.

exception handling). The C mapping is even stranger (since C is not an object-oriented language), but it does make sense and properly handles the RPC semantics. A language mapping requires the developer ("user" in this case) to create some IDL code that represents the interfaces to his objects. Typically, a CORBA implementation comes with a tool called an IDL compiler which converts the user's IDL code into some language-specific generated code. A traditional compiler then compiles the generated code to create the linkable-object files for the application. This diagram illustrates how the generated code is used within the CORBA infrastructure:

Illustration of the autogeneration of the infrastructure code from an interface defined using the CORBA IDL

This figure illustrates the high-level paradigm for remote interprocess communications using CORBA. Issues not addressed here, but that are accounted-for in the CORBA specification include: data typing, exceptions, network protocol, communication timeouts, etc. For example: Normally the server side has the Portable Object Adapter (POA) that redirects calls either to the local servants or (to balance the load) to the other servers. Also, both server and client parts often have interceptors that are described below. Issues CORBA (and thus this figure) does not address, but that all distributed systems must address: object lifetimes, redundancy/fail-over, naming semantics (beyond a simple name), memory management, dynamic load balancing, separation of model between display/data/control semantics, etc. In addition to providing users with a language and a platform-neutral remote procedure call specification, CORBA defines commonly needed services such as transactions and security, events, time, and other domain-specific interface models.

Interoperability:
Different objects, written in different programming languages, can interact with each other by using CORBA. ORB (Object Request Broker) provides common services to objects written in different languages. For example, Visibroker provides a common nameserver for C++/Java Technology CORBA objects. In some cases, two different ORBs may need to interact, such as a C++; CORBA object that uses the ORBIT ORB; to talk to a Java Technology object that uses the Java Technology ORB. CORBA objects use IOR (Interoperable Object Reference) to communicate. ORB makes the IOR transparent to the programmers. In general, for object A to talk to object B, A uses the IOR of object B. IOR is the object's address that other objects can refer to. CORBA specification provides detailed information on the fields of the IOR. There are three ways to obtain the IOR of an object: 1. string_to_object

You can obtain a stringified object reference (Email, disk, file, etc.) and use the string_to_object call to convert it into an object reference.

2.

resolve_initial_references services.

You can use this call to get IORs to a small set of well known For example, to use the nameserver in a single ORB environment, you make the following call:

resolved_initial_references("NameServer");
The ORB returns the IOR to the name server. 3. Lookup

You can look up the IOR in the naming service. Once you have the IOR, you can convert it into an active object reference by using the string_to_object call and then invoke methods on it. In an ORB environment that supports binding for multiple languages (e.g. Visibroker and ORBacus support C++ and the Java Programming Language), the ORB provides an uniform naming service to objects of all the languages it supports. The naming service holds information about all the objects in the ORB. In a single ORB environment, the naming service is transparent. If you want objects located on two different ORBs to interact and they share a common nameserver, you would use the Lookup method. However, if the objects are located on different ORBs, then the client needs to get the IOR from the server objects. There are two ways to get the IOR from the server objects: 1. You can use a stringified reference for Object B. B writes its reference in a location that is accessible by Object A (such as on a shared NFS drive). A then performs the following:

try BufferedRead

{ in= new BufferedReader( new FileReader( "account.ref" ) ); ref = in.readLine(); } catch( IOException ex) { System.out.println( "Could not open file 'account.ref'" ); System.exit( -1 ); } org.omg.CORBA.Object obj = orb.string_to_object( ref );

2. You obtain an IOR for the nameserver of the ORB in which the other object resides. You then use the
nameserver to get the reference to Object B. To get the IOR for the nameserver of the target ORB, you can use well known (published) IORs or stringified IOR. For example, you can use the ORBNamingIOR argument to provide the NamingService IOR to the calling object.

Here is an example of Interoperability between the Java Technology 2 ORB and Mico, a GPLd 2.3 CORBA compliant ORB. This section explains how a C++ object talks to a Java Technology object, and more specifically how a C++ client makes calls to a JavaTechnology Server. The easiest way is for the Java Technology object to store its IOR in a publicly available location. Then the C++ client reads it and requests services from the Java Technology object. Another method is to use the name server. For example, in one window, you type:

tnameserv -ORBInitialPort 1050


tnameserv prints out its IOR to the terminal. In another window, you run the Java object:

java HelloServer -ORBInitialPort 1050


Now you can run the C++ client:

./client -ORBNoCodeSets -ORBNamingIOR IOR:010000...


You do not need to change any of the codes. Here are some of the benefits of using nameserver instead of using plain IORs: 1. 2. You can dynamically discover services and bind to them. You need only one IOR, that of the NameServer, to make calls to all objects. 3. You can browse through all the names that are in the NameServer and choose the calls you need to make.

You might also like