W trybie synchronicznym, po udanym zakończeniu funkcji gg_login()
, należy w pętli wywoływać funkcję gg_watch_fd()
, która po odebraniu informacji od serwera zwróci informację o zdarzeniu w strukturze gg_event
lub NULL
w przypadku błędu. Lista zdarzeń znajduje się poniżej.
Tryb asynchroniczny wymaga od programu obserwowania zmian na określonych deskryptorach za pomocą funkcji systemowych select()
czy poll()
, lub za pomocą mechanizmów pętli zdarzeń wbudowanych w wykorzystaną bibliotekę interfejsu użytkownika. Interesujące z punktu widzeniu połączenia asynchronicznego pola to fd
określające obserwowany deskryptor, pole check
będące maską bitową typu gg_check_t
, mówiącą czy obserwowana ma być możliwość odczytu i/lub zapisu oraz timeout
określające maksymalny czas wykonywania operacji. Gdy zaobserwuje się zmianę na deskryptorze należy wywołać funkcję gg_watch_fd()
i postępować podobnie jak w trybie synchronicznym.
timeout
, należy sprawdzić wartość flagi soft_timeout
. Jeśli jest równa 0
(tj. FALSE
), można przerwać połączenie i zwolnić zasoby, a jeśli jest różna (tj. TRUE
), należy wywołać gg_watch_fd()
ustawiając wcześniej timeout
na 0
, by dać szansę bibliotece zareagować na przekroczenie czasu operacji. Za pomocą mechanizmu soft_timeout
są realizowane próby połączenia z innymi portami, np. gdy domyślny port 8074 jest zablokowany oraz zwrotne połączenia bezpośrednie (7.x), gdy jedna ze stron połączenia znajduje się za routerem NAT lub firewallem.
Po zerwaniu połączenia lub nieudanym logowaniu pole state
przyjmuje wartość GG_STATE_IDLE
. Przed dodaniem deskryptora fd
do listy obserwowanych, warto w ten sposób sprawdzić, czy dane połączenie nie jest już nieaktywne.
Próba wysłania danych do zamkniętego połączenia (np. zerwanego przez serwer) w systemach uniksowych powoduje wysłanie sygnału SIGPIPE
, który domyślnie powoduje unicestwienie procesu. Dlatego, aby pozwolić bibliotece zareagować na zerwanie połączenia w sensowny sposób, należy ignorować sygnał w aplikacji.
struct gg_session *sesja; struct gg_login_params parametry; struct gg_event *zdarzenie; memset(¶metry, 0, sizeof(parametry)); parametry.uin = 12345; parametry.password = "hasło"; sesja = gg_login(¶metry); if (!sesja) { błąd("Nie można się połączyć"); exit(1); } informacja("Połączono"); gg_send_message(sesja, 23456, "Cześć!"); while ((zdarzenie = gg_watch_fd(sesja))) { switch (zdarzenie->type) { // ... } gg_event_free(zdarzenie); } gg_logoff(sesja); gg_free_session(sesja);
gg_ping()
.struct gg_session *sesja; struct gg_login_params parametry; struct timeval tv; fd_set rd, wd; int wynik; memset(¶metry, 0, sizeof(parametry)); parametry.uin = 12345; parametry.password = "hasło"; parametry.async = 1; sesja = gg_login(¶metry); if (!sesja) { błąd("Nie można się połączyć"); exit(1); } for (;;) { FD_ZERO(&rd); FD_ZERO(&wd); if ((sesja->check & GG_CHECK_READ)) FD_SET(sesja->fd, &rd); if ((sesja->check & GG_CHECK_WRITE)) FD_SET(sesja->fd, &wd); if (sesja->timeout) { tv.tv_sec = sesja->timeout; tv.tv_usec = 0; } wynik = select(sesja->fd + 1, &rd, &wd, NULL, (sesja->timeout) ? &tv : NULL); if (!wynik) { błąd("Przekroczono czas operacji"); gg_free_session(sesja); exit(1); } if (wynik == -1) { if (errno != EINTR) { błąd("Błąd funkcji select()"); gg_free_session(sesja); exit(1); } } if (FD_ISSET(sesja->fd, &rd) || FD_ISSET(sesja->fd, &wd)) { struct gg_event *zdarzenie; zdarzenie = gg_watch_fd(sesja); if (!zdarzenie) { błąd("Połączenie przerwane"); gg_free_session(sesja); exit(1); } switch (zdarzenie->type) { case GG_EVENT_CONN_SUCCESS: informacja("Połączono"); break; case GG_EVENT_CONN_FAILED: błąd("Nie można się połączyć"); gg_event_free(zdarzenie); gg_free_session(sesja); exit(1); // ... } gg_event_free(zdarzenie); } }
gg_ping()
.Typ zdarzenia | Pole gg_event | Typ pola | Opis |
---|---|---|---|
GG_EVENT_NONE | - | - | Nie wydarzyło się nic wartego uwagi. |
Zdarzenia związane z połączeniem | |||
GG_EVENT_CONN_SUCCESS | - | - | Połączono z serwerem. Pierwszą rzeczą, jaką należy zrobić jest wysłanie listy kontaktów. |
GG_EVENT_CONN_FAILED | event.failure | gg_failure_t | Nie udało się połączyć. |
GG_EVENT_PONG | - | - | Utrzymanie połączenia. Obecnie serwer nie wysyła już do klienta ramek utrzymania połączenia, polega wyłącznie na wysyłaniu ramek przez klienta. |
GG_EVENT_DISCONNECT | - | - | Serwer zrywa połączenie. Zdarza się, gdy równolegle do serwera podłączy się druga sesja i trzeba zerwać połączenie z pierwszą. |
Wiadomości | |||
GG_EVENT_XML_EVENT | event.xml_event | gg_event_xml_event | Otrzymano komunikat systemowy (7.7). |
GG_EVENT_MSG | event.msg | gg_event_msg | Otrzymano wiadomość. Przekazuje również wiadomości systemowe od numeru 0. |
GG_EVENT_ACK | event.ack | gg_event_ack | Potwierdzenie doręczenia wiadomości. |
GG_EVENT_IMAGE_REQUEST | event.image_request | gg_event_image_request | Żądanie przesłania obrazka z wiadommości. |
GG_EVENT_IMAGE_REPLY | event.image_reply | gg_event_image_reply | Przysłano obrazek z wiadomości. |
Lista kontaktów | |||
GG_EVENT_NOTIFY | event.notify | gg_notify_reply | Informacja o statusach osób z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. |
GG_EVENT_NOTIFY_DESCR | event.notify_descr | gg_event_notify_descr | Informacja o statusie opisowym osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. |
GG_EVENT_STATUS | event.status | gg_event_status | Zmiana statusu osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. |
GG_EVENT_NOTIFY60 | event.notify60 | gg_event_notify60 | Informacja o statusach osób z listy kontaktów. |
GG_EVENT_STATUS60 | event.status60 | gg_event_status60 | Zmiana statusu osoby z listy kontaktów. |
GG_EVENT_USERLIST | event.userlist | gg_event_userlist | Wynik importu lub eksportu listy kontaktów. |
Katalog publiczny | |||
GG_EVENT_PUBDIR50_SEARCH_REPLY | event.pubdir50 | gg_pubdir50_t | Odpowiedź katalogu publicznego. |
GG_EVENT_PUBDIR50_READ | event.pubdir50 | gg_pubdir50_t | Odczytano własne dane z katalogu publicznego. |
GG_EVENT_PUBDIR50_WRITE | event.pubdir50 | gg_pubdir50_t | Zmieniono własne dane w katalogu publicznym. |
Połączenia bezpośrednie | |||
GG_EVENT_DCC7_NEW | event.dcc7_new | gg_dcc7 | Nowe połączenie bezpośrednie (7.x). |
GG_EVENT_DCC7_ACCEPT | event.dcc7_accept | gg_event_dcc7_accept | Zaakceptowano połączenie bezpośrednie (7.x), nowy deskryptor. |
GG_EVENT_DCC7_REJECT | event.dcc7_reject | gg_event_dcc7_reject | Odrzucono połączenie bezpośrednie (7.x). |
GG_EVENT_DCC7_ERROR | event.dcc7_error | gg_error_t | Błąd połączenia bezpośredniego (7.x). |
enum gg_check_t |
enum gg_event_t |
Rodzaj zdarzenia.
void gg_event_free | ( | struct gg_event * | e | ) |
Zwalnia pamięć zajmowaną przez informację o zdarzeniu.
Funkcję należy wywoływać za każdym razem gdy funkcja biblioteki zwróci strukturę gg_event
.
e | Struktura zdarzenia |
struct gg_event* gg_watch_fd | ( | struct gg_session * | sess | ) | [read] |
Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze sesji.
Funkcja zwraca strukturę zdarzenia gg_event
. Jeśli rodzaj zdarzenia to GG_EVENT_NONE
, nie wydarzyło się jeszcze nic wartego odnotowania. Strukturę zdarzenia należy zwolnić funkcja gg_event_free()
.
sess | Struktura sesji |
NULL
jeśli wystąpił błąd