Commits

Dimitris Leventeas committed 7e0044a

Initial Release

Comments (0)

Files changed (8)

Binary file added.
+/* client program */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include "types.h"
+#include "globals.h"
+#include "client_functions.h"
+
+
+int main(void)
+{
+	struct sockaddr myname;
+	int sock, adrlen;
+	// create the socket that will be used for client's and server's interconnection
+	sock = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (sock < 0) // if socket's creation is failed, then exit 
+	{
+		printf("client socket failure %d\n", errno);
+		perror("client: ");
+		exit(ERROR);
+	}
+	/* AF_UNIX is a synonym for AF_LOCAL. Although AF_LOCAL is mandated by
+	 * POSIX.1g, AF_UNIX is portable to more systems. AF_UNIX was the traditional
+	 * name stemming from BSD, so even most POSIX systems
+	 * support it. It is also the name of choice in the Unix98 specification.
+	 */
+	myname.sa_family = AF_UNIX;
+	strcpy(myname.sa_data, "myle");
+	adrlen = strlen(myname.sa_data) + sizeof(myname.sa_family);
+	if (connect( sock, &myname, adrlen) < 0)
+	{	
+		printf("client connect failure %d\n", errno);
+		perror("client: ");
+		exit(ERROR);
+	}
+
+	login_client(sock); // the client logins to the server
+	choose_action(sock); // after logging in, choose the action that will be taken
+
+	exit(SUCCESS);
+}

client_functions.h

+#ifndef CLIENT_FUNCTIONS_H
+#define CLIENT_FUNCTIONS_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "types.h"
+#include "globals.h"
+
+void login_client(int socket); // logins the client to the server
+void choose_action(int socket); // clients chooses between send mails and read his mailbox
+void send_mail(int socket); // sends a mail to another user
+void read_mails(int socket); // read the user's mailbox
+
+/* the client connects to the server and tries to login. In case of failure, it exits.
+ * In other case, it continues its interaction with the server
+ */
+void login_client(int socket)
+{
+	char login_name[LOGIN_SIZE], message[BODY_SIZE]; // temporary variables for the corresponding parts of the mail
+
+	printf("Welcome, please insert your login name\n");
+	scanf("%s", login_name);
+	write(socket, login_name, LOGIN_SIZE - 1); // send the login name to the server
+	read(socket, message, BODY_SIZE); // read the server's response
+	message[19]='\0'; // terminate the read message regularly
+	// if the server has responded that the login was succefull, then continue
+	if(strncmp(message,"Login successful",strlen("Login successful"))==0) 
+		printf("Login successful\n");
+	else	
+	{
+	// else terminate
+		printf("Login failed!Try another day :D \n");
+		exit(ERROR);
+	}
+}
+
+/* Choose the action that will be taken and then send the message to the server and wait for response.
+ * There are two possible actions. Read all the mails and then delete them, or send an email to a specific
+ * user of the server.
+ */
+void choose_action(int socket)
+{
+	char action[5]; // holds temporarily the desired action of the user
+	printf("Choose action (type \"read\" to read your mails, \"send\" to send a mail or \"exit\" to exit server)\n");
+	scanf("%s",action);
+
+	/* while user insists on giving as input a string that doesn't correspond to an action, we insist on waiting
+	 * waiting for an appropriate action. Let's see who will be the winner. NOT. Just joking.
+	 */
+	while((strncmp(action,"read",strlen("read")+1)!=0)&&(strncmp(action,"send",strlen("send")+1)!=0)&&(strncmp(action,"exit",strlen("exit")+1)!=0))
+	{
+		printf("Choose action(type \"read\" to read your mails , \"send\" to send a mail or \"exit\" to exit server)\n");
+		scanf("%s",action);
+	}
+	write(socket, action, 4); // write at the socket so the server is aware of the action that has to be taken
+	if(strncmp(action, "send", strlen("send") + 1) == 0)
+	{ // send action	
+		send_mail(socket);
+	}
+	else if(strncmp(action, "read", strlen("read") + 1) == 0)
+	{ // read action
+		read_mails(socket);
+	}
+	if (strncmp(action,"exit",strlen("exit")+1)==0)
+	{ // exit action
+		printf("Leaving matrix\n");
+		exit(ERROR);
+	}
+}
+
+/* Send a mail to a specific user of the database. If this user doesn't exist then the program terminates without
+ * taking any action
+ */
+void send_mail(int socket)
+{
+	char message[BODY_SIZE], receiver[LOGIN_SIZE]; // temprorary variable for the corresponding parts of the mail
+
+	printf("Give receiver name\n");
+	scanf("%s", receiver);
+	write(socket, receiver, LOGIN_SIZE); // send the receiver name to server for verification
+	
+	read(socket, message, BODY_SIZE); // receives the server's response to the request
+	message[BODY_SIZE - 1] = '\0'; // terminate appropiately the string
+	printf("%s\n", message); // print out the server's response so the user is aware of how the things go
+
+	if(strncmp(message, "The receiver does not exist", strlen("The receiver does not exist") + 1) == 0)
+	{ // the receiver doesn't exist
+		exit(ERROR);
+	}
+	// if we have reached this line, then the receiver exists and we continue normally the execution of the program
+	printf("Write message to be sent\n");
+	getchar(); // read the '\n' character from the previous input line in order to avoid problems with fgets()
+	fgets(message, BODY_SIZE, stdin); // read the body of the message
+	write(socket, message, BODY_SIZE); // send it to the server in order to be added to the receiver's mail list
+}
+
+/* Receives the user's mail_list from the server one by one and prints them */
+void read_mails(int socket)
+{
+	int mails_to_read;
+	read(socket, &mails_to_read, sizeof(int)); // read firstly the number of mails to be read
+	printf("Mails to be read: %d\n", mails_to_read);
+	mailbox temp_mail; // temporary mail which contains one mail at a time so that can be printed
+	temp_mail = malloc(sizeof(mail));
+	int i = 0;
+	/* prints the mails of the list one by one */
+	for(i = 0; i < mails_to_read; i++){
+		read(socket, temp_mail, sizeof(mail));
+		printf("%s user has received the mail\nbody = %s \n by %s\n\n", temp_mail->receiver.login, temp_mail->body, temp_mail->sender.login);
+	}
+	free(temp_mail);
+}
+
+#endif
+
+#ifndef GLOBALS_H
+#define GLOBALS_H	
+	
+	
+// the length of the username
+#define LOGIN_SIZE 15
+// the number of the user stored in server's database
+#define TOT_USERS 3
+// the number of the characters of each mail
+#define BODY_SIZE 250
+#define ERROR -1
+#define SUCCESS 0
+
+
+#endif
+
Binary file added.
+/* server program */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <pthread.h>
+
+#include "types.h"
+#include "globals.h"
+#include "server_functions.h"
+
+
+
+int main(void)
+{	
+	server_initialization();
+	int sock, new_sd, pid, adrlen;
+	pthread_t thread;
+	/* AF_UNIX is a synonym for AF_LOCAL. Although AF_LOCAL is mandated by
+	 * POSIX.1g, AF_UNIX is portable to more systems. AF_UNIX was the traditional
+	 * name stemming from BSD, so even most POSIX systems
+	 * support it. It is also the name of choice in the Unix98 specification.
+	 */
+	struct sockaddr myname;
+	myname.sa_family = AF_UNIX;
+	strcpy(myname.sa_data, "myle"); // socket name. The path is relative
+	adrlen = strlen(myname.sa_data) + sizeof(myname.sa_family);
+	// create, bind and listen to the socket
+	sock = socket_initialization(myname, adrlen);
+	/* We don't close the socket in the threads because all of them use the same socket,
+	 * and if we close it once, it's closed for all threads
+	 */
+
+	/* Ignore child process termination. */
+	signal (SIGCHLD, SIG_IGN);
+
+	/* Place the server in an infinite loop, waiting on connection requests to come from clients.
+	 * In practice, there would need to be a clean way to terminate this process, but for now it
+	 * will simply stay resident until terminated by the starting terminal or the super-user.
+	 */
+
+	while (1) // the server waits for new connections
+	{
+		/* accept() returns another socket descriptor. The old descriptor is still
+		 * listening for new connections, but this new one is connected to the client
+		 */
+		if ((new_sd = accept(sock, &myname, &adrlen)) < 0)
+		{
+			printf("server accept failure %d\n", errno);
+			perror("server: ");
+			exit(1);
+		}
+		/* Create independent threads each of which will execute function */
+		// check if we can use rc variable to indicate which thread failed
+		if(pthread_create( &thread, NULL, service, (void*)new_sd ))
+		{
+			printf("Thread creation failed\n");
+		}
+	} 
+}
+

server_functions.h

+#ifndef SERVER_FUNCTION_H
+#define SERVER_FUNCTION_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include "types.h"
+#include "globals.h"
+
+pthread_mutex_t send_mutex = PTHREAD_MUTEX_INITIALIZER; // intialization of mutex regarding the send operation
+pthread_mutex_t user_mutex[TOT_USERS]; // we have to initialize the array but we can't do it yet
+
+int authenticate(int socket);  // is responsible for client's login
+void send_service(int , int ); // send mail service
+void read_service(int , int ); // read mail service
+int socket_initialization(struct sockaddr myname , int adrlen); // create, bind and listen to the socket
+void delete_mails( int user);//After sending the mailbox to the client we delete them from the database
+void *service( void *connection ); // service function for the clients who log in
+
+server_user *user_in_dbase;// the users that exist in our database
+
+/* intilializes the database users so that no one has any mails when the server starts */
+void server_initialization()
+{	
+	user_in_dbase = malloc(TOT_USERS * sizeof(server_user));
+	// register the users in the database
+	strcpy( user_in_dbase[0].login , "Dimitris" ); // 1st user
+	strcpy( user_in_dbase[1].login , "Giwrgos" ); // 2nd user
+	strcpy( user_in_dbase[2].login , "Stayros" ); // 3rd user
+	// initially the users have no mails
+	user_in_dbase[0].mail_list = NULL;
+	user_in_dbase[1].mail_list = NULL;
+	user_in_dbase[2].mail_list = NULL;
+	
+	// initialize dynamically the mutexes. Each one corresponds to a mailbox
+	int i = 0;
+	for ( i = 0; i < TOT_USERS; i++)
+	{
+		pthread_mutex_init(&user_mutex[i], NULL );
+	}
+}
+
+/* Identifies the client's username (login name) by comparing with the existing names in 
+ * our database and returns an integer which corresponds to the user or returns -1 if this
+ * user doesn't exist in the database
+ */
+int authenticate(int socket)
+{
+	char login[LOGIN_SIZE]; // a temporary buffer where we store the login name
+	// In case we don't read anything, the string must be terminated
+	// we read the user name from the socket
+	read(socket, login, LOGIN_SIZE - 1); // we don't read the string terminating character
+	login[LOGIN_SIZE - 1] = '\0'; // we append to the last character of the string the '\0'
+	// the string is not empty and the login name exists in our users base
+	int i = 0; // i indicates the user in the database
+	for (i = 0; (strncmp(login, user_in_dbase[i].login, strlen(user_in_dbase[i].login) +1)!=0) && (i < TOT_USERS); i++)
+		;
+	if(i < TOT_USERS) // if the user has been found
+	{
+		// informs the client that the login process has been accomplished succesfully
+		printf("The user who has logged in is: %s\n", user_in_dbase[i].login);
+		write(socket, "Login successful", 20);
+		return i;
+	}
+	return -1; // we need to send a message to inform the client that he/she failed, in case (s)he hasn't been found
+}
+
+/* creates a mail which is linked to the mail list of the corresponding user */
+void send_service(int socket,int user)
+{
+	char mail_receiver[LOGIN_SIZE],mail_sent[BODY_SIZE];
+	read(socket, mail_receiver, LOGIN_SIZE); // read the receiver of the mail from the client
+	mail_receiver[LOGIN_SIZE-1]='\0'; // finalize the string properly
+	int i = 0, receiver = 0;
+	// checks if the receiver exists by traversing all the users in the database
+	for (i = 0; (strncmp(mail_receiver,user_in_dbase[i].login,strlen(user_in_dbase[i].login)+1)!=0) && (i < TOT_USERS); i++)
+		;
+	receiver = i; // define the receiver in the database
+	if(receiver < TOT_USERS) // if receiver < TOT_USERS then we have successfully found the user you were looking for
+	{
+		// informs the client that the login process has been accomplished succesfully
+		printf("The user who is going to receive the mail is: %s\n", user_in_dbase[receiver].login);
+		write(socket,"Correct receiver", BODY_SIZE);
+	}
+	// in other case the user doesn't exist in the database
+	else
+	{
+		write(socket, "The receiver does not exist", BODY_SIZE);
+		return;
+	}	
+	read(socket, mail_sent, BODY_SIZE); // the main body of the mail is read
+	// create the new mail which will be added at the end of the linked list
+	mailbox sent_mail = malloc(sizeof(mail));
+	// if malloc fails
+	if (sent_mail==NULL)
+	{
+		printf("You don't have enough memory on your system. This application is going to be killed\n");
+		exit(1);
+	}
+	/* setting up the data in the new mail */
+	// the +1 in strlen is because of the 1 extra position required for the '\0' character
+	strncpy(sent_mail->sender.login, user_in_dbase[user].login, strlen(user_in_dbase[user].login) + 1);
+	strncpy(sent_mail->receiver.login, user_in_dbase[receiver].login, strlen(user_in_dbase[receiver].login) + 1);
+	strncpy(sent_mail->body, mail_sent, strlen(mail_sent) + 1);
+	// the newest mail is the first one
+	// we enter to the critical section, so we lock down the mutex
+	// it's the part where we add a new mail at the head of the mail list. Because we update the common structures
+	// we have to prevent other threads from executing the following code
+	pthread_mutex_lock( &user_mutex[receiver] );
+		sent_mail->next = user_in_dbase[receiver].mail_list;
+		user_in_dbase[receiver].mail_list = sent_mail;
+	pthread_mutex_unlock( &user_mutex[receiver] );
+	// leaving the critical section, we unlock the mutex so other threads can run this piece of code
+}
+
+/* delivers the mailbox of the logged in user to the client */
+void read_service(int socket,int user)
+{
+	int num_mails = 0; // keep the number of the mails so that the client knows how many mails are to be read
+	pthread_mutex_lock( &user_mutex[user] );// we use the mail list which can be changed by another thread so we
+						// have
+						// to protect it in order to have the updated version
+	mailbox next_mail = user_in_dbase[user].mail_list; // temporary mail which holds the first mail of the list
+	pthread_mutex_unlock( &user_mutex[user] ); // leaving the critical section
+	
+	// count the number of mails to be read by traversing the mail list
+	while(next_mail != NULL)
+	{
+		next_mail = next_mail->next;
+		num_mails++;
+	}
+
+	write(socket, &num_mails, sizeof(int));
+	/* send the mails one by one to the client-user */
+	pthread_mutex_lock( &user_mutex[user] ); // each time, only one thread is allowed to modify
+						 // this specific user's list of mails
+	next_mail = user_in_dbase[user].mail_list;
+	while(next_mail != NULL)
+	{
+		write(socket, next_mail, sizeof(mail));
+		next_mail = next_mail->next; // each time denotes the next mail in the list
+		free(user_in_dbase[user].mail_list); // free the allocated space for the mail which has just been read
+		user_in_dbase[user].mail_list = next_mail; // the new head of the mail list
+	}
+	// exit the critical section and enable the access for this user's mail list
+	pthread_mutex_unlock( &user_mutex[user] );
+}
+
+// create, bind and listen to the socket
+int socket_initialization(struct sockaddr myname, int adrlen)
+{
+	int sock; // create the socket
+	sock = socket(AF_UNIX, SOCK_STREAM, 0);
+
+	if (sock < 0) // socket creation failed
+	{
+		printf("server socket failure %d\n", errno);
+		perror("server: ");
+		exit(1);
+	}
+
+	unlink(myname.sa_data); /* defensive programming */
+
+	// bind the socket to a specific name
+	if (bind(sock, &myname, adrlen) < 0)
+	{
+		printf("server bind failure %d\n", errno);
+		perror("server: ");
+		exit(1);
+	}
+
+	// listen to data from the socket
+	if (listen(sock, 5) < 0) // server can handle up to 5 clients at the same time
+	{
+		printf("server listen failure %d\n", errno);
+		perror("server: ");
+		exit(1);
+	}
+	
+	return sock;
+}
+
+void *service( void *connection )
+{
+	char client_action[5]; // holds the clients desired action
+	int user = -1;	// the initial value doesn't correspond to any user
+	int socket = (int)connection;
+
+	user = authenticate(socket); // the clients tries to connect to the server
+	if (user == -1)
+	{ // if user login failed, then we wait for the next login operation
+		close(socket); // close the socket, not the listening socket
+		pthread_exit(NULL); // wait for the next login operation
+	}
+	read(socket, client_action, 5); // inform the server for the action to be taken
+	client_action[4] = '\0'; // finilize the string which contains the action to be taken
+	
+	/* serve the read action if the client wants to "read" */
+	if (strncmp(client_action, "read", strlen("read")) == 0)
+	{
+		read_service(socket, user);
+	}
+	/* end of read action */
+	
+	/* serves the send action if the client wants to "send" */
+	else if(strncmp(client_action, "send", strlen("send")) == 0)
+	{ /* parental process */
+		send_service(socket , user);	
+	} 
+	close(socket); // close the socket that is created by the accept()
+	pthread_exit(NULL);
+}
+
+#endif
+
+#ifndef TYPES_H
+#define TYPES_H
+#include <sys/types.h>
+// sockaddr_un
+#include <sys/socket.h>	//simperilipsi arxeio gia tin xrisimopoiisi sockets
+#include "globals.h"
+
+// A structure that contains the address binded to the socket. The length and format of the address depend on the address family of the socket.
+typedef struct sockaddr_un
+{
+u_short sa_family;
+char sa_data[14];
+}sockaddr_un;
+
+
+// user name up to LOGIN_SIZE - 1 characters
+typedef struct user_client {
+char login[LOGIN_SIZE];
+}client_user;
+
+// the structure of the mail
+typedef struct mail {
+client_user sender;         // the user that sends the mail
+client_user receiver;       // the receiver of them mail
+char body[BODY_SIZE];       // the body of the mail
+struct mail * next;         // a pointer to the next mail
+} mail;                     // an alias for the structure
+
+// the mail list for each user
+// it's actually a pointer to the first element (header) of the structure and then
+// each elements points to the next one
+typedef mail *mailbox;
+
+// the user form server's side angle
+typedef struct user_server {
+char login[LOGIN_SIZE];
+mailbox mail_list;         // points to the list of mails of each user that is stored in server
+}server_user;
+
+
+#endif
+