Skip to content

Instantly share code, notes, and snippets.

@AustinW
Last active August 29, 2015 13:57
Show Gist options
  • Save AustinW/9865400 to your computer and use it in GitHub Desktop.
Save AustinW/9865400 to your computer and use it in GitHub Desktop.
CPSC 351 - Assignment 2
CPSC 351 Assignment 2
Authors: Ryan Ott, Ben Hoelzel, Austin White (section 1)
Programming language used: C++
Execution:
$ make
$ ./receiver
--- Separate window ---
$ ./sender [FILE_NAME]
Extra Credit: NO
all: receiver sender
# Receiver
receiver: recv.cpp
g++ recv.cpp -o receiver
# Sender
sender: sender.cpp
g++ sender.cpp -o sender
/**
* @File: msg.h
* @Description:
*
* This header file used by both the sender and the receiver.
* It contains the struct of the message relayed through message queues.
*
* @authors: Ryan Ott, Ben Hoelzel, Austin White
* @date: March 29th, 2014
*/
// The information type
#define SENDER_DATA_TYPE 1
// The done message type
#define RECV_DONE_TYPE 2
/**
* The message structure
* @property long mtype
* @property int size
*/
struct message
{
// The message type
long mtype;
// How many bytes in the message
int size;
/**
* Prints the structure
* @param fp - the file stream to print to
*/
void print(FILE* fp)
{
fprintf(fp, "%ld %d", mtype, size);
}
// Testing function to print to stdout
void printer()
{
printf("%d\n", size);
}
};
/**
* @File: recv.cpp
* @Description:
*
* This program shall implement the process that receives files from the sender
* process. It shall perform the following sequence of steps:
*
* 1. The program shall be invoked as ./recv where recv is the name of the executable.
* 2. The program shall setup a chunk of shared memory and a message queue.
* 3. The program shall wait on a message queue to receive a message from the sender
* program. When the message is received, the message shall contain a field called
* size denoting the number of bytes the sender has saved in the shared memory
* chunk.
* 4. If size is not 0, then the receiver reads size number of bytes from shared
* memory, saves them to the file (always called recvfile), sends message to the
* sender acknowl- edging successful reception and saving of data, and finally goes
* back to step 3.
* 5. Otherwise, if size field is 0, then the program closes the file, detaches the shared
* memory, deallocates shared memory and message queues, and exits.
*
* @authors: Ryan Ott, Ben Hoelzel, Austin White
* @date: March 29th, 2014
*/
#include <sys/shm.h>
#include <sys/msg.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fstream>
#include "msg.h"
// The size of the shared memory chunk
#define SHARED_MEMORY_CHUNK_SIZE 1000
using namespace std;
// The ids for the shared memory segment and the message queue
int shmid, msqid;
// The pointer to the shared memory
void *sharedMemPtr;
// The name of the received file
const char recvFileName[] = "recvfile";
/**
* Automatically create a keyfile.txt if it doesn't exist
*/
key_t createKeyFile()
{
// Open an output filestream
ofstream keyFile;
// Open the file
keyFile.open("keyfile.txt");
// Fill the file with "Hello World"
keyFile << "Hello World";
// Close it
keyFile.close();
// Try getting the key again using the generated file
key_t key = ftok("keyfile.txt", 'a');
// Fail if there's an error
if (key < 0) { perror("(ftok) Error opening file"); }
return key;
}
/**
* Sets up the shared memory segment and message queue
* @param shmid - the id of the allocated shared memory
* @param msqid - the id of the shared memory
* @param sharedMemPtr - the pointer to the shared memory
*/
void init(int& shmid, int& msqid, void*& sharedMemPtr)
{
key_t key = ftok("keyfile.txt", 'a');
if (key < 0) {
key = createKeyFile();
}
// Create message queue
if ( (shmid = shmget(key, SHARED_MEMORY_CHUNK_SIZE, 0644 | IPC_CREAT)) == -1 ){
perror("shmget");
exit(1);
}
// Attach to the message queue
if ((msqid = msgget(key, 0644 | IPC_CREAT)) == -1) {
perror("msgget");//Failed msgget
exit(1);//exit
}
// Attach to the shared memory
sharedMemPtr = shmat(shmid, (void *)0, 0);
if (sharedMemPtr == (char *)(-1)){
perror("shmat");
exit(1);
}
}
/**
* The main loop
*/
void mainLoop()
{
// The size of the mesage
int msgSize;
message myMessage;
// Open the file for writing
FILE* fp = fopen(recvFileName, "w");
// Error checks
if ( ! fp)
{
perror("Error opening the receiver file");
exit(-1);
}
/**
* ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
* @description: Receive message from the queue
* @param msqid: The message queue
* @param msgp: Message buffer to send the message to
* @param msgsz: Maximum size in bytes for the member mtext of the structure pointed to by the msgp argument
* @param msgtyp: If msgtyp is greater than 0, then the first message in the queue of type msgtyp is read
* @param msgflag: 0 | IPC_NOWAIT | MSG_EXCEPT | MSG_NOERROR
* @return On failure return -1 with errno indicating the error, otherwise msgrcv() returns the number of bytes actually copied into the mtext array.
*/
if ( msgrcv(msqid, &myMessage, sizeof(struct message) - sizeof(long), SENDER_DATA_TYPE , 0) == -1) {
perror("msgrcv: Error receiving message");
fclose(fp);
exit(1);
}
// Testing for console
printf("MsgSize\n");
myMessage.printer();
// Set message size from received message
msgSize = myMessage.size;
/* TODO: Receive the message and get the message size. The message will
* contain regular information. The message will be of SENDER_DATA_TYPE
* (the macro SENDER_DATA_TYPE is defined in msg.h). If the size field
* of the message is not 0, then we copy that many bytes from the shared
* memory region to the file. Otherwise, if 0, then we close the file and
* exit.
*
* NOTE: the received file will always be saved into the file called
* "recvfile"
*/
/* Keep receiving until the sender set the size to 0, indicating that
* there is no more data to send
*/
while (msgSize != 0)
{
// If the sender is not telling us that we are done, then get to work
if (msgSize != 0)
{
// Save the shared memory to file
if ( fwrite(sharedMemPtr, sizeof(char), msgSize, fp) < 0)
{
perror("(fwrite) Error writing to file");
}
// Set the sent message type
myMessage.mtype=RECV_DONE_TYPE;
// Send the meassage we are done with message
if ( msgsnd(msqid, &myMessage, 0 , 0) == -1) {
perror("(msgsnd) Error sending message");
exit(1);
}
// Receive next message
if ( msgrcv(msqid, &myMessage, sizeof(struct message) - sizeof(long), SENDER_DATA_TYPE , 0) == -1) {
perror("(msgrcv) Error receiving message");
exit(1);
}
// Testing for console
myMessage.printer();
// Set message size from recived message, LCV
msgSize = myMessage.size;
}
else
{
// Close the file
fclose(fp);
}
}
}
/**
* Perfoms the cleanup functions
* @param sharedMemPtr - the pointer to the shared memory
* @param shmid - the id of the shared memory segment
* @param msqid - the id of the message queue
*/
void cleanUp(const int& shmid, const int& msqid, void* sharedMemPtr)
{
// Detach from shared memory
shmdt(sharedMemPtr); // detach shared memory
// Deallocate the shared memory chunk
shmctl(shmid, IPC_RMID, NULL); // delete shared memory
// Deallocate the message queue
msgctl(msqid, IPC_RMID, NULL); // deallocate message queue
}
/**
* Handles the exit signal
* @param signal - the signal type
*/
void ctrlCSignal(int signal)
{
// Free system V resources
cleanUp(shmid, msqid, sharedMemPtr);
}
int main(int argc, char** argv)
{
/* Install a singnal handler (see signaldemo.cpp sample file).
* In a case user presses Ctrl-c your program should delete message
* queues and shared memory before exiting. You may add the cleaning functionality
* in ctrlCSignal().
*/
signal(SIGINT, ctrlCSignal); // signal handler call
// Initialize
init(shmid, msqid, sharedMemPtr);
// Go to the main loop
mainLoop();
return 0;
}
/**
* @File: sender.cpp
* @Description:
*
* This program shall implement the process that sends files to the receiver process.
* It shall perform the following sequence of steps:
*
* 1. The sender shall be invoked as ./sender file.txt where sender is the name of the
* executable and file.txt is the name of the file to transfer.
* 2. The program shall then attach to the shared memory segment, and connect to the
* message queue both previously set up by the receiver.
* 3. Read a predefined number of bytes from the specified file, and store these bytes in
* the chunk of shared memory.
* 4. Send a message to the receiver (using a message queue). The message shall contain a
* field called size indicating how many bytes were read from the file.
* 5. Wait on the message queue to receive a message from the receiver confirming success-
* ful reception and saving of data to the file by the receiver.
* 6. Go back to step 3. Repeat until the whole file has been read.
* 7. When the end of the file is reached, send a message to the receiver with the size
* field set to 0. This will signal to the receiver that the sender will send no more.
* 8. Close the file, detach shared memory, and exit.
*
* @authors: Ryan Ott, Ben Hoelzel, Austin White
* @date: March 29th, 2014
*/
#include <sys/shm.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include "msg.h"
// The size of the shared memory chunk
#define SHARED_MEMORY_CHUNK_SIZE 1000
// For testing purposes
#define MESSAGE_COUNT_FOR_TESTING 1000
using namespace std;
// The ids for the shared memory segment and the message queue
int shmid, msqid;
// The pointer to the shared memory
void* sharedMemPtr;
/**
* Sets up the shared memory segment and message queue
* @param shmid - the id of the allocated shared memory
* @param msqid - the id of the shared memory
*/
void init(int& shmid, int& msqid, void*& sharedMemPtr)
{
//Create file
ofstream fout;
fout.open("keyfile.txt");
if ( ! fout.good()) {
perror("(ofstream) File creation failed");
exit(1);
}
// Build file bigger than allocated memory chunk. To tests messaging
for (int i =0; i < MESSAGE_COUNT_FOR_TESTING; i++){
fout << i << " Hello World!\n";
}
fout.close();
// Get key
key_t key = ftok("keyfile.txt", 'a');
// Get the id of the shared memory segment
if ( (shmid = shmget(key, SHARED_MEMORY_CHUNK_SIZE, 0644 | IPC_CREAT)) == -1 ) {
perror("(shmget) Error getting the shared memory segment id");
exit(1);
}
// Attach to the shared memory
sharedMemPtr = shmat(shmid, (void *) 0, 0);
if (sharedMemPtr == (char *)(-1)) {
perror("(shmat) Error getting a pointer to shared memory");
exit(1);
}
// Attach to the message queue
if ( (msqid = msgget(key, 0644 | IPC_CREAT)) == -1) {
perror("(msgget) Error getting message from queue");
exit(1);
}
}
/**
* Performs the cleanup functions
* @param sharedMemPtr - the pointer to the shared memory
* @param shmid - the id of the shared memory segment
* @param msqid - the id of the message queue
*/
void cleanUp(const int& shmid, const int& msqid, void* sharedMemPtr)
{
// Detach from shared memory
if (shmdt(sharedMemPtr) == -1) {
perror("(shmdt) Error detaching from shared memory");
exit(1);
}
// Destroy shared memeory
if (shmctl(shmid, IPC_RMID, NULL) == -1 ) {
perror("(shmctl) Error destroying shared memory");
exit(1);
}
// Destroy message queue
if (msgctl(msqid, IPC_RMID, NULL) == -1) {
perror("(msgctl) Error destroying message queue");
exit(1);
}
}
/**
* The main send function
* @param fileName - the name of the file
*/
void send(const char* fileName)
{
// Open the file for reading
FILE* fp = fopen(fileName, "r");
// Was the file open?
if ( ! fp)
{
perror("(fopen) Error opening file");
exit(-1);
}
// A buffer to store message we will send to the receiver.
message sndMsg;
//Set message type to sender
sndMsg.mtype = SENDER_DATA_TYPE;
// A buffer to store message received from the receiver.
message rcvMsg;
/* Read the whole file */
while (!feof(fp))
{
/* Read at most SHARED_MEMORY_CHUNK_SIZE from the file and store them in shared memory.
* fread will return how many bytes it has actually read (since the last chunk may be less
* than SHARED_MEMORY_CHUNK_SIZE).
*/
if ( (sndMsg.size = fread(sharedMemPtr, sizeof(char), SHARED_MEMORY_CHUNK_SIZE, fp)) < 0) {
perror("(fread) Error reading from shared memory");
exit(-1);
}
/* Send message to queue alerting reciver message is ready */
if (msgsnd(msqid, &sndMsg , sizeof(struct message) - sizeof(long), 0) == -1){
perror("(msgsnd) Error sending message to alert receiver");
}
/* Wait until the receiver sends a message of type RECV_DONE_TYPE telling the sender
* that it finished saving the memory chunk.
*/
if ( msgrcv(msqid, &rcvMsg, sizeof(struct message) - sizeof(long), RECV_DONE_TYPE, 0) == -1 ) {
perror("(msgrcv) Error receiving message from receiver");
exit(1);
}
}
/* Send message to queue to tell reciver we have no more data to send, size = 0
* Set the message size in sndMsg to 0; siganls no more data
*/
sndMsg.size = 0;
if (msgsnd(msqid, &sndMsg , sizeof(struct message) - sizeof(long) , 0) == -1) {
perror("(msgsnd) Error sending a message");
}
// Close the file
fclose(fp);
}
int main(int argc, char** argv)
{
// Check the command line arguments
if (argc < 2)
{
fprintf(stderr, "USAGE: %s <FILE NAME>\n", argv[0]);
exit(-1);
}
// Connect to shared memory and the message queue
init(shmid, msqid, sharedMemPtr);
// Send the file
send(argv[1]);
// Cleanup
cleanUp(shmid, msqid, sharedMemPtr);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment