Prototyp serwera czy dobre założenia ?

0

Założeniem tego serwera jest to że ma być prototypem w najprostszej postaci oparty na protokole TCP. Sytuacja taka:

  1. Server uruchomiony i nasłuchuje na porcie
  2. Łaczy sie troje użytkowników powiedzmy że graja w grę
  3. Każdy z użytkowników ma do socketa przypisany identyfikator taki identyfikator zapisuje w bazie czyli ze mam trzech userow to mam trzy identyfikatory
  4. Uzytkownik nr 1 wciska guzik STRZAŁKA w LEWO (dla uzytkownika nr 2) i teraz chce żeby to zostało wysłane do serwera a serwer odeslal od razu tą strzałke w lewo do usera nr 2. zeby nie zapamietywal nic na serwerze tylko odsylal dalej

a) wciskam strzalke (dla usera nr 2)
b) serwer pobiera z bazy identyfikator socketu dla usera nr 2
c) jesli jest wysyla do niego strzalke w lewo
6. bez buferowania bez niczego

Czy da sie to zrobic i czy to dobre zalozenie ?

2

Nie do końca rozumiem po co za baza danych zamiast zwykłej mapy w pamięci ale tak poza tym to ok.

0

otrzebuje zrobic taki serwer i opisac jego dzialanie a pozniej prosty czat ze np napisze wiadomosc wysle i odrau pojawi sie u kogos, ale wiadomosc musi przejsc przez serwer natomiast nie ma byc zapamietana tylko od razu wyslana do usera albo unicestwiona. Tylko wlasnie nie ma byc to klient - klient tylko klient - server - klient

1

Pare postów niżej pokazywałem taki prosty server chatu.

otrzebuje zrobic taki serwer i opisac jego dzialanie a pozniej prosty czat ze np napisze wiadomosc wysle i odrau pojawi sie u kogos, ale wiadomosc musi przejsc przez serwer natomiast nie ma byc zapamietana tylko od razu wyslana do usera albo unicestwiona.
Unicestwiona, to znaczy? Kiedy wiadomość ma zostać unicestwiona?

Podklejam jescze raz :- )
Jak widać logika prosta.

  1. Zapisujesz deskryptor połączenia
  2. Puszczasz wątek klienta
  3. Jeśli wiadomość nadeszła rozsyłasz do wszystkich oprócz siebie.
  4. Jeśli rozłączenie to kasujesz deskryptor.
 
#include <iostream>
#include <string.h>
#include <netinet/in.h>
#include <algorithm>
#include <sys/socket.h>
#include <thread>
/** usage, telnet localhost 8888 **/
std::vector<int> clients;
void connection_handler(int);
int main()
{
    sockaddr_in servaddr;
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(8888);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    int yes = 1;
 
    int listen_socket = socket(AF_INET, SOCK_STREAM, 0);
    setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
    bind(listen_socket, (sockaddr*)&servaddr, sizeof(servaddr));
    listen(listen_socket, 10);
 
    int newfd = 0;
    while((newfd = accept(listen_socket, nullptr, nullptr)) > 0)
    {
        clients.push_back(newfd);
        std::thread client_thread(connection_handler, newfd) ;
        client_thread.detach();
    }
 
}
void connection_handler(int client)
{
    int rcv_bytes = 0, snd_bytes = 0;
    std::array<char, 250> buff = {0};
    while((rcv_bytes = recv(client, buff.data(), buff.size(), 0)) > 0)
    {
        for(auto cli : clients){
            if(cli != client)
                send(cli, buff.data(), strlen(buff.data()), 0);
        }
        memset(buff.data(), 0, buff.size());
    }
    clients.erase(std::find(clients.begin(), clients.end(), client));
    std::cout << client << " drop..\n";
}
 
0

jak wyslac wiadomsoc czy ten wcisniety guzik do konkretnego uzytkownika ? ten send(cli, buff.data(), strlen(buff.data()), 0); to ten cli to jest lista clients.push_back(newfd); tych identyfikatorow ? i jak sie user rozlaczy to na to miejsce przesuwa sie tablica czy jest puste i ktos inny po polaczeniu wpada w to puste miejsce ?

0

Probowalem skompilowac g++ -std=c++11 -o srv srv.cpp ale mam błąd jak poniżej
srv.cpp: In function ‘void connection_handler(int)’:
srv.cpp27: error: variable ‘std::array<char, 250ul> buff’ has initializer but incomplete type

0

To będzie trochę bardziej skomplikowane niż kod @Proxima :)

Żeby user1 mógł coś wysłać do user2 to user1 musi coś wiedzieć o user2 np. jego nick.

Czyli serwer też musi mieć dodatkowe informację, a nie tylko deskryptor. Czyli user po połączeniu musi podesłać coś do serwera o sobie np. nick. Wtedy masz na serwerze mapę

map<nick, deskryptor>

I teraz musisz wybrać czy klient ma wysyłać w ciemno do serwera wiadomość np. taką [NICK][WIADOMOSC CHATU] z nadzieją, że ktoś o nicku NICK będzie podłączony. Czy klient ma wiedzieć jacy inni userzy są podłączeni. A to rodzi nowy problem taki, że serwer po każdym podłączeniu i rozłączeniu klienta musi do wszystkich innych wysyłać te informację. Miłej zabawy ;)

0

Na poczataku bede wiedzial ze taki i takin uzytkownik istnieje a poniewaz bedzie dwoje userow to bedzie latwe. Ale jakbym mial robic uzytkownikow jako czat to musialaby nuc to lista ( nick, deskryotor) i chcialem uzyc bazy danych ale to jak mowisz duzo roboty bo damy user musialby miec liste znajomych

0

Przejrzałęm pare rzeczy o TCP/UDP i z tego co wywnioskowałem

TCP:
Serwer czeka na poączenie jeżeli ono nadejdzie tworzymy nowy wątek który obsluguje komunikacje z klientem. Ulatwia nam to sprawe i mamy kontrolowany przeplyw pakietow. Prawdopodobnie takie zastosowanie powinno byc jezeli piszemy jakis komunikator czy czat. Jeżeli mam 2 tys osob to jak rozumiem ze 2 tysiace watkow ?

UDP:
Bezstanowy protokol ktory nie gwarantuje czy pakiet doszedl i prawdopodobnie powinno sie to wykorzystwyac przy strumieniach audio/video czy grach FPS. Jednak chcialem sie zapytac czy napisanie komunikatora o UDP to dobry pomysl? to znaczy ze przy 2 tysiacach uzytkownikow nie bedzie zadnych watkow a zasada polaczenia z serwerem bylaby taka:

  1. Polaczanie przez SSL - szyfrowane
  2. Logowanie /login/haslo po ktorym serwer zwraca klucz Sesji
  3. Kazde wyslanie zapytania od klienta musi zawierac klucz sesji
  4. Zeby sie wylogowac wysylamy polecenie zeby skasowac klucz sesji
  5. Dodatkowo wysylamy co minute PING z naszym kluczem sesji ze jestesmy aktywni.
  6. jezeli user nie wysle ping co 60 sekund to znaczy ze serwer skasuje nam sesje a my bedziemy wylogowani

Ma to sens ?
I czy w UDP kliencie sa tez podlaczenie i maja swoj deskryptor ? zeby serwer wiedzial komu ma przeslac dalej nasza wiadomosc ?

0

Jeśli piszesz chat, prawdopodobnie zależy ci na tym, aby dane doszły bezbłędnie i nie przeszkadza ci mniejsza trochę mniejsza szybkość (jeśli myślisz że Ci przeszkadza, mylisz się, do chatu TCP jest jak znalazł), co do pytania o wątki to nie - 2k osób nie równa się 2k wątków, nie pisałem tak dużego programu sieciowego i tu Ci nie pomogę. Najpierw może zastanów się czy twój chat w ogóle kiedykolwiek będzie miał liczbę klientów większą niż chociażby 20, nie oszukujmy się, jest multum czatów, i nie wydaje mi się, aby twój był jakiś wyjątkowy.
Generalnie twoje punkty są ok, dla protokołu UDP.
Co do ostatniego pytania to nie, UDP jako samo UDP nie jest protokołem połączeniowym, to znaczy że między klientem a serverem nie ma żadnego połączenia (gniazdo serwera to po prostu taka dziura w którą wpadają/wypadają dane), dane wysyłasz za pomocą funkcji sendto i odbierasz przy pomocy recvfrom, natomiast jest możliwe połączenie gniazda datagramowego tylko że jest to tylko taki lukier gdyż tak naprawde żadne połączenie ofc nie następuje, aczkolwiek kernel zapisuje sobie dane które przekazujesz do funkcji connect i po prostu możesz na gnieździe datagramowym (UDP) wywoływać funkcje recv i send jakgdyby było ono połączone, ale pamiętaj że nie jest :- )
Ah, i jescze jedno, jeśli wybierasz UDP musisz liczyć się z tym że dane mogą nie dojść i wypadałoby robić retransmisje, czym defacto zajmuje się protokół TCP natomiast jeśli dojdą, będą poprawne.

0

Ehhh potrzebuje zrobic tak ze mamy polaczone powiedzmy 20 osob do serwera (UDP) i user 1 wysyla wiadomosc do User 4 - jezeli te protokoly sa bezstanowe i nie ma polaczenia miedzy nimi to musialbym co kilka sekund pytac o to czy sa jakies wiadomosc bo wtedy na moje pytanie serwer moze odpowiedziec i odeslac wiadomosc jak rozumiem. ale o bez sensu i zostaje mi chyba na TCP. ALe to sie wiaze z watkami.

0

Ale po co ty chcesz odpytywać klienta czy ma jakieś dane, to jest conajmniej niepotrzebne. Z klienta wysyłasz pakiet danych który zawiera wiadomość i partnera rozmowy, server odsyła to do partnera i wszystko. Nie jest potrzebne żadne odpytywanie... I do servera UDP nikt nie jest podłączony! W UDP nie istnieje coś takiego jak połączenie.

0

no ale właśnie jak w UDP serwer bedzie wiedzial do kogo ma wyslac moja wiadomosc ? bo tego nie moge zakumac. Jezeli zaloguje sie do serwera (UDP) znaczy wysle pakiet i on odesle klucz sesji to zeby zapamietal jego identyfikator i odeslal pozniej kolejne wiadomosci tyle ze jak to nie jest polaczenie to nie ma jak zapamieatc. ale gry jakos robia na udp

0

A co za problem aby serwer wysyłał przy każdej zmianie (nowy klient/rozłączenie) tablice z danymi klientów?

1

Co Ty z tym UDP? Zrób to na TCP i (jeden) problem z głowy.

0

No a jak robia serwerry do gier na UDP ?
// Edit Mówie o mało skomplikowanych grach :p
Na wątkach, są jescze inne metody ale wątki chyba są najprostsze wbrew pozorom.
Jak panicznie boisz się wątków możesz stworzyć współbieżny serwer w oparciu o procesy (czytaj funkcja fork()), jescze jak procesy są nieco łatwiejsze niż wątki to sa niestety wolniejsze (czas tworzenia nowego procesu), i dochodzi problem z IPC, łatwo będzie za pomocą kolejek FIFO bądź kolejek komunikatów systemu V ale czy to będzie wydajne to nie wiem. Możesz użyć też shared memory tyle że to już będzie na 100% trudniejsze niż wątki.
// Edit
Ah, i pozostaje jeszcze jedna opcja a mianowicie multipleksacja I/O funkcją select(), nie korzystam, ale się da.

0

Nareszcie coś sie udalo hahaha mówiąc serio serio to napisałem w C "napisalem to za duzo powiedziane, skleilem kod z dokumentacji i jakis ksiazek z przykladami" W kazdym razie to co mam teraz to :

  1. Serwer TCP
  2. Jak kazdy serwer tego typu tworze socket, oczekuje na klienta (jak dziwka)
  3. przychodzi klient sobie i ja go na oddzielny wątek (niestety na razie tak na boczku z nim rozmawiam)
  4. Klient sie loguje do konta a ze na razie rozmawia z serwerem to mam na watku ten sam socket wiec odpisuje mu bez ceregieli
  5. Jak sie zaloguje to biore jego identyfikator socketa i wstawiam do bazy (user sie logowal na swoj numer id i haslo i teraz w bazie ma swoj identyfikator socketa)
  6. od tej pory jezeli ktos wysle pakiet ze chce przeslac wiadomosc do uzytkownika to na tym watku system sprawdzi w bazie identyfikator jak jest i wysle wiadomosc chyba (bo to dopiero bedzie dzialac)

uruchomie serwer i wysylam wiadomosc z kilku klientow (na razie klient to telnet) no i ta wiadomosc jest wysweitlona na serwerze albo moze wrocic do klienta

To co zostalo mi do zrobienia to :

  1. serializacja danych albo robienie jakis pakietow ze chce wyslac dane za pomoca tablicy:
array(
id_usera_docelowe = 434324,
wiadomosc = "efiewjfiewf",
flagi = 443 , 
)

i taka tablice przeslac jako serializowaną ale nie wiem czy to dobry pomysl
I nie wiem jak mam zrobic zeby text napisany od klienta byl czysty zeby nie bylo znaku specjalnego na koncu nowa linia /0 albo /r/n
kod ktory mi odbiera wiadomosci jest :

    //Odbieranie wiadomosci
   char message[1024];
    while( (readedSize = recv(socket , message , 1024 , 0)) > 0 ) {
        //Odeslij wiadomosc     
        write(socket , message , strlen(message));
       //wyswietl wiadomosc
       puts(message);
    }

Jak przetworzyc teraz ta zmienna message zeby nie bylo zadnych znakow smieci w niej i zebym mogl pozniej rozpakowac albo zserializowana tablice albo dla testow zorbic if (message == "test") puts("dziala");

0

Protokół jest binarny czy tekstowy?

0

No ja planowalem jak pisalem wyzej skladac tablice z danymi np logowanie:
Array (
Head: "login",
User_login: 62773,
Pass: "alamalota",
)
I chcialem to spakowac w sensie jakis serializer do stringa i caly string odebrac rozserializowac i zminic na tablice z paramsami i wywolac odpowiednie funkcje.

Teraz gdy testowalem i wyslalem zwyklym textem: hey
A w serwerze chcialem odebrac if (rcv == "hey") printf("test ok") no to nic nie wyswietlilo bo ten hey co wyslalem ma pewnie na koncu znak kowej lini albo /0 czy cos i nie wiem jak to teraz zrobic najlepiej

0

Nie wyświetliło Ci, bo ten warunek w ifie to nie w C - porównujesz wskaźniki a nie łańcuchy. Do porównywania masz funkcje strcmp/strncmp.

Array (
Head: "login",
User_login: 62773,
Pass: "alamalota",
)

Tak ma wyglądać to, co wysyłasz do serwera?

0

Napisałem serwer w c++ bo w C nie moglem sobie poradzic z obsluga postgresa i z serializacja. Zaraz sprawdze to porownanie ten warunek w kazdym razie c++ jest dla mnie duzo latwiejszy niz C

1

A kto powiedział, że użycie TCP jest równoznaczne z użyciem wielu wątków? Można napisać serwer TCP obsługujący 1000000 równoległych połączeń na jednym wątku. I jeśli zależy Ci na wydajności, to liczba wątków powinna być stała, uzależniona od liczby rdzeni procesora, a nie od liczby podłączonych użytkowników. Jeżeli będziesz mieć jeden wątek na połączenie, to 10k połączeń nie przeskoczysz, a już przy ok. tysiącu system się będzie męczył obsługa wątków zamiast logika aplikacji.

UDP się stosuje w grach ze względu na opóźnienia, a nie wątki. Po prostu dla gry opóźniony pakiet nie ma żadnej wartości, więc nie ma sensu aby blokował późniejsze pakiety. Lepiej zgubić jeden pakiet, niż przez retransmisję jednego pakietu opóźnić 20 innych. W UDP pakiety mogą się mijać tj. przychodzić w innej kolejności, w TCP kolejność jest zachowana i późniejsze pakiety muszą czekać na dostarczenie wcześniejszych.

0

zainteresuj się iocp.

0

Utknąłem :/ a może jest ktoś kto podjąłby się tego zadania ? Jeśli tak to proszę o PW

0

Utkąłem z tym zeby dobrze rozegrać ilość wątków i nieblokujące porty. Potrzebuje zrobić na start 4 wątki albo zalezne od parametru 8 czy 12 wątków. Potem nie wiem jak do tego wątku przerzucić nowe połączenie jak nadejdzie. I powiedzmy wątek obsługuje porty nieblokujące bo jak rozumiem to dla przykładu mamy 4 wątki. Łączy sie 10 klientów i 3 z nich akurat trafi do jednego wątku. I teraz jak jeden wyśle pakiet to port nie może byc zablokowany bo w tym czasie za chwile ktos inny moze wysylac pakiet. Tak to rozumiem ale nie wiem jak to zrobic.

na razie dla testow robie to na UDP. Jestem na etapie logowania i przesylania wiadomosci mam liste uzytkownikow (ktorzy sie zalogowali) i poprzez ping wiem czy sa aktywni czy wylogowani i komu skasowac sesje. Jednak docelowo bede chcial to zrobic na TCP

0

Zrób sobie wspomnianą pulę wątków. Do tej puli będziesz oddelegowywał odbiór i obsługę (pojedynczej) wiadomości z połączeń, do których klient coś wysłał.

1 użytkowników online, w tym zalogowanych: 0, gości: 1