Distributed System Lab
Distributed System Lab
7th Semester
SUBMITTED TO
3. Implement RPC.
4. Implement RMI.
Client-Side Code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main()
{
int client_socket;
struct sockaddr_in server_address;
// Create socket
if ((client_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("Error creating socket");
exit(EXIT_FAILURE);
}
// Set up server address struct
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr("127.0.0.1"); // Connect to
localhost
server_address.sin_port = htons(PORT);
// Connect to server
if (connect(client_socket, (struct sockaddr *)&server_address,
sizeof(server_address)) == -1)
{
perror("Error connecting to server");
exit(EXIT_FAILURE);
}
printf("Connected to server on port %d\n", PORT);
// Communication with server
char buffer[BUFFER_SIZE];
int bytes_received;
while (1)
{
// Get user input
printf("Enter message (type 'exit' to quit): ");
fgets(buffer, BUFFER_SIZE, stdin);
// Send data to server
send(client_socket, buffer, strlen(buffer), 0);
if (strcmp(buffer, "exit\n") == 0)
{
break;
}
// Receive response from server
bytes_received = recv(client_socket, buffer, BUFFER_SIZE, 0);
if (bytes_received <= 0)
{
perror("Error receiving data");
break;
}
buffer[bytes_received] = '\0'; // Null-terminate the received data
// Print received data
printf("Received from server: %s\n", buffer);
}
// Close socket
close(client_socket);
return 0;
}
Server-Side Code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main()
{
int server_socket, client_socket;
struct sockaddr_in server_address, client_address;
socklen_t client_address_len = sizeof(client_address);
// Create socket
if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("Error creating socket");
exit(EXIT_FAILURE);
}
// Set up server address struct
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = INADDR_ANY;
server_address.sin_port = htons(PORT);
// Bind the socket
if (bind(server_socket, (struct sockaddr *)&server_address,
sizeof(server_address)) == -1)
{
perror("Error binding");
exit(EXIT_FAILURE);
}
// Listen for incoming connections
if (listen(server_socket, 5) == -1)
{
perror("Error listening");
exit(EXIT_FAILURE);
}
printf("Server listening on port %d...\n", PORT);
// Accept connection from client
if ((client_socket = accept(server_socket, (struct sockaddr
*)&client_address, &client_address_len)) == -1)
{
perror("Error accepting connection");
exit(EXIT_FAILURE);
}
printf("Connection accepted from %s:%d\n",
inet_ntoa(client_address.sin_addr), ntohs(client_address.sin_port));
// Communication with client
char buffer[BUFFER_SIZE];
int bytes_received;
while (1)
{
// Receive data from client
bytes_received = recv(client_socket, buffer, BUFFER_SIZE, 0);
if (bytes_received <= 0)
{
perror("Error receiving data");
break;
}
buffer[bytes_received] = '\0'; // Null-terminate the received data
// Print received data
printf("Received from client: %s\n", buffer);
// Send a response to the client
send(client_socket, buffer, strlen(buffer), 0);
}
// Close sockets
close(client_socket);
close(server_socket);
return 0;
}
Output
Experiment 2
a. Pipes
// Pipes allow communication between two related processes.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int pipe_fd[2];
char buffer[20];
if (pipe(pipe_fd) == -1)
{
perror("Pipe creation failed");
return 1;
}
pid_t pid = fork();
if (pid == -1)
{
perror("Fork failed");
return 1;
}
if (pid == 0)
{
close(pipe_fd[1]);
read(pipe_fd[0], buffer, sizeof(buffer));
printf("Child received: %s\n", buffer);
close(pipe_fd[0]);
}
else
{
close(pipe_fd[0]);
write(pipe_fd[1], "Hello from parent", 17);
close(pipe_fd[1]);
}
return 0;
}
Output
b. Shared Memory
e. Sockets
// Sockets provide communication between processes over a network. For
interprocess //communication on the same machine, Unix domain sockets are
commonly used.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
int main()
{
int server_socket, client_socket;
socklen_t server_len, client_len;
struct sockaddr_un server_address, client_address;
// Create a socket
server_socket = socket(AF_UNIX, SOCK_STREAM, 0);
// Set up the server address
server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, "ipc_socket");
server_len = sizeof(server_address);
// Bind the socket to a name
bind(server_socket, (struct sockaddr *)&server_address, server_len);
// Listen for connections
listen(server_socket, 5);
// Accept a connection
printf("Server waiting for a connection...\n");
client_len = sizeof(client_address);
client_socket = accept(server_socket, (struct sockaddr *)&client_address,
&client_len);
printf("Server accepted a connection.\n");
// Communicate with the client (send and receive data)
// Close sockets
close(client_socket);
close(server_socket);
// Remove the socket file
unlink("ipc_socket");
return 0;
}
Output
Experiment 3
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
int main()
{
int client_socket;
struct sockaddr_in server_addr;
// Create socket
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket == -1)
{
perror("Error creating socket");
exit(EXIT_FAILURE);
}
// Set up server address structure
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8888);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// Connect to the server
if (connect(client_socket, (struct sockaddr *)&server_addr,
sizeof(server_addr)) == -1)
{
perror("Error connecting to server");
close(client_socket);
exit(EXIT_FAILURE);
}
printf("Connected to server\n");
// Send operation code to the server
int operation = 1; // 1 for addition
send(client_socket, &operation, sizeof(operation), 0);
if (operation == 1)
{ // Addition
// Send data to the server
int a = 3, b = 4;
send(client_socket, &a, sizeof(a), 0);
send(client_socket, &b, sizeof(b), 0);
// Receive the result from the server
int result;
recv(client_socket, &result, sizeof(result), 0);
printf("Result received from server: %d\n", result);
}
// Close the socket
close(client_socket);
return 0;
}
Server-Side Code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
// Remote procedure: Addition
int add(int a, int b)
{
return a + b;
}
int main()
{
int server_socket, client_socket;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len = sizeof(client_addr);
// Create socket
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1)
{
perror("Error creating socket");
exit(EXIT_FAILURE);
}
// Set up server address structure
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8888);
server_addr.sin_addr.s_addr = INADDR_ANY;
// Bind the socket
if (bind(server_socket, (struct sockaddr *)&server_addr,
sizeof(server_addr)) == -1)
{
perror("Error binding");
close(server_socket);
exit(EXIT_FAILURE);
}
// Listen for incoming connections
if (listen(server_socket, 5) == -1)
{
perror("Error listening");
close(server_socket);
exit(EXIT_FAILURE);
}
printf("Server listening on port 8888...\n");
// Accept a connection from the client
client_socket = accept(server_socket, (struct sockaddr *)&client_addr,
&client_addr_len);
if (client_socket == -1)
{
perror("Error accepting connection");
close(server_socket);
exit(EXIT_FAILURE);
}
printf("Connection accepted from %s:%d\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
// Receive data from the client
int operation;
recv(client_socket, &operation, sizeof(operation), 0);
if (operation == 1)
{ // Addition
int a, b, result;
recv(client_socket, &a, sizeof(a), 0);
recv(client_socket, &b, sizeof(b), 0);
// Call the remote procedure
result = add(a, b);
// Send the result back to the client
send(client_socket, &result, sizeof(result), 0);
}
// Close sockets
close(client_socket);
close(server_socket);
return 0;
}
Output
Experiment 4
Client
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8080
// Remote Object definition
typedef struct
{
int value1;
int value2;
} RemoteObject;
int add(int a, int b)
{
return a + b;
}
int main()
{
int client_socket;
struct sockaddr_in server_addr;
// Create socket
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket == -1)
{
perror("Error creating socket");
exit(EXIT_FAILURE);
}
// Set up server address structure
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// Connect to the server
if (connect(client_socket, (struct sockaddr *)&server_addr,
sizeof(server_addr)) == -1)
{
perror("Error connecting to server");
close(client_socket);
exit(EXIT_FAILURE);
}
printf("Connected to server\n");
// Receive the remote object from the server
RemoteObject remoteObject;
recv(client_socket, &remoteObject, sizeof(RemoteObject), 0);
// Invoke the remote method
int result = add(remoteObject.value1, remoteObject.value2);
printf("Result of remote method invocation: %d\n", result);
// Close the socket
close(client_socket);
return 0;
}
Server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8080
// Remote Object definition
typedef struct
{
int (*add)(int, int);
} RemoteObject;
// Remote method implementation
int add(int a, int b)
{
return a + b;
}
int main()
{
int server_socket, client_socket;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len = sizeof(client_addr);
// Create socket
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1)
{
perror("Error creating socket");
exit(EXIT_FAILURE);
}
// Set up server address structure
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = INADDR_ANY;
// Bind the socket
if (bind(server_socket, (struct sockaddr *)&server_addr,
sizeof(server_addr)) == -1)
{
perror("Error binding");
close(server_socket);
exit(EXIT_FAILURE);
}
// Listen for incoming connections
if (listen(server_socket, 5) == -1)
{
perror("Error listening");
close(server_socket);
exit(EXIT_FAILURE);
}
printf("Server listening on port %d...\n", PORT);
// Accept a connection from the client
client_socket = accept(server_socket, (struct sockaddr *)&client_addr,
&client_addr_len);
if (client_socket == -1)
{
perror("Error accepting connection");
close(server_socket);
exit(EXIT_FAILURE);
}
printf("Connection accepted from %s:%d\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
// Initialize the remote object
RemoteObject remoteObject;
remoteObject.add = add;
// Send the remote object to the client
send(client_socket, &remoteObject, sizeof(RemoteObject), 0);
// Close sockets
close(client_socket);
close(server_socket);
return 0;
}
Experiment 5
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int process_id;
int timestamp;
} Event;
void updateTimestamp(int *logical_clock, int new_timestamp)
{
*logical_clock = (new_timestamp > *logical_clock) ? new_timestamp + 1 :
*logical_clock + 1;
}
void simulateLamportClocks()
{
int logical_clock_A = 0;
int logical_clock_B = 0;
// Event for Process A
Event event_A1 = {1, logical_clock_A};
printf("Event A1 - Process %d, Logical Clock: %d\n", event_A1.process_id,
event_A1.timestamp);
// Update logical clock for Process A
updateTimestamp(&logical_clock_A, event_A1.timestamp);
// Event for Process B
Event event_B1 = {2, logical_clock_B};
printf("Event B1 - Process %d, Logical Clock: %d\n", event_B1.process_id,
event_B1.timestamp);
// Update logical clock for Process B
updateTimestamp(&logical_clock_B, event_B1.timestamp);
// Event for Process A
Event event_A2 = {1, logical_clock_A};
printf("Event A2 - Process %d, Logical Clock: %d\n", event_A2.process_id,
event_A2.timestamp);
// Update logical clock for Process A
updateTimestamp(&logical_clock_A, event_A2.timestamp);
// Event for Process B
Event event_B2 = {2, logical_clock_B};
printf("Event B2 - Process %d, Logical Clock: %d\n", event_B2.process_id,
event_B2.timestamp);
// Update logical clock for Process B
updateTimestamp(&logical_clock_B, event_B2.timestamp);
}
int main()
{
simulateLamportClocks();
return 0;
}
Experiment 8
Problem Statement: Simulate Mattern and Fidge vector clocks for ordering events.
Answer: Vector Clock is an algorithm that generates partial ordering of events and detects
causality violations in a distributed system. We can implement it in C as follows:
#include <stdio.h>
#include <stdlib.h>
#define NUM_PROCESSES 3
typedef struct
{
int process_id;
int vector_clock[NUM_PROCESSES];
} Event;
void updateVectorClock(int *vector_clock, int process_id, int new_timestamp)
{
vector_clock[process_id] = new_timestamp + 1;
}
void simulateMatternFidgeVectorClocks()
{
int vector_clock_A[NUM_PROCESSES] = {0};
int vector_clock_B[NUM_PROCESSES] = {0};
int vector_clock_C[NUM_PROCESSES] = {0};
// Event for Process A
Event event_A1 = {0, {0}};
printf("Event A1 - Process %d, Vector Clock: [%d, %d, %d]\n",
event_A1.process_id, event_A1.vector_clock[0],
event_A1.vector_clock[1], event_A1.vector_clock[2]);
// Update vector clock for Process A
updateVectorClock(vector_clock_A, event_A1.process_id,
event_A1.vector_clock[0]);
// Event for Process B
Event event_B1 = {1, {0}};
printf("Event B1 - Process %d, Vector Clock: [%d, %d, %d]\n",
event_B1.process_id, event_B1.vector_clock[0],
event_B1.vector_clock[1], event_B1.vector_clock[2]);
// Update vector clock for Process B
updateVectorClock(vector_clock_B, event_B1.process_id,
event_B1.vector_clock[1]);
// Event for Process C
Event event_C1 = {2, {0}};
printf("Event C1 - Process %d, Vector Clock: [%d, %d, %d]\n",
event_C1.process_id, event_C1.vector_clock[0],
event_C1.vector_clock[1], event_C1.vector_clock[2]);
// Update vector clock for Process C
updateVectorClock(vector_clock_C, event_C1.process_id,
event_C1.vector_clock[2]);
Event event_A2 = {0, {0}};
printf("Event A2 - Process %d, Vector Clock: [%d, %d, %d]\n",
event_A2.process_id, event_A2.vector_clock[0],
event_A2.vector_clock[1], event_A2.vector_clock[2]);
// Update vector clock for Process A
updateVectorClock(vector_clock_A, event_A2.process_id,
event_A2.vector_clock[0]);
// Event for Process B
Event event_B2 = {1, {0}};
printf("Event B2 - Process %d, Vector Clock: [%d, %d, %d]\n",
event_B2.process_id, event_B2.vector_clock[0],
event_B2.vector_clock[1], event_B2.vector_clock[2]);
// Update vector clock for Process B
updateVectorClock(vector_clock_B, event_B2.process_id,
event_B2.vector_clock[1]);
// Event for Process C
Event event_C2 = {2, {0}};
printf("Event C2 - Process %d, Vector Clock: [%d, %d, %d]\n",
event_C2.process_id, event_C2.vector_clock[0],
event_C2.vector_clock[1], event_C2.vector_clock[2]);
// Update vector clock for Process C
updateVectorClock(vector_clock_C, event_C2.process_id,
event_C2.vector_clock[2]);
}
int main()
{
simulateMatternFidgeVectorClocks();
return 0;
}
Output
Experiment 9
Problem Statement: Detect whether there is a Dead lock in a given wait for graph.
Answer: Since the maximum out-degree of a node in a WFG for the single resource model
can be 1, the presence of a cycle in the WFG shall indicate that there is a deadlock. We can
implement it in C as follows
#include <stdio.h>
#include <stdlib.h>
#define MAX_PROCESSES 100
// Function to perform DFS
int isCyclicUtil(int graph[MAX_PROCESSES][MAX_PROCESSES], int process, int
visited[MAX_PROCESSES], int recursionStack[MAX_PROCESSES], int numProcesses)
{
if (!visited[process])
{
visited[process] = 1;
recursionStack[process] = 1;
for (int i = 0; i < numProcesses; i++)
{
if (graph[process][i])
{
if (!visited[i] && isCyclicUtil(graph, i, visited,
recursionStack, numProcesses))
{
return 1;
}
else if (recursionStack[i])
{
return 1;
}
}
}
}
recursionStack[process] = 0;
return 0;
}
// Function to check for deadlock using DFS
int isDeadlock(int graph[MAX_PROCESSES][MAX_PROCESSES], int numProcesses)
{
int visited[MAX_PROCESSES] = {0};
int recursionStack[MAX_PROCESSES] = {0};
for (int i = 0; i < numProcesses; i++)
{
if (isCyclicUtil(graph, i, visited, recursionStack, numProcesses))
{
return 1; // Deadlock detected
}
}
return 0; // No deadlock
}
int main()
{
int numProcesses;
printf("Enter the number of processes: ");
scanf("%d", &numProcesses);
int graph[MAX_PROCESSES][MAX_PROCESSES];
printf("Enter the wait-for graph adjacency matrix (1 for edge, 0 for no
edge):\n");
for (int i = 0; i < numProcesses; i++)
{
for (int j = 0; j < numProcesses; j++)
{
scanf("%d", &graph[i][j]);
}
}
if (isDeadlock(graph, numProcesses))
{
printf("Deadlock detected!\n");
}
else
{
printf("No deadlock.\n");
}
return 0;
}
Output
Experiment 10