Created
April 10, 2017 15:10
-
-
Save Gelio/dc6a32ac40db4e9dadacb8d6e8017c31 to your computer and use it in GitHub Desktop.
UDP task scheduler with client-server architecture created in C for the Operating Systems class
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _GNU_SOURCE | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <errno.h> | |
#include <string.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <sys/time.h> | |
#include <netinet/in.h> | |
#include <signal.h> | |
#include <netdb.h> | |
#include <sys/stat.h> | |
#include <fcntl.h> | |
#include <time.h> | |
#define ERR(source) (perror(source),\ | |
fprintf(stderr,"%s:%d\n",__FILE__,__LINE__),\ | |
exit(EXIT_FAILURE)) | |
#define MAXBUF 576 | |
#define PORT "2000" | |
#define DELAY_MIN 500 | |
#define DELAY_MAX 2500 | |
#define CLIENT_LIFESPAN 7 | |
volatile sig_atomic_t lastSignal=0, | |
shouldQuit = 0; | |
void sigalrm_handler(int sig) { | |
lastSignal=sig; | |
} | |
void sigIntHandler(int signal) | |
{ | |
shouldQuit = 1; | |
} | |
int sethandler( void (*f)(int), int sigNo) { | |
struct sigaction act; | |
memset(&act, 0, sizeof(struct sigaction)); | |
act.sa_handler = f; | |
if (-1==sigaction(sigNo, &act, NULL)) | |
return -1; | |
return 0; | |
} | |
int make_socket(void){ | |
int sock; | |
sock = socket(PF_INET,SOCK_DGRAM,0); | |
if(sock < 0) ERR("socket"); | |
return sock; | |
} | |
struct sockaddr_in make_address(char *address, char *port){ | |
int ret; | |
struct sockaddr_in addr; | |
struct addrinfo *result; | |
struct addrinfo hints = {}; | |
hints.ai_family = AF_INET; | |
if((ret=getaddrinfo(address,port, &hints, &result))){ | |
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); | |
exit(EXIT_FAILURE); | |
} | |
addr = *(struct sockaddr_in *)(result->ai_addr); | |
freeaddrinfo(result); | |
return addr; | |
} | |
void usage(char * name){ | |
fprintf(stderr,"USAGE: %s domain\n",name); | |
} | |
int checkForTimeout() | |
{ | |
if (lastSignal == SIGALRM) | |
{ | |
printf("Client timeout\n"); | |
return 1; | |
} | |
return 0; | |
} | |
void doClient(int fd, struct sockaddr_in addr){ | |
alarm(CLIENT_LIFESPAN); | |
socklen_t size = sizeof(struct sockaddr_in); | |
if (sendto(fd, NULL, 0, 0, &addr, size) < 0) | |
ERR("sendto"); | |
char buf[MAXBUF]; | |
if (recv(fd, buf, MAXBUF, 0) < 0) | |
{ | |
if (errno == EINTR && (checkForTimeout() || shouldQuit)) | |
return; | |
ERR("recv"); | |
} | |
printf("Received from the server: %s\n", buf); | |
int digit1, | |
digit2; | |
int32_t result; | |
char operatorChar = buf[1]; | |
digit1 = atoi(buf + 0); | |
digit2 = atoi(buf + 2); | |
switch (operatorChar) | |
{ | |
case '+': | |
result = digit1 + digit2; | |
break; | |
case '-': | |
result = digit1 - digit2; | |
break; | |
case '*': | |
result = digit1 * digit2; | |
break; | |
default: | |
printf("Unknown operator %c\n", operatorChar); | |
return; | |
} | |
int timeToWait = DELAY_MIN + rand() % (DELAY_MAX - DELAY_MIN + 1); | |
printf("Waiting %d ms\n", timeToWait); | |
struct timespec sleepTime; | |
sleepTime.tv_sec = (int)(timeToWait / 1000); | |
sleepTime.tv_nsec = (timeToWait - sleepTime.tv_sec * 1000) * 1000; | |
if (nanosleep(&sleepTime, NULL) < 0 && errno != EINTR) | |
ERR("nanosleep"); | |
if (shouldQuit || checkForTimeout()) | |
return; | |
printf("Woke up, sending the result\n"); | |
*(int32_t*)buf = htonl(result); | |
if (sendto(fd, buf, MAXBUF, 0, &addr, size) < 0) | |
{ | |
if (errno == EINTR && (checkForTimeout() || shouldQuit)) | |
return; | |
ERR("sendto"); | |
} | |
printf("Sent the result (%d) to the server\n", result); | |
// Wait for retransmission | |
printf("Listening for retransmission request\n"); | |
if (recv(fd, buf, MAXBUF, 0) < 0) | |
{ | |
if (errno == EINTR && (checkForTimeout() || shouldQuit)) | |
return; | |
ERR("recv"); | |
} | |
printf("Retransmission request received\n"); | |
*(int32_t*)buf = htonl(result); | |
if (sendto(fd, buf, MAXBUF, 0, &addr, size) < 0) | |
{ | |
if (errno == EINTR && (checkForTimeout() || shouldQuit)) | |
return; | |
ERR("sendto"); | |
} | |
printf("Result retransmitted, exiting\n"); | |
} | |
int main(int argc, char** argv) { | |
int fd; | |
struct sockaddr_in addr; | |
if(argc!=2) { | |
usage(argv[0]); | |
return EXIT_FAILURE; | |
} | |
srand(time(NULL)); | |
if (sethandler(sigIntHandler, SIGINT) < 0) | |
ERR("setting sigint"); | |
if(sethandler(sigalrm_handler,SIGALRM)) ERR("Seting SIGALRM:"); | |
fd = make_socket(); | |
addr=make_address(argv[1],PORT); | |
doClient(fd,addr); | |
if(TEMP_FAILURE_RETRY(close(fd))<0)ERR("close"); | |
return EXIT_SUCCESS; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _GNU_SOURCE | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <errno.h> | |
#include <string.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <sys/un.h> | |
#include <sys/time.h> | |
#include <netinet/in.h> | |
#include <signal.h> | |
#include <netdb.h> | |
#include <fcntl.h> | |
#include <time.h> | |
#define ERR(source) (perror(source),\ | |
fprintf(stderr,"%s:%d\n",__FILE__,__LINE__),\ | |
exit(EXIT_FAILURE)) | |
#define BACKLOG 3 | |
#define MAXBUF 576 | |
#define MAXADDR 5 | |
#define PORT 2000 | |
#define ALARM_TIME 2 | |
struct connections{ | |
int free; | |
int32_t chunkNo; | |
struct sockaddr_in addr; | |
}; | |
int sethandler( void (*f)(int), int sigNo) { | |
struct sigaction act; | |
memset(&act, 0, sizeof(struct sigaction)); | |
act.sa_handler = f; | |
if (-1==sigaction(sigNo, &act, NULL)) | |
return -1; | |
return 0; | |
} | |
volatile sig_atomic_t shouldQuit = 0, | |
lastSignal = 0; | |
void sigIntHandler(int signal) | |
{ | |
shouldQuit = 1; | |
} | |
void sigAlrmHandler(int signal) | |
{ | |
lastSignal = signal; | |
} | |
int make_socket(int domain, int type){ | |
int sock; | |
sock = socket(domain,type,0); | |
if(sock < 0) ERR("socket"); | |
return sock; | |
} | |
int bind_inet_socket(uint16_t port,int type){ | |
struct sockaddr_in addr; | |
int socketfd,t=1; | |
socketfd = make_socket(PF_INET,type); | |
memset(&addr, 0, sizeof(struct sockaddr_in)); | |
addr.sin_family = AF_INET; | |
addr.sin_port = htons(port); | |
addr.sin_addr.s_addr = htonl(INADDR_ANY); | |
if (setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR,&t, sizeof(t))) ERR("setsockopt"); | |
if(bind(socketfd,(struct sockaddr*) &addr,sizeof(addr)) < 0) ERR("bind"); | |
if(SOCK_STREAM==type) | |
if(listen(socketfd, BACKLOG) < 0) ERR("listen"); | |
return socketfd; | |
} | |
void doServer(int fd){ | |
struct sockaddr_in addr; | |
char buf[MAXBUF]; | |
socklen_t size=sizeof(addr);; | |
int tasksSent = 0, | |
resultsReceived = 0; | |
for(;;){ | |
printf("Waiting for clients\n"); | |
ssize_t bytesReceived = recvfrom(fd,buf,MAXBUF,0,&addr,&size); | |
if(bytesReceived<0) | |
{ | |
if (errno == EINTR && shouldQuit) | |
{ | |
printf("C-c received\n"); | |
break; | |
} | |
ERR("recvfrom"); | |
} | |
if (bytesReceived > 0) | |
{ | |
printf("Ignoring message\n"); | |
continue; | |
} | |
struct sockaddr_in clientAddr = addr; | |
// New connection | |
int digit1 = rand() % 10, | |
digit2 = rand() % 10, | |
operatorInt = rand() % 3; | |
char operatorChar; | |
switch (operatorInt) | |
{ | |
case 0: | |
operatorChar = '+'; | |
break; | |
case 1: | |
operatorChar = '*'; | |
break; | |
case 2: | |
operatorChar = '-'; | |
break; | |
} | |
snprintf(buf, MAXBUF, "%d%c%d", digit1, operatorChar, digit2); | |
if (sendto(fd, buf, MAXBUF, 0, &addr, size) < 0) | |
ERR("sendto"); | |
printf("Message %s sent to the client\n", buf); | |
tasksSent++; | |
addr.sin_addr.s_addr = 0; | |
alarm(ALARM_TIME); | |
int messageRetransmitted = 0; | |
while (1) | |
{ | |
if (recvfrom(fd, buf, MAXBUF, 0, &addr, &size) < 0) | |
{ | |
if (errno == EINTR) | |
{ | |
if(shouldQuit) | |
break; | |
if (lastSignal == SIGALRM) | |
{ | |
lastSignal = 0; | |
if (messageRetransmitted) | |
{ | |
printf("No response from the client, moving on\n"); | |
break; | |
} | |
// Retransmission | |
if (sendto(fd, buf, MAXBUF, 0, &addr, size) < 0) | |
ERR("sendto"); | |
printf("Message %s retransmitted to the client\n", buf); | |
messageRetransmitted = 1; | |
alarm(ALARM_TIME); | |
continue; | |
} | |
} | |
ERR("recvfrom"); | |
} | |
if (addr.sin_addr.s_addr != clientAddr.sin_addr.s_addr) | |
{ | |
printf("Received data not from the client, ignoring\n"); | |
continue; | |
} | |
alarm(0); | |
printf("Result received\n"); | |
int32_t result = ntohl(*(int32_t*)buf); | |
printf("%d%c%d=%d\n", digit1, operatorChar, digit2, result); | |
resultsReceived++; | |
break; | |
} | |
if (shouldQuit) | |
break; | |
} | |
printf("Sent %d tasks and received %d results\n", tasksSent, resultsReceived); | |
} | |
void usage(char * name){ | |
fprintf(stderr,"USAGE: %s\n",name); | |
} | |
int main(int argc, char** argv) { | |
int fd = bind_inet_socket(PORT,SOCK_DGRAM); | |
srand(time(NULL)); | |
if (sethandler(sigIntHandler, SIGINT) < 0) | |
ERR("sethandler sigint"); | |
if (sethandler(sigAlrmHandler, SIGALRM) < 0) | |
ERR("sethandler sigalrm"); | |
doServer(fd); | |
if(TEMP_FAILURE_RETRY(close(fd))<0)ERR("close"); | |
fprintf(stderr,"Server has terminated.\n"); | |
return EXIT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment