Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <sys/types.h>
- //#include <sys/wait.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <unistd.h>
- //#include <netdb.h>
- //#include <signal.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- //#include <time.h>
- #include <pthread.h>
- #define SERVER_PORT 1234
- #define QUEUE_SIZE 10
- #define MAX_THREADS 10 //ile użytkowników
- #define SIZEOF_HEADER 70
- //rozmiar h. K->S, "TYPE=D ..."; "TYPE=U ..."; "TYPE=L FROM=abcdabcdabcdęążź ..."; "TYPE=M FROM=qwertyuiop1234567890 TO=1qazxsw23edcvfr45tgb SIZE=999999 "
- #define SIZEOF_MSG_HEADER 256
- //rozmiar h. S->K, "TYPE=U LIST=user1 user2 user3 user4 user5 user6 user7 user8 user9 user10 ..."; "TYPE=W DECI=W ..."; "TYPE=D DECI=W ..."; "TYPE=L DECI=W ..."; "TYPE=M DECI=A ..."
- #define SIZEOF_LOGIN 20 //rozmiar loginu bez '\0'
- #define SIZEOF_MSG 999999
- #define SIZEOF_MSG_BUFF 10
- /*
- dostep do pol struktury: (*th_data).pole
- NAGŁÓWKI STAŁEJ DŁUGOŚCI:
- SIZEOF_HEADER(K1->S) - po każdej istotnej wartości następuje spacja, gdy sizeof()<SIZEOF_HEADER -> ewentualne wypełnienie dowolnymi znakami
- SIZEOF_MSG_HEADER(S->K1) analogicznie
- POŁĄCZENIE:
- tresc headera potwierdzenia połączenia(S->K): "TYPE=C DECI=A ..."
- tresc headera odrzucenia połączenia(S->K): "TYPE=C DECI=W ..."
- WIADOMOŚCI:
- tresc headera wiadomosci(K->S, SIZEOF_HEADER): "TYPE=M FROM=żąęźółśćżą TOTO=abcdabcdabcdabcdabcd SIZE=999999 ..." -> przesłanie headera i wiadomości ->
- tresc headera potwierdzenia przesłania wiadomości(S->K): "TYPE=N DECI=A ..."
- tresc headera nieprawidlowego przeslania wiadomosci(S->K): "TYPE=N DECI=W ..."
- LOGOWANIE:
- tresc headera nadania loginu(K->S, SIZEOF_HEADER): "TYPE=L FROM=useruseruseręąęą ..." -> odbiór i przesłanie odpowiedzi ->
- tresc headera nieprawidlowego loginu(S->K): "TYPE=L DECI=W ..." -> brak dodania użytkownika do listy; powinno nastąpić ponowne odpytanie przez klienta
- tresc headera akceptacji loginu(S->K): "TYPE=L DECI=A ..." -> dodanie użytkownika do listy; powinno nastąpić "zalogowanie"
- DISCONNECT:
- tresc headera disconnectu(K->S, SIZEOF_HEADER): "TYPE=D ..." - brak kolejnych pakietow
- tresc headera nieprawidlowego disconnectu(S->K): "TYPE=D DECI=W ..." -> brak disconnecta
- tresc headera akceptacji disconnectu(S->K): "TYPE=D DECI=A ..." -> disconnect
- LISTA USERÓW:
- tresc headera żądania listy userow(K->S, SIZEOF_HEADER): "TYPE=U ..."
- tresc headera listy userow(S->K): "TYPE=U LIST=user1 user2 user3 user4 user5 user6 user7 user8 user9 user10 ..."
- ZŁY NAGŁÓWEK:
- tresc headera odpowiedzi na zly naglowek(S->K): "TYPE=W DECI=W ..." - użytkownik
- OGÓLNA IDEA:
- Użytkownik wysyłając header lub header+wiadomość zawsze czeka na odpowiedź serwera, ponieważ zawsze powinien ją otrzymać - czyli nie może wysyłać 2 wiadomości jednocześnie, bo nie będzie wiedział co odebrać - przyda się semafor czy coś.
- */
- //struktura zawierajaca dane, które zostana przekazane do watku
- pthread_mutex_t socket_mutex[MAX_THREADS];
- pthread_mutex_t opened_mutex;
- pthread_mutex_t usernames_mutex;
- struct thread_data_t {
- struct shared_thread_data_t *shared;
- int id;
- };
- struct shared_thread_data_t {
- int *connection_socket_descriptor;
- int *opened;
- char **usernames;
- };
- int minimum(int a, int b) {
- if (a < b) return a;
- return b;
- }
- int mywrite(int desc, char* msg, size_t sizeoff){
- int nowsend, tosend=sizeoff;
- while(1) {
- nowsend = write(desc, msg, tosend);
- if(nowsend < 0) return nowsend;
- tosend -= nowsend;
- //msg cut begin
- msg += nowsend;
- if (!tosend) break;
- if (!nowsend) break;
- }
- return sizeoff-tosend;
- }
- int myread(int desc, char* msg, size_t sizeoff){
- int nowrcv, torcv=sizeoff;
- while(1) {
- nowrcv = read(desc, msg, torcv);
- if(nowrcv < 0) return nowrcv;
- torcv-=nowrcv;
- //msg cut begin
- msg+=nowrcv;
- if(!torcv) break;
- if(!nowrcv) break;
- }
- return sizeoff-torcv;
- }
- //OBSŁUGA USTAWIANIA LOGINU
- int loginheader(char* header, struct thread_data_t *th_data, int my_id){
- int i;
- char login[SIZEOF_LOGIN + 1];
- char msg[SIZEOF_MSG_HEADER];
- char *header_ptr;
- pthread_mutex_lock(&opened_mutex);
- pthread_mutex_lock(&usernames_mutex);
- printf("Wątek %d: Odebrano zlecenie zmiany loginu\n", my_id);
- memset(login, 0, sizeof(login));
- header_ptr = strstr(header, "FROM=");
- for (i = 0; header_ptr[i + 5] != ' ' && header_ptr[i+5] != '\n'; i++) { //Pobranie loginu z nagłówka
- login[i] = header_ptr[i + 5];
- }
- header_ptr[i + 5] = '\0';
- header_ptr += 5;
- printf("Wątek %d: Odebrano login: '%s' , len: %ld\n", my_id, login, strlen(login));
- for (i = 0; i < MAX_THREADS; i++) { //porównanie loginu z wszystkimi loginami w tabeli i sprawdzenie długości(strcmp dla pustych usernames[i] zwraca 0)
- if (th_data->shared->opened[i]) {
- if (!strlen(login) || strlen(login) > SIZEOF_LOGIN) break;
- if (!strcmp(login, th_data->shared->usernames[i]) && strlen(th_data->shared->usernames[i])) break;
- }
- }
- if (i == MAX_THREADS) { //PRAWIDŁOWY LOGIN -> AKTUALIZACJA LISTY UŻYTKOWNIKÓW -> WYSŁANIE A(CCEPT)
- strcpy(th_data->shared->usernames[my_id], header_ptr);
- strcpy(msg, "TYPE=L DECI=A ");
- pthread_mutex_lock(&socket_mutex[my_id]);
- mywrite(th_data->shared->connection_socket_descriptor[my_id], msg, sizeof(msg));
- pthread_mutex_unlock(&socket_mutex[my_id]);
- printf("Wątek %d: login '%s' zweryfikowany jako prawidłowy i dołączony do listy loginów, wysłano potwierdzenie dołączenia\n",
- my_id, login);
- } else { //NIEPRAWIDŁOWY LOGIN -> WYSŁANIE W(RONG)
- strcpy(msg, "TYPE=L DECI=W ");
- pthread_mutex_lock(&socket_mutex[my_id]);
- mywrite(th_data->shared->connection_socket_descriptor[my_id], msg, sizeof(msg));
- pthread_mutex_unlock(&socket_mutex[my_id]);
- printf("Wątek %d: login '%s' zweryfikowany jako nieprawidłowy, wysłano odrzucenie dołączenia\n", my_id,
- login);
- }
- pthread_mutex_unlock(&usernames_mutex);
- pthread_mutex_unlock(&opened_mutex);
- return 0;
- }
- //OBSŁUGA PRZESYŁANIA WIADOMOŚCI
- int messageheader(char* header, struct thread_data_t *th_data, int my_id){
- int i;
- char login[SIZEOF_LOGIN + 1];
- char msg[SIZEOF_MSG_HEADER];
- char *header_ptr;
- char sizestr[10];
- int size, right_size, received = 0;
- char fullmessage[SIZEOF_MSG];
- printf("Wątek %d: Odebrano zlecenie przesłania wiadomości\n", my_id);
- memset(login, 0, sizeof(login)); //Pobranie odbiorcy z nagłówka
- header_ptr = strstr(header, "TO=");
- for (i = 0; header_ptr[i + 3] != ' '; ++i) login[i] = header_ptr[i + 3];
- memset(sizestr, 0, sizeof(sizestr));
- header_ptr = strstr(header, "SIZE=");
- for (i = 0; header_ptr[i + 5] != ' '; ++i) sizestr[i] = header_ptr[i + 5];
- size = atoi(sizestr);
- printf("Wątek %d: Odebrano zlecenie przesłania wiadomości do użytkownika o nazwie '%s', rozmiar wiadomości: '%d'\n", my_id, login, size);
- strcpy(msg, header); //Przygotowanie headera do przesłania dalej - dodanie bajtów, w celu osiągnięcia poprawnego rozmiaru nagłówka
- memset(fullmessage, 0, sizeof(fullmessage)); //wczytywanie wiadomosci o wielkosci size
- right_size = size;
- header_ptr = fullmessage;
- while (received < right_size) {
- size = myread(th_data->shared->connection_socket_descriptor[my_id], header_ptr, (size_t) minimum(right_size - received, SIZEOF_MSG_BUFF));
- header_ptr += size;
- received += size;
- if(!size) break;
- }
- printf("Wątek %d: Odebrano wiadomość o objętości: %d\n", my_id, received);
- pthread_mutex_lock(&usernames_mutex);
- for (i = 0; i < MAX_THREADS; i++) { //szukanie właściwego użytkownika
- if (!strncmp(login, th_data->shared->usernames[i], strlen(login))) { //JEŚLI ZNALAZŁEŚ UŻYTKOWNIKA, TO PRZEŚLIJ MU WIADOMOŚĆ
- pthread_mutex_lock(&socket_mutex[i]);
- mywrite(th_data->shared->connection_socket_descriptor[i], msg, sizeof(msg)); //wysyłanie nagłówka do odbiorcy
- printf("Wątek %d: Wysłano nagłówek wiadomości do użytkownika o id=%d\n", my_id, i);
- mywrite(th_data->shared->connection_socket_descriptor[i], fullmessage, (size_t) received); //wysyłanie wiadomości do odbiorcy
- printf("Wątek %d: Wysłano wiadomość do użytkownika o id=%d\n", my_id, i);
- pthread_mutex_unlock(&socket_mutex[i]);
- break;
- }
- }
- pthread_mutex_unlock(&usernames_mutex);
- if (i == MAX_THREADS) { //WIADOMOŚĆ NIE ZOSTAŁA WYSŁANA LUB BŁEDNIE WYSŁANA -> WYSŁANIE W(RONG)
- printf("Wątek %d: Brak właściwego odbiorcy połączonego z serwerem\n", my_id);
- strcpy(msg, "TYPE=N DECI=W ");
- pthread_mutex_lock(&socket_mutex[my_id]);
- mywrite(th_data->shared->connection_socket_descriptor[my_id], msg, sizeof(msg));
- pthread_mutex_unlock(&socket_mutex[my_id]);
- printf("Wątek %d: Wysłano pakiet o niepoprawnym wysłaniu wiadomości do nadawcy\n", my_id);
- } else { //WIADOMOŚĆ ZOSTAŁA WYSŁANA POPRAWNIE -> WYSŁANIE A(CCEPT)
- strcpy(msg, "TYPE=N DECI=A ");
- pthread_mutex_lock(&socket_mutex[my_id]);
- mywrite(th_data->shared->connection_socket_descriptor[my_id], msg, sizeof(msg));
- pthread_mutex_unlock(&socket_mutex[my_id]);
- printf("Wątek %d: Wysłano pakiet potwierdzający wysłanie wiadomości do nadawcy\n", my_id);
- }
- return 0;
- }
- //OBSŁUGA ŻĄDANIA LISTY UŻYTKOWNIKÓW
- int usersheader(char* header, struct thread_data_t *th_data, int my_id){
- int i;
- char msg[SIZEOF_MSG_HEADER];
- printf("Wątek %d: Odebrano zlecenie przesłania listy użytkowników\n", my_id);
- strcpy(msg, "TYPE=U LIST="); //PRZYGOTOWANIE PAKIETU DO WYSŁANIA
- pthread_mutex_lock(&usernames_mutex);
- for (i = 0; i < MAX_THREADS; i++) {
- if (strlen(th_data->shared->usernames[i])) {
- strncat(msg, th_data->shared->usernames[i], strlen(th_data->shared->usernames[i]));
- strcat(msg, " ");
- }
- }
- pthread_mutex_unlock(&usernames_mutex);
- printf("Wątek %d: Przygotowany pakiet: %s\n", my_id, msg);
- pthread_mutex_lock(&socket_mutex[my_id]);
- mywrite(th_data->shared->connection_socket_descriptor[my_id], msg, sizeof(msg));
- pthread_mutex_unlock(&socket_mutex[my_id]);
- printf("Wątek %d: Wysłano listę użytkowników\n", my_id);
- return 0;
- }
- //OBSŁUGA NIEPRAWIDŁOWEGO NAGŁÓWKA
- int wrongheader(char* header, struct thread_data_t *th_data, int my_id) {
- char msg[SIZEOF_MSG_HEADER];
- printf("Wątek %d: Odebrano nieprawidłowy nagłówek\n", my_id);
- strcpy(msg, "TYPE=W DECI=W ");
- pthread_mutex_lock(&socket_mutex[my_id]);
- mywrite(th_data->shared->connection_socket_descriptor[my_id], msg, sizeof(msg));
- pthread_mutex_unlock(&socket_mutex[my_id]);
- printf("Wątek %d: Wysłano informacje o nieprawidłowym nagłówku\n", my_id);
- return 0;
- }
- //funkcja opisujaca zachowanie watku - mu// si przyjmowac argument typu (void *) i zwracac (void *)
- void *ThreadBehavior(void *t_data) {
- struct thread_data_t *th_data = (struct thread_data_t *) t_data;
- char header[SIZEOF_HEADER];
- char *header_ptr;
- int check;
- int diff = 0;
- int my_id = th_data->id;
- printf("watek: id=%d\n", my_id);
- while (1) {
- memset(header, 0, sizeof(header));
- check = SIZEOF_HEADER;
- header_ptr = header;
- while (check > 0) { //WCZYTYWANIE NAGŁÓWKA DO header[]
- diff = (int) myread(th_data->shared->connection_socket_descriptor[my_id], header_ptr, check);
- check -= diff;
- header_ptr += diff;
- if (!diff) break;
- }
- if (!diff) break;
- printf("Wątek %d: Odebrano nagłówek o objętości: %ld\n", my_id, sizeof(header));
- if (!strncmp(header, "TYPE=L", 6)) { //OBSŁUGA PRZYPISANIA LOGINU
- if(loginheader(header, th_data, my_id) < 0) {
- fprintf(stderr, "serwer: Błąd przy próbie ustalenia loginu\n");
- exit(1);
- }
- }
- else if (!strncmp(header, "TYPE=M", 6)) { //OBSŁUGA PRZESYŁANIA WIADOMOŚCI
- if(messageheader(header, th_data, my_id) < 0) {
- fprintf(stderr, "serwer: Błąd przy próbie przesłania wiadomości\n");
- exit(1);
- }
- }
- else if (!strncmp(header, "TYPE=U", 6)) { //OBSŁUGA PRZESYŁANIA LISTY UŻYTKOWNIKÓW
- if(usersheader(header, th_data, my_id) < 0) {
- fprintf(stderr, "serwer: Błąd przy próbie przesłania listy\n");
- exit(1);
- }
- }
- else { //OBSŁUGA ZŁEGO NAGŁÓWKA
- if(wrongheader(header, th_data, my_id) < 0) {
- fprintf(stderr, "serwer: Błąd przy próbie obsługi złego nagłówka\n");
- exit(1);
- }
- }
- }
- printf("Wątek %d: Następuje rozłączenie użytkownika\n", my_id);
- pthread_mutex_lock(&opened_mutex);
- pthread_mutex_lock(&usernames_mutex);
- th_data->shared->opened[my_id] = 0;
- strcpy(th_data->shared->usernames[my_id],""); //Usunięcie użytkownika z listy
- close(th_data->shared->connection_socket_descriptor[my_id]);
- pthread_mutex_unlock(&usernames_mutex);
- pthread_mutex_unlock(&opened_mutex);
- pthread_exit(NULL);
- }
- int main(int argc, char *argv[]) {
- int *opened;
- char **usernames;
- int i;
- int server_socket_descriptor;
- int create_result = 0;
- int *connection_socket_descriptor;
- int checksum;
- int tymczasowy_deskryptor;
- char msg_conn[SIZEOF_MSG_HEADER];
- int* tablica;
- tablica = malloc(MAX_THREADS * sizeof(int));
- //alokacja współdzielonych struktur danych
- connection_socket_descriptor = malloc(MAX_THREADS * sizeof(int));
- opened = malloc(MAX_THREADS * sizeof(int));
- usernames = (char **) malloc(MAX_THREADS * sizeof(char *));
- for (i = 0; i < MAX_THREADS; i++)
- usernames[i] = (char *) malloc((SIZEOF_LOGIN + 1) * sizeof(char));
- pthread_t thread[5];
- int bind_result;
- int listen_result;
- char reuse_addr_val = 1;
- struct sockaddr_in server_address;
- //inicjalizacja mutexów
- for (i = 0; i < MAX_THREADS; i++) {
- pthread_mutex_init(&socket_mutex[i], NULL);
- tablica[i]=i;
- }
- pthread_mutex_init(&opened_mutex, NULL);
- pthread_mutex_init(&usernames_mutex, NULL);
- //inicjalizacja gniazda serwera
- memset(&server_address, 0, sizeof(struct sockaddr));
- server_address.sin_family = AF_INET;
- server_address.sin_addr.s_addr = htonl(INADDR_ANY);
- server_address.sin_port = htons(SERVER_PORT);
- server_socket_descriptor = socket(AF_INET, SOCK_STREAM, 0);
- if (server_socket_descriptor < 0) {
- fprintf(stderr, "%s: Błąd przy próbie utworzenia gniazda..\n", argv[0]);
- exit(1);
- }
- setsockopt(server_socket_descriptor, SOL_SOCKET, SO_REUSEADDR, &reuse_addr_val, sizeof(reuse_addr_val));
- bind_result = bind(server_socket_descriptor, (struct sockaddr *) &server_address, sizeof(struct sockaddr));
- if (bind_result < 0) {
- fprintf(stderr, "%s: Błąd przy próbie dowiązania adresu IP i numeru portu do gniazda.\n", argv[0]);
- exit(1);
- }
- listen_result = listen(server_socket_descriptor, QUEUE_SIZE);
- if (listen_result < 0) {
- fprintf(stderr, "%s: Błąd przy próbie ustawienia wielkości kolejki.\n", argv[0]);
- exit(1);
- }
- for (i = 0; i < MAX_THREADS; i++) {
- opened[i] = 0;
- }
- struct shared_thread_data_t s_data;
- s_data.opened = opened;
- s_data.connection_socket_descriptor = connection_socket_descriptor;
- s_data.usernames = usernames;
- while (1) {
- printf("Serwer: czekam na użytkownika\n");
- tymczasowy_deskryptor = accept(server_socket_descriptor, NULL, NULL);
- if (tymczasowy_deskryptor < 0) {
- fprintf(stderr, "%s: Błąd przy próbie utworzenia gniazda dla połączenia.\n", argv[0]);
- exit(1);
- }
- pthread_mutex_lock(&opened_mutex);
- checksum=0;
- for (i = 0; i < MAX_THREADS; i++) { //znajdz pierwszy wolny watek
- if (opened[i] == 0) {
- checksum = 1;
- break;
- }
- }
- if (checksum) { //ZNALEZIONO PUSTY SLOT
- struct thread_data_t *t_data;
- t_data = malloc(sizeof(struct thread_data_t));
- t_data->shared = &s_data;
- printf("wybrane i: %d\n", i);
- t_data->id = i;
- connection_socket_descriptor[i] = tymczasowy_deskryptor;
- create_result = pthread_create(&thread[i], NULL, ThreadBehavior, (void *) &t_data);
- if (create_result) {
- fprintf(stderr, "%s: Błąd przy próbie utworzenia wątku, kod błędu: %d\n", argv[0], create_result);
- exit(1);
- }
- opened[i] = 1;
- strcpy(msg_conn, "TYPE=C DECI=A ");
- pthread_mutex_lock(&socket_mutex[i]);
- mywrite(connection_socket_descriptor[i], msg_conn, sizeof(msg_conn));
- pthread_mutex_unlock(&socket_mutex[i]);
- printf("Serwer: użytkownik %d podłączył się\n", i);
- }
- else { //NIE ZNALEZIONO PUSTEGO SLOTA
- strcpy(msg_conn, "TYPE=C DECI=W ");
- mywrite(tymczasowy_deskryptor, msg_conn, sizeof(msg_conn));
- close(tymczasowy_deskryptor);
- printf("Serwer: brak miejsca dla kolejnego użytkownika\n");
- }
- pthread_mutex_unlock(&opened_mutex);
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement