// jakiśtam sobie opis API libgadu // (c) copyright 2001-2002 by wojtek kaniewski // robert j. wozny każda sesja jest opisywana przez ,,struct gg_session''. biblioteka może w ramach jednego procesu/wątku obsługiwać tyle sesji, na ile pozwolą zasoby. deklarujemy sobie... struct gg_session *blah; następnie wypadałoby się połączyć. przykład będzie dotyczył socketów nieblokujących, bo w większości aplikacji ciężko sobie pozwolić na zawieszanie programu na czas łączenia. struct gg_login_params p; memset(&p, 0, sizeof(p)); p.uin = 123456; p.password = "hasło"; p.async = 1; p.status = GG_STATUS_INVISIBLE; if (!(blah = gg_login(&p))) my_error(); jeśli uda się rozpocząć proces łączenia, dostajemy wskaźnik do struktury, inaczej NULL. wywołanie gg_login() powoduje uruchomienie drugiego procesu w tle, który wywoła gethostbyname() i przez pipe'a zwróci wynik. później połączy się z serwerem, wyśle, odbierze, połączy się ze wskazanym adresem IP, zaloguje się itd. jako że wszystko dzieję się w tle, klient musi sprawdzać cały czas podane deskryptory. pole ,,blah->fd'' zawiera deskryptor, a ,,blah->check'' jest bitmapą i zawiera GG_CHECK_READ i/lub GG_CHECK_WRITE jeśli mamy sprawdzić czy przyszły nowe dane i/lub możemy wysyłać. jeśli coś się wydarzy, wywołujemy ,,gg_watch_fd()'', a libgadu sobie już sprawdzi, co takiego się zdarzyło: while (1) { fd_set rd, wr, ex; FD_ZERO(&rd); FD_ZERO(&wr); FD_ZERO(&ex); if ((blah->check & GG_CHECK_READ)) FD_SET(blah->fd, &rd); if ((blah->check & GG_CHECK_WRITE)) FD_SET(blah->fd, &wr); FD_SET(blah->fd, &ex); if (select(blah->fd + 1, &rd, &wr, &ex, NULL) == -1) my_error(); if (FD_ISSET(blah->fd, &ex)) my_error(); if (FD_ISSET(blah->fd, &rd) || FD_ISSET(blah->fd, &wr)) my_handle_event(); } nie ma tutaj obsługi timeoutów itp. jeśli komuś zależy, niech sobie sam dopisze. poza tym, jeśli program sprawdza też inne deskryptory jak np. stdin dla klientów konsolowych, dobrze byłoby sprawdzić, czy dana sesja coś robi i nie sprawdzać ,,blah->fd'' jeśli ,,blah->state == GG_STATE_IDLE''. no i od czasu do czasu można by dawać znać serwerowi, że coś się dzieje, za pomocą... gg_ping(blah); ale to już wymaga implementacji jakichś timeoutów i liczenia czasu od ostatniego pinga. ,,blah->last_pong'' mówi nam, kiedy serwer ostatni raz odpowiedział na pakiet ping, a ,,blah->last_event'' mówi, kiedy dostaliśmy cokolwiek ostatnio od serwera. wracając do obsługi deskryptorów -- jeśli klient zauważy, że coś się zmieniło na podanym sockecie, powinien wywołać ,,gg_watch_fd()'', która wszystkim się zajmie. zwraca ona wskaźnik do zaalokowanej struktury opisującej zdarzenie. po obejrzeniu należy zwolnić ją za pomocą ,,gg_event_free()''. w powyższym przykładzie jest wywoływana funkcja ,,my_handle_event()'', która może wyglądać tak: struct gg_event *e; if (!(e = gg_watch_fd(blah))) my_error(); switch (e->type) { case GG_EVENT_NONE: case GG_EVENT_PONG: /* olewamy */ break; case GG_EVENT_CONN_SUCCESS: printf("połączono!\n"); /* tutaj wysyłamy userlistę za pomocą gg_notify() */ break; case GG_EVENT_CONN_FAILED: printf("nie udało się\n"); /* powód w e->event.failure, stałe GG_FAILURE_... */ break; case GG_EVENT_MSG: printf("masz wiadomość!\n"); printf("od: %d\n", e->event.msg.sender); printf("treść: %s\n", e->event.msg.message); /* e->event.msg.class mówi czy rozmowa czy wiad. */ /* jeśli e->event.msg.sender równy 0, to mamy */ /* wiadomość systemową o numerze w msg.class */ break; case GG_EVENT_NOTIFY: printf("oto ludzie, którzy się pojawili: "); /* tutaj sprawdzanie tablicy e->event.notify */ break; case GG_EVENT_STATUS: printf("ktoś %d zmienił stan\n", e->event.status.uin); /* nowy stan w e->event.status.status */ break; case GG_EVENT_ACK: printf("wiadomość dotarła do %d.\n", e->event.ack.recipient); /* e->event.ack.status mówi czy dotarła do klienta */ /* czy leży na serwerze, stałe GG_ACK_... */ /* e->event.ack.seq to numerek wiadomości */ break; } gg_event_free(e); przy okazji wiadomo, co oznacza który event. część z nich można ignorować, jeśli robi się okrojonego klienta, np. commandline'owego wysyłającego jedną wiadomość. po zalogowaniu wypadałoby wysłać serwerowi listę użytkowników, którzy nas interesują. ,,gg_notify()'' przyjmuje za argument tablicę zmiennych typu ,,uin_t''. w odpowiedzi dostaniemy GG_EVENT_NOTIFY i tablicę struktur ,,struct gg_notify_reply'', jeśli ktoś jest. po szczegóły odsyłam do libgadu.c, libgadu.h i źródeł konsolowego klienta. jeśli dodajemy lub usuwamy kogoś w trakcie działania, należy skorzystać z ,,gg_add_notify()'' lub ,,gg_remove_notify()''. żeby zmienić stan na zajęty lub dostępny, używamy ,,gg_change_status()''. wysyłanie wiadomości za pomocą ,,gg_send_message()''. parametr ,,class'' mówi, czy ma sie pojawić w osobnym okienku (GG_CLASS_MSG) czy w okienku rozmowy (GG_CLASS_CHAT). funkcja zwraca numer sekwencyjny wiadomości, którego możemy użyć do potwierdzenia. biblioteka libgadu.c wymaga zdefiniowania, albo i nie, WORDS_BIGENDIAN. jest ona definiowana [albo i nie] przez skrypt configure - i jest definiowana [#define WORDS_BIGENDIAN 1] gdy mamy maszynę bigendianową. jeżeli piszesz coś z użyciem glib, możesz wrzucić coś takiego: #include #if G_BYTE_ORDER == G_BIG_ENDIAN # define WORDS_BIGENDIAN 1 #endif nie testowałem, ale powinno działać. jeżeli zaś nie korzystasz z glib, skryptu configure... to bądź ostrzeżony: bez zdefiniowania WORDS_BIGENDIAN na maszynach bigendianowych nie będzie poprawnie pracować ;> $Id: api.txt,v 1.9 2002/08/07 15:08:11 wojtekka Exp $