#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX 256 using namespace std; /* Struktura opisująca wątek klienta */ struct client_threads{ int id_soc; // Numer gniazda int id_user; // Numer użytkownika struct sockaddr_in addrx; // Adres Gniazda }; /* Struktura opisująca wiadomosc przesyłaną miedzy klientem a serwerem. Podczas przesyłu znakiem konca wiadomosci jest ^ */ struct mess{ int op; // numer operacji (3 pierwsze / znaki 0-2) int id; // miejsce na id uzytkownika (3 nastepne /znaki 3-5) string text; // wiadomosc (od 6 do znaku konca "^") }; mutex mtx; //Semafor dostępu do listy klientów mutex mtx_write; //Semafor do operacji na plikach vector client_list; //Lista klientów w systemie vector users_list; // Lista istniejacych użytkowników naszego chatu int generateID(); void loadUsers(); void service(int my_soc); void disconnect (int id); string checkifDigit(string check); string getHistory(string id); int find_client(int id_name); int find_clients(int id_name); string getFriends(string id); bool checkUser(string user); string getPassword(string id); struct mess recive(int client); void set_name(int soc, int name); bool checkFriend(string id,string id_friend); void createUser(string id, string password); void addFriend(string id, string id_friend); string getHistory(string id_from,string id_to); bool checkifFriend(string user_id, string friend_id); void SaveAndclearFile(string id_from,string id_to,string text); void send_message(int fd,string op, string id, string text); void makeHistory(string id_from,string id_to, string text); int main(int argc, char** argv){ struct sockaddr_in server_addr; socklen_t socket_len; int server_socket; loadUsers(); // Tworzenie gniazda if((server_socket = socket(PF_INET, SOCK_STREAM, 0)) != -1) cout << "Utworzona gniazdo serwera... " << endl ; else cout << "Błąd tworzenia gniazda serwera... " << endl ; int value = 1; // Czy port jest zajety if(setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int) ) < 0) cout << "Błąd setsockopt..." << endl; server_addr.sin_family = PF_INET; server_addr.sin_port = htons(9004); server_addr.sin_addr.s_addr = INADDR_ANY; // Wązanie gniazda if(bind (server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) != -1) cout << "Powiązano gniazdo serwera... " << endl ; else cout << "Błąd powiązania gniazda serwera... " << endl ; // Gniazdo oczekuje nanpolaczenia if(listen(server_socket,10) != -1) cout << "Gniazdo serwera w trybie pasywnym... " << endl ; else cout << "Błąd przechodzenia w stan pasywny gniazda serwera... " << endl ; while(true){ //Tworzenie struktury nowego klienta i akceptacja połączenie client_threads* new_client = new client_threads; socket_len = sizeof(new_client->addrx); if ((new_client->id_soc = accept(server_socket, (struct sockaddr*)&new_client->addrx, &socket_len)) != -1) cout << "Zaakceptowano nowe połączenie... " << endl ; else cout << "Błąd nowego połączenia... " << endl ; //Dosanie nowego klienta do listy klientów w systemie mtx.lock(); client_list.push_back(new_client); mtx.unlock(); //Procedura obsługi klienta thread client_thread(service,new_client->id_soc); client_thread.detach(); } return EXIT_SUCCESS; } /* Funkcja obsuługująca klientów */ void service(int my_soc){ string send_from; //Login użytkownika obsugiwanego przez wątek (jeśli jest zalogowany) struct mess recived_message;//Wiadomość otrzymana od klienta while(true){ recived_message = recive(my_soc);//Odczyt wiadomosci if(recived_message.op == -1){//Zakończenie Połączenia cout << "Brak Połączenia z użytownikiem" << endl; break; } switch(recived_message.op){//Obsługa poszczególnych operacji case 0 : { //Log in - Logowanie do serwisu send_from = to_string(recived_message.id); if((find_clients(recived_message.id) == -1) & (recived_message.text.compare(getPassword(to_string(recived_message.id))) == 0)){ set_name(my_soc, recived_message.id); send_message(my_soc,"000",to_string(recived_message.id),"ok"); cout << "Pomyslne logowanie uzytkownika: " << recived_message.id << endl; } else{ send_message(my_soc,"000",to_string(recived_message.id),"0"); cout << "Błąd logowania uzytkownika: " << recived_message.id << endl; } break; } case 1 : { //Friends_Send - Wysłanie znajomych użytkownika send_message(my_soc,"001",send_from, getFriends(to_string(recived_message.id))); break; } case 2 : { //History_Send - Wysłanie historii konwersacji send_message(my_soc,"002",to_string(recived_message.id), getHistory(send_from,to_string(recived_message.id))); break; } /* Wiadmość wysyłana jeśli obaj użytkownicy mają siebie w liście znajomych. Jeśli znajomy jest niedostępny wiadomośc jest dopisywana do jego historii */ case 3 : { //Send Message - Przesłanie wiadomości int send_to; if(checkifFriend(send_from,to_string(recived_message.id))) if((send_to = find_client(recived_message.id)) != -1) send_message(send_to,"004",send_from, recived_message.text); else makeHistory(send_from, to_string(recived_message.id), send_from + ": " + recived_message.text); else send_message(my_soc,"004",to_string(recived_message.id), "SERVER: NIE MAM CIE W ZNAJOMYCH"); ; break; } case 4 : { //Add New User - Tworzy nowego uzytkownika int i; if ((i = generateID()) != -1){ createUser(to_string(i), recived_message.text); send_message(my_soc,"004",to_string(i), recived_message.text); } else send_message(find_client(recived_message.id),"004","0-1", "Brak Miejsca"); break; } case 5 : { // History_Make - Po przy zamykaniu konwersacji klient wysła cała historie która jest zapisywana SaveAndclearFile(send_from, to_string(recived_message.id),recived_message.text); break; } case 6 : { // Friend - Dodaj znajomego do listy if(checkUser(to_string(recived_message.id))){ addFriend(send_from, to_string(recived_message.id)); send_message(my_soc,"006",to_string(recived_message.id), "1"); } else send_message(my_soc,"006",to_string(recived_message.id), "0"); break; } default : { // Not defined operation cout << "Nieznany kod operacji" << endl; break; } } } disconnect (my_soc); close(my_soc); } /* Nadpisuje całą romowy użytkownika id_from z użytkownikiem id_to */ void SaveAndclearFile(string id_from,string id_to, string text){ ofstream currfile; mtx_write.lock(); currfile.open(id_from + "/" + id_to +".txt", ios_base::trunc); currfile << text; currfile.close(); mtx_write.unlock(); } /* Sprawdza czy użytkownik friend_id ma w znajomych użytkownika user_id */ bool checkifFriend(string user_id, string friend_id){ string line; mtx_write.lock(); ifstream currfile(friend_id + "/Friends.txt"); while(currfile >> line) if (user_id.compare(line) == 0){ cout << "foud user " << line << endl; mtx_write.unlock(); return true; } currfile.close(); mtx_write.unlock(); return false; } /* Sprawdza czy użytkownik istnieje */ bool checkUser(string user){ string line; ifstream currfile("Users.txt"); while(currfile >> line) if (user.compare(line) == 0){ cout << "found user " << line << endl; return true; } currfile.close(); return false; } /* Dodaj użutkownika id_friend do znajomych */ void addFriend(string id, string id_friend){ ofstream currfile; mtx_write.lock(); currfile.open(id + "/Friends.txt", ios_base::app); currfile << id_friend + "\n"; cout << "Added " << id_friend << " to " << id << "/Friends.txt" << endl; currfile.close(); mtx_write.unlock(); } /* Dopisz wiadomość do historii użytkownika niedostępnego */ void makeHistory(string id_from,string id_to, string text){ ofstream currfile; mtx_write.lock(); currfile.open(id_to + "/" + id_from +".txt", ios_base::app); currfile << text << "\n"; currfile.close(); mtx_write.unlock(); } /* Uzyskaj historię rozmowy użytkonika id_form z użytkownikiem id_to */ string getHistory(string id_from,string id_to){ string history(id_from + "/" + id_to + ".txt"), text, line; mtx_write.lock(); ifstream currfile(history); while(getline(currfile,line)) text.append(line +"\n"); currfile.close(); mtx_write.unlock(); if(!text.empty()) return text; else return "0"; } /* Uzyskaj listę znajomych */ string getFriends(string id){ string friends(id + "/Friends.txt"), text, line; ifstream currfile(friends); while(currfile >> line) text.append(line +" "); currfile.close(); return text; } /* Uzyskaj hasło */ string getPassword(string id){ string line; string password(id + "/Haslo.txt"); ifstream currfile(password); getline (currfile,line); currfile.close(); return line; } /* Stwórz nowego użytkownika */ void createUser(string id, string password){ string dir("mkdir " + id); ofstream currfile; system(dir.c_str()); currfile.open("Users.txt", ios_base::app); currfile << id + "\n"; currfile.close(); currfile.open(id + "/Haslo.txt"); currfile << password; currfile.close(); currfile.open(id + "/Friends.txt"); currfile.close(); } /* Wczytaj użytkowników */ void loadUsers(){ string line; ifstream currfile("Users.txt"); while(currfile >> line) users_list.push_back(stoi(line)); currfile.close(); } /* Generuj pierwsze wolne id z przedziału 100-998 */ int generateID(){ for (int i = 100; i < 999; ++i) if(!(find(users_list.begin(), users_list.end(), i) != users_list.end())){ users_list.push_back(i); return i; } return -1; } /* Usuń klienta z listy podłączonych klientów */ void disconnect (int soc) { mtx.lock(); for(size_t i=0; iid_soc == soc){ delete client_list[i]; client_list.erase(client_list.begin() + i); mtx.unlock(); return; } mtx.unlock(); } /* Znajdź numer gniazda pod którym znajduje się użytkownik o danym id */ int find_client(int id_name){ for(size_t i=0; iid_user == id_name){ return client_list[i]->id_soc; } return -1; } /* Czy dany użytkownik jest zalogowany */ int find_clients(int id_name){ for(size_t i=0; iid_user == id_name){ return 1; } return -1; } /* Logowanie - przypisanie użytkownika do danego gniazda */ void set_name(int soc, int name){ mtx.lock(); for(size_t i=0; iid_soc == soc){ client_list[i]->id_user = name; mtx.unlock(); break; } mtx.unlock(); } /* Wysłanie wiadomości w ustalonym formacie */ void send_message(int fd,string op, string id, string text){ string message = op + id + text + "^"; cout << "Send: " << message << endl; write(fd, message.c_str(), message.size()); } /* Odbiór wiadomości i przetworzenie jej do danej struktury */ struct mess recive(int client){ char bufor[MAX]; string zdanie; size_t fnd; struct mess recived; int size; do{ size = read(client, &bufor, 256); if(size == 0){// Jeśli 0 to klient się rozłączył recived.op = -1; return recived; } if(size == -1){// Jeśli 0 to klient się rozłączył recived.op = -1; return recived; } //Jeśli znaleziono znak końca wiadomości to break i koniec czytania wiadomości string wczytane(bufor); fnd = wczytane.find_first_of("^");//fnd reprezentuje pozycję znaku w stringu if(fnd != string::npos){// Jeśli nie znajdzie znaku w stringu przyjmuje wartość nops czyli wiadmość się nie skończyła if (fnd == 0) break; wczytane = wczytane.substr(0,fnd); zdanie.append(wczytane); break; } zdanie.append(wczytane); }while(fnd == string::npos);// Czytaj dopóki nie wytąpi znak końca //Otrzymana wiadmość cout << "Recv: " << zdanie << endl; //Sprawdzanie czy wiadomość jest zgodna z ustaloną formą i przekształcenie do struktury if (zdanie.size() >= 6){ recived.op = stoi(checkifDigit(zdanie.substr(0,3))); recived.id = stoi(checkifDigit(zdanie.substr(3,3))); recived.text = zdanie.substr(6); } else{ cout << "Nieodpowiednia ramka" << endl; recived.op = -2; return recived; } return recived; } /* Sprawdza czy string składa się wyłącznie z cyfr jeśli nie to ustal na -1 tzn. rozłącz */ string checkifDigit(string check){ for(size_t i=0; i