minikomunikator, c++

minikomunikator, c++
MI
  • Rejestracja:prawie 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:199
0

Cześć :)
Piszę sobie minikomunikator w Qt i mam do Was pewne pytanie:
Rozważmy wysyłanie wiadomości. Mamy tutaj dwie możliwości:
1.
Wg mnie powinienem to zaimplementować to tak:
Serwer po otrzymaniu wiadomości zapisuje do bazy danych wiadomość po czym sprawdza czy adresat jest online. Jeżeli jest to wysyła mu komunikat. Na ten komunikat klient daje standardowe zapytanie o nowe wiadomości, a serwer mu je zwraca.
2.
Klient ma osobny wątek, który co jakiś czas wysyła zapytanie do serwera z zapytaniem, czy nie ma czasem dla niego nowych wiadomości. Tu jednak odrzuca mnie trochę fakt, że serwer będzie zalewany ciągłymi zapytaniami, ale wydaje mi sie, że i tak muszę mieć w kliencie jakiś wątek, który stale ( co to znaczy stale?) będzie sprawdzał połączenie.
Doradźcie mi tu trochę :)

TK
  • Rejestracja:ponad 13 lat
  • Ostatnio:16 dni
  • Postów:342
1
mielony napisał(a):

Cześć :)
1.
Wg mnie powinienem to zaimplementować to tak:
Serwer po otrzymaniu wiadomości zapisuje do bazy danych wiadomość po czym sprawdza czy adresat jest online. Jeżeli jest to wysyła mu komunikat. Na ten komunikat klient daje standardowe zapytanie o nowe wiadomości, a serwer mu je zwraca.

Masz zamiar tworzyc archwium wiadomosci na serwerze? Bo jezeli nie to zapis w bazie danych komunikatu dla uzytkownika online mija sie z celem. Serwer powinien wyslac do klienta wiadomosc jezeli jest online lub zapisac ja w bazie danych w przeciwnym przypadku.

Edit: z drugiej strony wypadaloby przechowywac gdzies wiadomosci, ktorych odbiorca nie potwierdzil. Pytanie tylko czy trzeba do tego celu wykorzystywac baze danych? Z jednej strony jest to wygodne, a z drugiej to nie wiem czy optymalne - nalezaloby sie troche nad tym zastanowic :)

Pytanie serwera o nowe wiadomosci wydaje mi sie bezcelowe. Klient powinien potwierdzic odebranie wiadomosci i tyle wystarczy.

mielony napisał(a):

Cześć :)
2.
Klient ma osobny wątek, który co jakiś czas wysyła zapytanie do serwera z zapytaniem, czy nie ma czasem dla niego nowych wiadomości. Tu jednak odrzuca mnie trochę fakt, że serwer będzie zalewany ciągłymi zapytaniami, ale wydaje mi sie, że i tak muszę mieć w kliencie jakiś wątek, który stale ( co to znaczy stale?) będzie sprawdzał połączenie.
Doradźcie mi tu trochę :)

Ja bym to zrobil tak: klient w ogole sie nie pyta o nowe wiadomosci - dostaje wszystkie zalegle wiadomosci po zalogowaniu sie w sposob automatyczny. Jak je dostanie to wysle potwierdzenie ze dostal i na tej podstawie serwer wie zeby juz wiecej ich nie wysylac. W momencie kiedy serwer odbierze nowe wiadomosci przeznaczone dla konkretnego klienta to je po prostu przekazuje klientowi. Klient nie musi o nie pytac bo o nowych wiadomosciach powie mu serwer.

edytowany 1x, ostatnio: tk
MI
  • Rejestracja:prawie 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:199
0

ok, w jaki sposób serwer może powiedzieć klientowi? Mam mieć tabelę userów, którzy się logowali i trzymać w niej kolumny numer oraz IP?
IP może być zmienne, zatem żeby wysłać wiadomość do konkretnego klienta musiałbym mieć jego aktualne IP.

TK
  • Rejestracja:ponad 13 lat
  • Ostatnio:16 dni
  • Postów:342
1

Nie wiem jak chcesz rozwiazac komunikacje. Jezeli wykorzystasz do komunikacji gniazda to adres IP bedzie Cie interesowal glownie po stronie klienta i glownie w trakcie nawiazywania polaczenia. Jezeli klient polaczy sie z serwerem to wlasciwie nie musisz juz sie zadnymi adresami IP przejmowac. Po stronie serwera zaczniesz pisac do gniazda reprezentujacego klienta a klient bedzie czytal z gniazda reprezentujacego serwer.

Adres IP to moze i byc zmienny, ale po stronie klienta. Po stronie serwera powinien byc raczej staly i na dodatek zewnetrzny. Poniewaz to klient musi znac adres serwera w celu nawiazania polaczenia nie masz sie co przejmowac zmiennoscia adresu klienta. Po prostu serwer bedzie nasluchiwal pod okreslonym adresem na okreslonym porcie. Jezeli wykryjesz probe nawiazania polaczenia to je akceptujesz i zapamietujesz gdzies gniazdo klienta, ktory sie polaczyl. Nastepnie w celu wyslania wiadomosci po prostu piszesz do gniazda i reszta sie nie martwisz.

W Qt za duzo nie pisalem, ale wydaje mi sie, ze powinienes zainteresowac sie takimi klasami jak QTcpServer i QTcpSocket .

Tu masz pierwszy z brzegu przyklad chata: http://thesmithfam.org/blog/2009/07/09/example-qt-chat-program/

MI
  • Rejestracja:prawie 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:199
0

dobrze, w takim razie jeżeli serwer przyjmie od kogoś wiadomość i jej adresatem jest 123456, to serwer musi sprawdzić czy klient jest na liście socketów ( liniowo) w następujący sposób:
Mam obiekt klienta, gdzie mam zapamiętatany jego socket i nr, który go reprezentuje. Przeszukuje wszystkich klientów w celu znalezienia właściewego numeru?
Chodzi tu o sam sposób, bo można oczywiście szukać w czasie logarytmicznym. ( numery są porównywalne)

edytowany 2x, ostatnio: mielony
TK
  • Rejestracja:ponad 13 lat
  • Ostatnio:16 dni
  • Postów:342
1
mielony napisał(a):

dobrze, w takim razie jeżeli serwer przyjmie od kogoś wiadomość i jej adresatem jest 123456, to serwer musi sprawdzić czy klient jest na liście socketów ( liniowo) w następujący sposób:

Mam obiekt klienta, gdzie mam zapamiętatany jego socket i nr, który go reprezentuje. Przeszukuje wszystkich klientów w celu znalezienia właściewego numeru?

Tak, mniej-wiecej o to chodzi. Jezeli znajdziesz adresata na liscie soketow to przesylasz do niego komunikat nadawcy (piszesz do socketa adresata). Tylko musisz pamietac o tym aby w razie utraty polaczenia zaktualizowac liste socketow.

No i inna sprawa ze nie tylko szukasz numerow ale szukasz tez socketow. Jezeli z socketa (nadawcy) przyjdzie jakas wiadomosc to musisz poszukac numeru (nadawcy), zeby wiedziec kto to przyslal. Jak juz bedziesz mial ten numer (nadawcy) to szukasz gniazda (odbiorcy) na podstawie numeru (odbiorcy). Numer odbiorcy ustalisz na podstawie pakietu wyslanego przez nadawce (w pakiecie powinna sie znajdowac informacja do kogo chce wyslac i co). Odbiorcy wysylasz nie tylko wiadomosc ale takze informacje o identyfikatorze nadawcy, tak zeby wiedzial od kogo to przyszlo.

mielony napisał(a):

Chodzi tu o sam sposób, bo można oczywiście szukać w czasie logarytmicznym. ( numery są porównywalne)

Sposob wydaje sie ok, tylko jak sam zauwazyles niekoniecznie musisz wyszukiwac dane w czasie liniowym. Lepiej byloby uzyc dwoch kolekcji typu QHash. Kluczem pierwszej z nich bylby identyfikator uzytkownika, wartoscia socket. W drugiej odrwotnie.

Takie rozwiazanie powinno pozwolic Ci wyszukiwac w senownym czasie uzytkownika na podstawie gniazda i gniazda na podstawie uzytkownika.

Sa oczywiscie kolekcje, ktore spelniaja obie te funkcje na raz, ale nie wiem czy znajdziesz je w Qt / standardowym C++. Jezeli tak to je wykorzystaj (lepiej pracowac na jednej kolekcji niz na dwoch), jezeli nie to albo skorzystaj z zewnetrzenej biblioteki albo pozostan przy dwoch kolekcjach.

edytowany 4x, ostatnio: tk
1

Robiłem kiedyś coś bardzo podobnego do tego co chcesz zrobić.

Zacznij od przeczytania jakiegoś artukłu/poradniki o programowaniu sieciowym pod Windowsem to pozwoli Ci już zrozumieć jak większość tych rzeczy działa.

Gdy ja pisałem serwer to jako, że to był projekt tylko do nauki i poduczenia się czegoś to serwer dla każdego nowego podłączonego klienta tworzył nowy wątek i obsługiwał w nieskończonej pętli danego klienta aż do utracenia z nim połączenia. Wybrałem takie podejście bo używałem bokujących socketów.

A klient bardzo podobnie działał - 1 wątek do pisania wiadomości w konsoli i ich wysyłania, a drugi to nieskończona pętla, gdzie odbierał komunikaty od serwer aż utracił z nim połączenie.

Jeśli np będziesz chciał wysłać do kogoś wiadomość, a on nie będzie podłączony do serwera to możesz np tworzyć osobny plik w którym będziesz zapisywał wszystkie takie wiadomości np w fomracie:

wiadomosc;id_klienta
wiadomosc;id_klienta

id_klienta to musiałby być jakis unikalnie nadawny numerek przez serwer, ponieważ w końcu z jednego adresu IP może być wiele użytkowników - chyba że wyłączasz taką możliwość to wtedy:

wiadomosc;ip
wiadomosc;ip

I gdy do serwera przyjdzie nowe połączenie z danego adresu IP to wyślesz mi wszystkie zaległe wiadmości.

MI
  • Rejestracja:prawie 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:199
0

Tak, mniej-wiecej o to chodzi. Jezeli znajdziesz adresata na liscie soketow to przesylasz do niego komunikat nadawcy (piszesz do socketa adresata). Tylko musisz pamietac o tym aby w razie utraty polaczenia zaktualizowac liste socketow.

Ok, ale myślę też o tym tak:
Mam kolekcję klientów obecnie wpiętych. Ja widzę to tak:
Każdy klient ma wątek, który co jakiś czas (minutę?) wysyła do serwera komunikat: Drogi serwerze, jestem nadal podłączony. Zapamiętuję dla klienta ten czas jako pole.
W takiej sytuacji serwer również ma wątek, który przegląda ( ciągle?) kolekcję klientów i jeżeli nie przedłużył czasu to go odpinam.

Inną sprawą jest fakt, że po wysłaniu wiadomości do klienta i tak muszę brać potwierdzenie dojścia wiadomości. To będzie jedyne zapewnienie.

Pytanie:
O serwerze QTcpServer pisze, że działa on asynchronicznie, czy może mi ktoś powiedzieć co to dokładnie znaczy :)?
pozdrawiam :)

Lucas Darkstorm
Lucas Darkstorm
  • Rejestracja:około 13 lat
  • Ostatnio:prawie 4 lata
0
mielony napisał(a):

O serwerze QTcpServer pisze, że działa on asynchronicznie, czy może mi ktoś powiedzieć co to dokładnie znaczy :)?
pozdrawiam :)

To znaczy mniej więcej tyle, że nie musisz specjalnie tworzyć dodatkowego wątku, aby serwer mógł odbierać połączenia. http://xion.org.pl/2008/03/10/asynchronicznosc-kontra-watki/ <-- tu jest fajnie napisane, o co chodzi z asynchronicznością.

edytowany 1x, ostatnio: Lucas Darkstorm
TK
  • Rejestracja:ponad 13 lat
  • Ostatnio:16 dni
  • Postów:342
1
mielony napisał(a):

Ok, ale myślę też o tym tak:
Mam kolekcję klientów obecnie wpiętych. Ja widzę to tak:
Każdy klient ma wątek, który co jakiś czas (minutę?) wysyła do serwera komunikat: Drogi serwerze, jestem nadal podłączony. Zapamiętuję dla klienta ten czas jako pole.
W takiej sytuacji serwer również ma wątek, który przegląda ( ciągle?) kolekcję klientów i jeżeli nie przedłużył czasu to go odpinam.

Moim zdaniem dobrze do tego podchodzisz. Pamietaj jednak, ze rozwiazanie, o ktorym piszesz nie jest po to aby w normalny sposob obsluzyc zerwanie polaczenia. Stosuje sie je po to aby zrobic porzadek z klientami ktorzy rozlaczyli sie z serwerem w nietypowy sposob.

W normalnej sytuacji powinienes reagowac takze na zdarzenie zerwania polaczenia i zdarzenia bledow komunikacji. Jezeli wiec obsluzysz zdarzenie rozlaczenia sie klienta to wtedy go odlaczasz od razu i nie czekasz az uplynie iles tam minut od jego ostatniej aktywnosci.

Rowniez pozdr :)

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)