This is in C. For this assignment we will write a simple database server. We wil
ID: 3819289 • Letter: T
Question
This is in C.
For this assignment we will write a simple database server.
We will be creating a simple database of student records, so let’s describe these first. The format of a student record is as follows:
typedef struct student {
char lname[ 10 ], initial, fname[ 10 ];
unsigned long SID;
float GPA;
} SREC;
Part One – the Server
We will create a database server. The job of the server is to accept a connection on a socket and perform one of the following actions:
A “get” command asking for the data to be returned in order, either alphabetical order by last name, alphabetical order by first name, in order by student ID, or in order by grade point average. In the case of GPA, the highest should come first (4.0 down to 0.0, not 0.0 up to 4.0).
A “put” command that will take one new SREC and add it to the database.
A “delete” command that will look up a certain SREC by SID and delete that record from the database.
A “stop” command. This command will save the entire database into a file (you name the file whatever you like). When the program starts again, if the file exists the contents are used to initialize the lists. In this way we can insert records, stop the server, run it again, and the records from before are available again. The data should be saved in the file in binary, by using “fread” and “fwrite” or “read” and “write”, whichever you prefer. Each “sizeof(SREC)” represents one student in the file.
While in memory the records must be maintained on four data structures, ordered by “lname”, “fname”, “SID”, or “GPA”. In the case of the names, use the order returned by “strcmp” as the ordering criteria. You can use singly-linked lists, doubly linked lists, or binary search trees – your choice. (If you choose to do search trees, when deleting a record you can mark the tree nodes as deleted and avoid having to write the actual tree code for a delete.)
Part Two – the Client
The client program asks for an activity, and acts on the activity. The client reads commands from the user, formats them into requests to the server, sends the request, and then takes back replies from the server and displays the results. Here are examples of the commands which correspond to the above, along with comments:
Command
Note
get lname
The server sends back the records, ordered by last name
get fname
The server sends back the records, ordered by first name
get SID
The server sends the records back ordered by SID
get GPA
The server sends back the records by high-to-low GPA.
put Mahoney,Bill,R,12345,4.0
The record is added to the database. There are no spaces in the “data” part of the record, and the fields are split apart by commas. The GPA and SID are guaranteed to be valid values, however note that the names may be longer than the 10 characters allowed by the structure. It is up to you to chop the names if necessary.
delete 12345
The database record with SID 12345 is removed.
stop
The server saves the file and exits. The client exits as well.
The format of commands is exactly as shown, in lower case for the commands and in mixed case for the data. “get” is used, not “Get”, for instance, and there will be exactly one space after the command and before any arguments.
The program should correctly handle the case where the user enters a command that is not in the list.
It is necessary to “pretty print” the results that come back from the server. For example, the output should look like this:
Enter next command> get SID
| SID | Lname | Fname | M | GPA |
+-------+-----------+------------+---+------+
| 33445 | Mahoney | Bill | R | 4.00 |
| 45678 | Cavanaugh | Patrick | Z | 4.00 |
| 67890 | Dough | John | A | 2.67 |
| 88891 | Smythe | Jane | F | 3.78 |
| 99341 | Namethati | Waytoolon | S | 3.81 |
+-------+-----------+------------+---+------+
Enter next command> put Simpson,Homer,A,4,1.3
Enter next command> get SID
| SID | Lname | Fname | M | GPA |
+-------+-----------+------------+---+------+
| 00004 | Simpson | Homer | A | 1.30 |
| 33445 | Mahoney | Bill | R | 4.00 |
| 45678 | Cavanaugh | Patrick | Z | 4.00 |
| 67890 | Dough | John | A | 2.67 |
| 88891 | Smythe | Jane | F | 3.78 |
| 99341 | Namethati | Waytoolon | S | 3.81 |
+-------+-----------+------------+---+------+
The output should look exactly as shown (with different data obviously) including the ‘|’, ‘+’, etc. The “SID” should occupy exactly 5 columns, zero filled if needed, with a space on either side. The names, both the last and first, should occupy exactly nine columns, with a space on either side. The middle initial has a space on each side. The GPA should occupy exactly four columns.
Part Three – the Port Number and usage
It will be necessary to make sure that each student in the class has a unique port number to work from. Also, what happens on a Linux machine if a process dies unexpectedly is that the port remains “in use” for a period of time. Thus we will assign port numbers in ranges, with 10 per student. Suppose your port numbers are 20000-20009. You could write the program so that it uses port 20000 and if that port is in use after a program crashes, you can switch to 20001. The ports remain in use only for a certain amount of time, so assigning things in groups of ten should be fine. From playing around on Loki it seems to take about a minute or so before the socket is once again available.
There is a class list posted on Blackboard along with this assignment that shows your port range assignment.
You do not retrieve the port number from a command line argument, rather create an easy to access (and find for my sake) constant for your port number, for example:
#define PORT_NUM 20000
Neither the Server nor the Client should accept any command line arguments. The port number is going to be previously defined on both sides as discussed above, and your client can assume the host as “loki” or “localhost”.
Hints
It will be necessary for the server to “tell” the client how many records will be coming back on a “get” request. I suggest that when the server receives the request, it first sends back an integer to the client, and then the client and server loop, reading (or sending) each record individually.
I would suggest that in the case of a “put”, the client take the command line and convert it into an SREC and then send the “put” command followed by the record.
For that matter, you might consider having the client send an integer or character representing what action is to be taken. These could include the four types of “get” requests as well.
Hand-in instructions…
Note: After the programs are collected, any compiled program (executable) in that directory is deleted and the program is recompiled for testing. Specifically, make sure you compile the program with the same options that are used for grading:
gcc -Wall –ansi –pedantic –o server server.c
gcc -Wall –ansi –pedantic –o client client.c
Command
Note
get lname
The server sends back the records, ordered by last name
get fname
The server sends back the records, ordered by first name
get SID
The server sends the records back ordered by SID
get GPA
The server sends back the records by high-to-low GPA.
put Mahoney,Bill,R,12345,4.0
The record is added to the database. There are no spaces in the “data” part of the record, and the fields are split apart by commas. The GPA and SID are guaranteed to be valid values, however note that the names may be longer than the 10 characters allowed by the structure. It is up to you to chop the names if necessary.
delete 12345
The database record with SID 12345 is removed.
stop
The server saves the file and exits. The client exits as well.
Explanation / Answer
client.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#define PORT_NUM 20720
void error(char *msg)
{
perror(msg);
exit(0);
}
typedef struct Student {
char lname[10],initial,fname[10];
unsigned long SID;
float GPA;
} Student;
typedef struct node {
Student data;
struct node *next;
} Node;
int main(int argc, char *argv[])
{
/* DECLARE VARIABLES */
int portno = PORT_NUM, sockfd = 0, cmd = 0, end = 0, count = 0, x = 0, delSID = 0, delsuc = 0;
struct sockaddr_in serv_addr;
struct hostent *server;
Student SREC;
char buffer[256], t1[20], t2[20], t3[20], t4[20], t5[20], t[20];
char* bufptr = NULL;
/* CODE GIVEN TO US TO SET UP OUR CLIENT */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname("loki");
if (server == NULL) {
fprintf(stderr,"ERROR, no such host ");
exit(0);
}
memset((char *) &serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
memcpy((char *)server->h_addr_list,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,(struct sockaddr*) &serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
/* START COMMUNICATION */
do{
/* reset necessary variables */
cmd=0;
memset(&SREC,0,sizeof(SREC));
memset(buffer, 0, sizeof(buffer));
memset(t1, 0, sizeof(t1));
/* store command into buffer */
printf("Please enter the command: ");
fgets(buffer,255,stdin);
bufptr=&buffer[0];
/* perform action based off command */
if(strncmp(buffer,"get lname",9)==0) { /* get lname */
cmd=1;
write(sockfd,&cmd,sizeof(int*)); /* send command number over */
read(sockfd,&count,sizeof(int*)); /* receive number of students to be received */
printf(" ");
printf("| SID | Lname | Fname | M | GPA | ");
printf("+-------+-----------+-----------+---+------+ ");
for(x=0;x<count;x++) {
read(sockfd,&SREC,sizeof(Student)); /* receive student info for printing */
printf("| %5.5lu | %-9s | %-9s | %c | %.2f | ",SREC.SID%100000, SREC.lname,SREC.fname,SREC.initial,SREC.GPA);
}
puts("+-------+-----------+-----------+---+------+ ");
}
else if(strncmp(buffer,"get fname",9)==0) { /* get fname */
cmd=2;
write(sockfd,&cmd,sizeof(int*)); /* send command number over */
read(sockfd,&count,sizeof(int*)); /* receive number of students to be received */
printf(" ");
printf("| SID | Lname | Fname | M | GPA | ");
printf("+-------+-----------+-----------+---+------+ ");
for(x=0;x<count;x++) {
read(sockfd,&SREC,sizeof(Student)); /* receive student info for printing */
printf("| %5.5lu | %-9s | %-9s | %c | %.2f | ",SREC.SID%100000, SREC.lname,SREC.fname,SREC.initial,SREC.GPA);
}
puts("+-------+-----------+-----------+---+------+ ");
}
else if(strncmp(buffer,"get SID",7)==0) { /* get SID */
cmd=3;
write(sockfd,&cmd,sizeof(int*)); /* send command number over */
read(sockfd,&count,sizeof(int*)); /* receive number of students to be received */
printf(" ");
printf("| SID | Lname | Fname | M | GPA | ");
printf("+-------+-----------+-----------+---+------+ ");
for(x=0;x<count;x++) {
read(sockfd,&SREC,sizeof(Student)); /* receive student info for printing */
printf("| %5.5lu | %-9s | %-9s | %c | %.2f | ",SREC.SID%100000, SREC.lname,SREC.fname,SREC.initial,SREC.GPA);
}
puts("+-------+-----------+-----------+---+------+ ");
}
else if(strncmp(buffer,"get GPA",7)==0) { /* get GPA */
cmd=4;
write(sockfd,&cmd,sizeof(int*)); /* send command number over */
read(sockfd,&count,sizeof(int*)); /* receive number of students to be received */
printf(" ");
printf("| SID | Lname | Fname | M | GPA | ");
printf("+-------+-----------+-----------+---+------+ ");
for(x=0;x<count;x++) {
read(sockfd,&SREC,sizeof(Student)); /* receive student info for printing */
printf("| %5.5lu | %-9s | %-9s | %c | %.2f | ",SREC.SID%100000, SREC.lname,SREC.fname,SREC.initial,SREC.GPA);
}
puts("+-------+-----------+-----------+---+------+ ");
}
else if(strncmp(buffer,"put",3)==0) {
cmd=5;
write(sockfd,&cmd,sizeof(int*));
/* tokenize buffer into SREC structure */
sscanf(buffer,"%s %[^','],%[^','],%[^','],%[^','],%s",t,t1,t2,t3,t4,t5);
strncpy(SREC.lname,t1,9);
strncpy(SREC.fname,t2,9);
SREC.initial=t3[0];
SREC.SID=strtoul(t4,&bufptr,10);
SREC.GPA=atof(t5);
read(sockfd,&cmd,sizeof(int*));
write(sockfd,&SREC,sizeof(Student)); /* send structure over */
}
else if(strncmp(buffer,"delete",6)==0) {
cmd=6;
write(sockfd,&cmd,sizeof(int*)); /* send command number over */
sscanf(buffer,"%s %d",t1,&delSID); /* scan SID number to delete */
read(sockfd,&cmd,sizeof(int*));
write(sockfd,&delSID,sizeof(int*)); /* send over SID number to delete */
read(sockfd,&delsuc,sizeof(int*)); /* receive whether deletion was successful or not */
if(delsuc) {
count--;
printf(" SID %d SUCCESSFULLY DELETED ", delSID);
}
else
printf(" SID %d DOES NOT EXIST, NOTHING DELETED ", delSID);
write(sockfd,&delsuc,sizeof(int*));
}
else if(strncmp(buffer,"stop",4)==0) {
cmd=7;
write(sockfd,&cmd,sizeof(int*)); /* send stop command */
}
else
{
cmd=8;
puts("UNRECOGNIZED COMMAND");
write(sockfd,&cmd,sizeof(int*));
}
if(cmd==7) {
read(sockfd,&end,sizeof(int*)); /* read back from server to stop loop */
if(end==1)
break;
}
write(sockfd,&cmd,sizeof(int*));
} while(end==0);
close(sockfd); /* close socket */
return 0;
}
server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#define PORT_NUM 20720
void error(char *msg)
{
perror(msg);
exit(1);
}
typedef struct Student {
char lname[10],initial,fname[10];
unsigned long SID;
float GPA;
} Student;
typedef struct node {
Student data;
struct node *next;
} Node;
void linked_insert(Node **nodePtr, Student value, int type);
int linked_delete(Node **nodePtr, int match);
void write_database(Node *nodePtr);
int load_database(Node **sidList,Node **lnameList, Node **fnameList, Node **gpaList);
int main(int argc, char *argv[])
{
/* DECLARE VARIABLES */
Node *fnameList = NULL, *lnameList = NULL, *gpaList = NULL, *sidList = NULL, *sendnode = NULL;
Student SREC;
int portno = PORT_NUM, sockfd = 0, newsockfd = 0, end = 0, x = 0, count = 0, cmd = 0, delSID = 0, delsuc = 0;
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
/* LOAD DATABASE FROM FILE "database" INTO LINKED LISTS */
count = load_database(&sidList,&lnameList,&fnameList,&gpaList);
/* CODE GIVEN TO US FOR SERVER */
/*STEP 1********************************************/
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
/*STEP 2*********************************************/
memset((char *) &serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
/*STEP 3*********************************************/
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
/*STEP 4*********************************************/
listen(sockfd,5);
/*STEP 5*********************************************/
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
memset(buffer, 0, 256);
/* BEGIN COMMUNICATION */
do{
read(newsockfd,&cmd,sizeof(int*)); /* read command from client */
switch(cmd) {
case 1: /* command "get lname" */
write(newsockfd,&count,sizeof(int*)); /* tell client number of records to send over */
sendnode = lnameList;
for(x=0;x<count;x++) {
write(newsockfd,&(sendnode -> data),sizeof(Student)); /* send student record to client */
sendnode = sendnode -> next; /* advance pointer to next student record */
}
break;
case 2: /* command "get fname" */
write(newsockfd,&count,sizeof(int*)); /* tell client number of records to send over */
sendnode = fnameList;
for(x=0;x<count;x++) {
write(newsockfd,&(sendnode -> data),sizeof(Student)); /* send student record to client */
sendnode = sendnode -> next; /* advance pointer to next student record */
}
break;
case 3: /* command "get SID" */
write(newsockfd,&count,sizeof(int*)); /* tell client number of records to send over */
sendnode = sidList;
for(x=0;x<count;x++) {
write(newsockfd,&(sendnode -> data),sizeof(Student)); /* send student record to client */
sendnode = sendnode -> next; /* advance pointer to next student record */
}
break;
case 4: /* command "get GPA" */
write(newsockfd,&count,sizeof(int*)); /* tell client number of records to send over */
sendnode = gpaList;
for(x=0;x<count;x++) {
write(newsockfd,&(sendnode -> data),sizeof(Student)); /* send student record to client */
sendnode = sendnode -> next; /* advance pointer to next student record */
}
break;
case 5: /* command "put [lname,fname,initial,SID,GPA]" */
count++;
write(newsockfd,&cmd,sizeof(int*));
read(newsockfd,&SREC,sizeof(SREC));
/* insert student record to linked lists */
linked_insert(&sidList,SREC,1);
linked_insert(&lnameList,SREC,2);
linked_insert(&fnameList,SREC,3);
linked_insert(&gpaList,SREC,4);
break;
case 6: /* command "delete [SID]" */
write(newsockfd,&cmd,sizeof(int*));
read(newsockfd,&delSID,sizeof(int*));
delsuc=0;
if(count) { /* make sure list is not empty */
/* delete student from the lists */
if(linked_delete(&sidList,delSID)&&linked_delete(&lnameList,delSID)&&linked_delete(&fnameList,delSID)&& linked_delete(&gpaList,delSID)) {
delsuc=1;
count--;
}
else
delsuc=0;
}
write(newsockfd,&delsuc,sizeof(int*));
read(newsockfd,&delsuc,sizeof(int*));
break;
case 7: /* command "stop" */
end=1;
write_database(sidList); /* save linked list to "database" file */
write(newsockfd,&end,sizeof(int*));
break;
default:
break;
}
read(newsockfd,&cmd,sizeof(int*));
} while(end==0);
close(newsockfd); /* close socket */
return 0;
}
/*
Method Name : load_database
Parameters : every linked list to be updated
Return value(s) : int c -- size of the linked lists
Partners : none
Description : this method reads all the student records from
the file called "database" and stores them in
all the linked lists.
*/
int load_database(Node **sidList,Node **lnameList, Node **fnameList, Node **gpaList) {
int c=0;
Student SREC;
FILE *dbase = NULL;
dbase = fopen ("database","rb");
if(dbase == NULL){
return 0;
}
fread(&SREC,sizeof(Student),1,dbase);
while (!feof(dbase))
{
linked_insert(sidList,SREC,1);
linked_insert(lnameList,SREC,2);
linked_insert(fnameList,SREC,3);
linked_insert(gpaList,SREC,4);
c++;
fread(&SREC,sizeof(Student),1,dbase);
}
fclose(dbase);
return c;
}
/*
Method Name : write_database
Parameters : Node *list -- a pointer to the first node in the list
whose student records will be written to the database
Return value(s) : void
Partners : none
Description : this method writes all the student records from the
linked list passed in to the file called "database"
*/
void write_database (Node *list) {
FILE *dbase = NULL;
Node *nodePtr = list;
dbase = fopen("database","wb");
if(dbase==NULL)
error("Error opening file");
if(nodePtr != NULL) {
while(nodePtr!= NULL)
{
fwrite(&nodePtr -> data,sizeof(nodePtr -> data),1,dbase);
nodePtr = nodePtr -> next;
}
}
}
/*
Method Name : linked_insert
Parameters : Node **nodePtr -- the list for the student record to be
inserted into
Student value -- the student record to be inserted
int type -- changes what the comparison criteria is
based on which list the student record will
be inserted into
Return value(s) : void
Partners : none
Description : this method inserts the given student record into the
given linked list
*/
void linked_insert (Node **nodePtr, Student value, int type) {
Node *newNode;
Node *previous;
Node *current;
newNode = (Node*)malloc(sizeof(Node));
switch(type){
case 1:
if(newNode!=NULL)
{
newNode -> data = value;
newNode -> next = NULL;
previous = NULL;
current = *nodePtr;
while(current!=NULL && value.SID > current->data.SID)
{
previous = current;
current = current -> next;
}
if(previous == NULL)
{
newNode -> next = *nodePtr;
*nodePtr = newNode;
}
else
{
previous -> next = newNode;
newNode -> next = current;
}
}
break;
case 2:
if(newNode!=NULL)
{
newNode -> data = value;
newNode -> next = NULL;
previous = NULL;
current = *nodePtr;
while(current!=NULL && strcmp(value.lname, current->data.lname)>0)
{
previous = current;
current = current -> next;
puts("in while loop ");
}
if(previous == NULL)
{
puts("prev == null, in if statement ");
newNode -> next = *nodePtr;
*nodePtr = newNode;
}
else
{
previous -> next = newNode;
newNode -> next = current;
}
}
break;
case 3:
if(newNode!=NULL)
{
newNode -> data = value;
newNode -> next = NULL;
previous = NULL;
current = *nodePtr;
while(current!=NULL && strcmp(value.fname, current->data.fname)>0)
{
previous = current;
current = current -> next;
}
if(previous == NULL)
{
newNode -> next = *nodePtr;
*nodePtr = newNode;
}
else
{
previous -> next = newNode;
newNode -> next = current;
}
}
break;
case 4:
if(newNode!=NULL)
{
newNode -> data = value;
newNode -> next = NULL;
previous = NULL;
current = *nodePtr;
while(current!=NULL && value.GPA < current->data.GPA)
{
previous = current;
current = current -> next;
}
if(previous == NULL)
{
newNode -> next = *nodePtr;
*nodePtr = newNode;
}
else
{
previous -> next = newNode;
newNode -> next = current;
}
}
break;
}
}
/*
Method Name : linked_delete
Parameters : Node **nodePtr -- the linked list to have a record removed from
int match -- the SID to be removed from the list
Return value(s) : int -- 0 or 1 whether or not the deletion was successful
Partners : none
Description : this method removes a record from the linked list based on the
Student ID passed to it
*/
int linked_delete(Node **nodePtr, int match)
{
Node *previous;
Node *current;
Node *temp;
if(match == (*nodePtr) -> data.SID) {
temp = *nodePtr;
*nodePtr = (*nodePtr) -> next;
free(temp);
return 1;
}
else
{
previous = *nodePtr;
current = (*nodePtr) -> next;
while (current != NULL && current -> data.SID != match)
{
previous = current;
current = current -> next;
}
if(current != NULL)
{
previous -> next = current -> next;
free(current);
return 1;
}
}
return 0;
}
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.