43
Lamport’s 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.

Distributed System Lab

Embed Size (px)

Citation preview

Page 1: Distributed System Lab

Lamport’s 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.

Page 2: Distributed System Lab

e11 → e12   , e12 → e22  e21 → e13   , e14 || e24

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 then C(a) < C(b) but C(a) < C(b) does not necessarily imply a → b

Page 3: Distributed System Lab

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

Raymond’s Tree-based algorithm Simple 2 process algorithm

Page 4: Distributed System Lab

Lamport’s 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 Pi’s request from its queue.

Ricartand Agrawala’sAlgorithm

•Similar to Lamport’sAlgorithm 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 Pk’s request it sends a timestamped reply else it keeps Pi’s 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.

Page 5: Distributed System Lab

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) //This defines a constant INADDR_NONE if not already defined.

#endif

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

{

int portNo ;

int msgCount ; //Private Data Members

public:

Page 6: Distributed System Lab

receiver(int x) //Constructor that initialzes portNO to the passed value and 0 to msgCount

{

portNo = x ;

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

}

void connection_oriented(void) ; //FOR TCP CONNECTION

void connection_less(void) ; //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):" ;

Page 7: Distributed System Lab

cin>>connectionType ;

if(connectionType == 1)

obj.connection_oriented() ; //i.e TCP socket is choosed by the user

else if(connectionType == 2)

obj.connection_less() ; //i.e UDP socket is 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

/****************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 ) {

Page 8: Distributed System Lab

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);

Page 9: Distributed System Lab

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*) ; //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 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 ) {

Page 10: Distributed System Lab

cout << "Error connecting to remote process:"<<endl;

return ;

}

}

int fromSize = sizeof(struct sockaddr_in);

while(1) //Receiver should never stops

{

msgCount = 0 ;

while(msgCount < 5) //5 msgs

{

msgCount++ ;

char *buffer = new char[1000];

for ( int i=0; i<1000; i++ ) {

buffer[i] = '\0';

}

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 */

Page 11: Distributed System Lab

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>

Page 12: Distributed System Lab

using namespace std ;//To use cout

#ifndef INADDR_NONE

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

#endif

class sender //Class that contains members and methods to implement the sender

{

int portNo ;

int msgCount ; //Private Data Members

public:

sender(int x) //Constructor that initialzes portNO to the passed value and 0 to msgCount

{

portNo = x ;

msgCount = 0 ;

}

void connection_oriented(void) ; //FOR TCP CONNECTION

void connection_less(void) ; //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

Page 13: Distributed System Lab

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) ; //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() ; //i.e UDP socket is 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

Page 14: Distributed System Lab

/****************CODE TAKEN FROM CLASS NOTES - CLASS SITE************************/

void sender::connection_oriented(void)

{

u_long ResolveAddress(const char*) ; //Calling function to resolve adddress

string remoteHost = "127.0.0.1"; //LOCALHOST

int mySocket;

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); //htons means converting to Network Byte Addressing Mode

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";

}

Page 15: Distributed System Lab

msgCount = 0 ;

while(msgCount < 5) //5 msgs

{

msgCount++ ;

string message = "" ;

cout<<"Enter Message "<<msgCount<<" : " ;

cin>>message ;

message += '\n' ;

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*) ; //Calling function to resolve adddress

string remoteHost = "127.0.0.1"; //LOCALHOST

int mySocket;

sockaddr_in sinRemote; //C defined Structure for socket address

Page 16: Distributed System Lab

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) //5 msgs

{

msgCount++ ;

string message = "" ;

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 ) {

Page 17: Distributed System Lab

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

Page 18: Distributed System Lab

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).

Page 19: Distributed System Lab

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

Page 20: Distributed System Lab

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

Page 21: Distributed System Lab

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:

Page 22: Distributed System Lab

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() .

Page 23: Distributed System Lab

‘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 ;

Page 24: Distributed System Lab

abc = br.readLine() ;

String message12 = h.sayHello(abc);

System.out.println(message12);

}

catch (Exception e)

{

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

}

}

}

Page 25: Distributed System Lab

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;

}

}

Page 26: Distributed System Lab

Hellointerface.java

import java.rmi.*;

public interface HelloInterface extends Remote

{

public String sayHello(String name) throws java.rmi.RemoteException;

}

Page 27: Distributed System Lab

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);

Page 28: Distributed System Lab

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

Page 29: Distributed System Lab

{

System.out.println("Registry: "+registryURL + " contains: ");

String [] names = Naming.list(registryURL);

for(int i=0; i<names.length; i++)

System.out.println(names[i]);

}

}

Page 30: Distributed System Lab

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 receivefrom 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

Page 31: Distributed System Lab

The Protocol Invariant

P ≡ i < sp : outp[i] ≠ udef

i < sq : outq[i] ≠ udef

< pack, w, i > Qp w = inq[i] ( i < sq + lq )

< pack, w, i > Qq w = inp[i] ( i < sp + lp )

outp[i] ≠ udef outp[i] = inq[i] ( ap > i – lq )

outq[i] ≠ udef outq[i] = inp[i] ( aq > i – lp )

ap ≤ sq

aq ≤ sp

Page 32: Distributed System Lab

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

Page 33: Distributed System Lab

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

Page 34: Distributed System Lab

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.

Page 35: Distributed System Lab

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

You can use this call to get IORs to a small set of well known services. 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.

Page 36: Distributed System Lab

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. You can dynamically discover services and bind to them.2. 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.