Assignment 

Client Server Processes

Choose one of these two options:
A. IPC using FIFO
B. Sockets and Client-Server

In this lab, you will be required to write two C routines which talk to each other through
a communications channel. You can Choose to either implement the code using FIFOs
or through the use of sockets. In the description below these will be referred to
generically as “the communications channel” or just “the channel”.
The objective is to create a client-server environment to copy the contents of a file from
the server to the client. Your client code will run as a single process on the computer.
Your server code must also be run as a separate process on the same computer. No
fork() or exec() calls are required for this lab.
Your client code, once connected, should send a message to the server. The message
should contain a filename. The filename should be supplied to the client as a command
line parameter. The server should then open the file and copy it through the channel to
the client. Each “packet” of information sent must use a structure that contains both
the number of bytes being transferred as well as the data stream.
The client code should then accept the data buffer through the channel and write the
identified number of bytes to a local filename. The data exchange should happen in
fixed block sizes, say 1024 bytes. When the server sends a packet to the client
containing fewer than the expected packet size, your code can assume the server has
hit the end of file.
Both the server and client should close their copy of the file. The client should then send
a disconnect message to the server and exit.

  1. IPC using FIFO
    Your client code should use two FIFOs. One for client-to-server transfers and one for
    server-to-client messages. The client must check for the presence of both FIFOs before
    it accepts user input. You can use the access() function from unistd.h to check for the
    presence of the FIFOs. If either of the FIFOs is missing, a message should be displayed
    as to which is missing, then the code should exit. You can’t run a client if the FIFOs
    don’t exist to act as communication channels to the server. Your testing logs should
    show both scenarios.
    Your server code, which you should run first, should check for the existence of the two
    If any of them is missing, the server code should create them. The server code
    should then wait for a message to arrive from the client, process it and return content of
    the requested file back to the client. The server should remain running waiting for a
    different client to connect.
    Ensure that once you are done running your code that you kill the server process. Either
    stop it using the kill command line verb (if you created a background process using &)
    or by pressing CTRL-C in the terminal window it is running under. The server should
    remain running just in case a different client connects at a later time.
  2. B) Internet Sockets
    This lab is essentially a socket implementation of the FIFO process described above. In
    this lab, you will be required to write two C routines which talk to each other through a
    proper TCP/IP sockets. The objective is to create a real client-server environment to
    copy the contents of a file from the server to the client. Your client code will run as a
    single process on the computer. You server code must be run as a separate process on
    the same computer.
    Your client code should check for the presence of the server and your code’s ability to
    If the client cannot connect, it should exit gracefully.
    Your server code should create and listen on a proper TCP/IP socket. The server code
    should then wait for a message to arrive from the client, process it and return content of
    the requested file back to the client. Once the file is transferred, your server code shouldgracefully hang up the connection. The server should remain running waiting for adifferent client to connect.

Ensure that one you are done running your code that you kill the server process. Either
stop it using the kill command line verb (if you created a background process using &)
or by pressing CTRL-C in the terminal window it is running under.

    Solution 

client.c 

#include <sys/socket.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <string.h>

#include <unistd.h>

#include <limits.h>

#define PORT 50001

#define HOST “127.0.0.1” /* the server is on the same host */

/* a data structure used to communicate between server and client. */

struct message {

size_t size;

char data[1024];

};

/* receive / send via the message channel */

intrecv_message(int cl, struct message *m);

intsend_message(int cl, struct message *m);

/* This function connects with server: create socket, connect

* The socket file id is returned if succeed. */

intconnect_server();

/* This function copies the file from the socket and saves into

* the out file stream. */

void copy(intsk, const char *filename, FILE *out);

int main(intargc, char *argv[]) {

intsk;

char path[PATH_MAX];

FILE *file;

/* make sure the path of the requested file is specified.

* the path should be a full path or a relative path on server. */

if (argc != 2) {

fprintf(stderr, “Usage: %s path\n”, argv[0]);

return -1;

}

sprintf(path, “%s_local_clone”, argv[1]);

printf(“COPY %s –> %s\n”, argv[1], path);

/* connect the server */

sk = connect_server();

/* open a local file to save the file received from the server */

file = fopen(path, “w”);

if (file == NULL) {

fprintf(stderr, “could not write into file %s [%d]\n”, path, errno);

close(sk);

return -1;

}

/* receive the file from the server */

copy(sk, argv[1], file);

fclose(file);

close(sk);

return 0;

}

intconnect_server() {

intsk;

structsockaddr_inaddr;

/* create the TCP socket to connect with server */

if ((sk = socket(AF_INET, SOCK_STREAM, 0)) < 0) {

fprintf(stderr, “could not create client socket [%d]\n”, errno);

exit(errno);

}

/* initialize the server address */

memset(&addr, 0, sizeof(addr));

addr.sin_family = AF_INET;

inet_pton(AF_INET, HOST, &addr.sin_addr);

addr.sin_port = htons(PORT);

/* connect with the server */

if (connect(sk, (structsockaddr *)&addr, sizeof(addr)) < 0) {

fprintf(stderr, “could not connect the server [%d]\n”, errno);

close(sk);

exit(errno);

}

returnsk;

}

void copy(intsk, const char *filename, FILE *out) {

struct message msg;

/* send the filename to the server */

msg.size = strlen(filename);

memcpy(msg.data, filename, msg.size);

if (send_message(sk, &msg) < 0) {

fprintf(stderr, “could not send the path of requested file [%d]\n”, errno);

return;

}

/* display the requested file */

msg.data[msg.size] = ‘\0’;

printf(“REQUESTED: %s\n”, msg.data);

/* read until the end of the TCP channel */

while (recv_message(sk, &msg) > 0) {

if (fwrite(msg.data, 1, msg.size, out) <msg.size) {

fprintf(stderr, “could not save file [%d]\n”, errno);

break;

}

}

printf(“RECEIVED\n”);

}

/* this two functions are shared by server and client to communicate

* with each other through the TCP channel. */

intrecv_message(int cl, struct message *m) {

size_t size;

char *destin;

if (read(cl, &size, sizeof(size)) < 0) {

fprintf(stderr, “could not read from TCP channel [%d]\n”, errno);

return -1;

}

/* read the content. A loop is used to guarantee

* that the given size of data is received from the channel. */

m->size = size;

destin = m->data;

while (size > 0) {

ssize_t count = read(cl, destin, size);

if (count < 0) {

fprintf(stderr, “could not read from TCP channel [%d]\n”, errno);

return -1;

}

/* move the destination array forward and decrease the remaining

* size of data to read */

destin += count;

size -= count;

}

return m->size;

}

intsend_message(int cl, struct message *m) {

/* send the size of the data and the data itself onto the channel */

if (write(cl, &m->size, sizeof(m->size)) < 0 ||

write(cl, m->data, m->size) < 0) {

fprintf(stderr, “could not write to TCP channel [%d]\n”, errno);

return -1;

}

return 0;

}

server.c 

#include <sys/socket.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <string.h>

#include <unistd.h>

#define PORT 50001

/* a data structure used to communicate between server and client. */

struct message {

size_t size;

char data[1024];

};

/* receive / send via the message channel */

intrecv_message(int cl, struct message *m);

intsend_message(int cl, struct message *m);

/* This function setup the server: create socket, bind, listen.

* The socket file id is returned if succeed. */

intsetup_server();

/* This function serves the client with the given socket. */

void serve(int cl);

int main() {

intsk, cl;

/* setup the server and serve clients one by one.

* The user should click Ctrl+C to terminate the server */

sk = setup_server();

while (1) {

/* accept a new client */

cl = accept(sk, NULL, 0);

if (cl == -1) {

fprintf(stderr, “could not accept new client [%d]\n”, errno);

break;

}

serve(cl);

close(cl);

}

return 0;

}

intsetup_server() {

intsk;

int yes = 0;

structsockaddr_inaddr;

/* create the TCP socket and set the address reusable */

if ((sk = socket(AF_INET, SOCK_STREAM, 0)) < 0) {

fprintf(stderr, “could not create server socket [%d]\n”, errno);

exit(errno);

}

setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));

/* initialize the bind address and bind & listen on the specific port */

memset(&addr, 0, sizeof(addr));

addr.sin_family = AF_INET;

addr.sin_addr.s_addr = INADDR_ANY;

addr.sin_port = htons(PORT);

if (bind(sk, (structsockaddr *)&addr, sizeof(addr)) < 0 ||

listen(sk, 5) < 0) {

fprintf(stderr, “could not setup the socket to port %d [%d]\n”, PORT, errno);

close(sk);

exit(errno);

}

returnsk;

}

void serve(int cl) {

struct message msg;

FILE *file;

/* read the path of the file the client requested */

if (recv_message(cl, &msg) < 0) {

fprintf(stderr, “could not receive the path of requested file [%d]\n”, errno);

return;

}

/* display the requested file */

msg.data[msg.size] = ‘\0’;

printf(“REQUESTED: %s\n”, msg.data);

/* open the file for reading and send the content to the client

* in blocks.*/

file = fopen(msg.data, “r”);

if (file == NULL) {

printf(“NOT EXIST\n”);

} else {

/* read until the end of file */

while ((msg.size = fread(msg.data, 1, sizeof(msg.data), file)) != 0) {

send_message(cl, &msg);

}

fclose(file);

printf(“SENT\n”);

}

/* send a message with size 0 to indicate the file does not exist */

msg.size = 0;

send_message(cl, &msg);

}

/* this two functions are shared by server and client to communicate

* with each other through the TCP channel. */

intrecv_message(int cl, struct message *m) {

size_t size;

char *destin;

if (read(cl, &size, sizeof(size)) < 0) {

fprintf(stderr, “could not read from TCP channel [%d]\n”, errno);

return -1;

}

/* read the content. A loop is used to guarantee

* that the given size of data is received from the channel. */

m->size = size;

destin = m->data;

while (size > 0) {

ssize_t count = read(cl, destin, size);

if (count < 0) {

fprintf(stderr, “could not read from TCP channel [%d]\n”, errno);

return -1;

}

/* move the destination array forward and decrease the remaining

* size of data to read */

destin += count;

size -= count;

}

return m->size;

}

intsend_message(int cl, struct message *m) {

/* send the size of the data and the data itself onto the channel */

if (write(cl, &m->size, sizeof(m->size)) < 0 ||

write(cl, m->data, m->size) < 0) {

fprintf(stderr, “could not write to TCP channel [%d]\n”, errno);

return -1;

}

return 0;

}