sockety problem

Problemy dotyczące programowania.

Moderatorzy: Moderatorzy, Administratorzy

infern
Użytkownik
Posty: 268
Rejestracja: 2006-10-26, 14:38
Lokalizacja: Wrocław

sockety problem

Post autor: infern »

Witam,

Oto kod obsługujący wymianę informacji przez gniazdo:

Kod: Zaznacz cały

void HandleCli(int clnSocket)
{
    char Buffer[BUFOR];
    int recvMsgSize;
    string mes;

    for (;;)
    {
        if ((recvMsgSize = recv(clnSocket, Buffer, sizeof(Buffer), 0)) < 0)
              ErrorOccured((char *)"Blad odebranych danych");

        if (recvMsgSize == 0) break;

        mes = Buffer;
        cout << mes << endl;
        cout << mes.length() << endl;
		mes.clear();
		cout << clnSocket << endl;

        for(int i=0;i<BUFOR;i++) Buffer[i]='\0';
    }
    close(clnSocket);
}
1. Czy aby napewno tak if (recvMsgSize == 0) break; kończyć pętle, jeżeli klient się odłączył niespodziewanie? W innym przypadku poprostu klient wyśle jakieś info w stylu (BYE)
2. Czy tak warto czyścić bufor? for(int i=0;i<BUFOR;i++) Buffer='\0';
3. Problem, jeżeli klient wysyła dane liniami, np:
Pierwsze dane
Drugie dane

To mój serwer interpretuje to jako jedną linię:

Kod: Zaznacz cały

Pierwsze dane
Drugie dane
Koniec

35
Chcę by każdą poszczególną linię odczytywał do bufora analizował i tak w kółko w pętli. Można temu zaradzić odbierając znak po znaku i czekając na '\n' ale ja nie chcę tego tak realizować, można też to zrobić na zasadzie wysłania linii i czekania na potwierdzenie, a może podsuniecie dodatkowy pomysł?
Pozdrawiam Tomek
Awatar użytkownika
Hannibal
Moderator w st. spocz.
Posty: 1644
Rejestracja: 2004-06-08, 16:03
Lokalizacja: Łódź

Re: sockety problem

Post autor: Hannibal »

Ostatni raz coś na socketach pisałem wieki temu, więc traktuj to bardzo poważnie bo mogę się mylić.

Po pierwsze nie używaj aktywnego czekania na dane. Użyj "select" lub 'poll'.

Po drugie, socket zachowuje się jak zwykły deskryptor pliku więc może użyć funkcji z stdio.h. W twoim przypadku przydatne będą "fdopen" i "fgets".
[size=75]Hannibal@current@2.6.X[/size]
Awatar użytkownika
mina86
Moderator
Posty: 3343
Rejestracja: 2004-06-14, 21:58
Lokalizacja: Linux 5.x x86_64
Kontakt:

Re: sockety problem

Post autor: mina86 »

W pierwszej kwestii recv() zwraca zero jeśli gniazdo zostanie poprawnie zamknięte. W przeciwnym wypadku zwróci -1, a zmienna errno będzie opisywała kod błędu.

Co do czyszczenia bufora to oczywiście nie ma ono żadnego sensu, aczkolwiek należy wspomnieć, iż sposób w jaki wypisujesz dane jest błędny. Jeżeli upierasz się, aby wykorzystywać strumienie z C++ to najlepiej wypisywanie wykonać tak:

Kod: Zaznacz cały

std::cout.write(Buffer, recvMsgSize);
A jeśli chcesz odczytywać po linii to albo samemu zaimplementujesz wyszukiwanie końców linii i buforowanie, albo zastosujesz się do rady Hannibala. Osobiście preferuje to pierwsze podejście, ale to drugie może być prostsze.

Na zakończenie jeszcze doradzę, abyś stosował jednolity i sensowny sposób nazywania zmiennych, funkcji itp. W szczególności jeżeli nazywasz po angielsku to wszystkie. Podobnie jeżeli zmienne są pisane od małej litery to niechaj wszystkie będą. Widzę, że konsekwentnie funkcje nazywasz od wielkiej litery więc tutaj za bardzo strofować Cię nie będę, choć przyznam, że jest to styl nazewnictwa, z którym się jeszcze nie spotkałem w C++.
Po pierwsze nie używaj aktywnego czekania na dane. Użyj "select" lub 'poll'.
Słusznie, że ostrzegłeś, że możesz się mylić, bo właśnie to czynisz. ;)
Zastrzegam sobie prawo nieanalizowania postów pisanych niepoprawną polszczyzną.
Post generated automatically by A.I. system code name ‘mina86’ in response to the previous one.
Awatar użytkownika
Hannibal
Moderator w st. spocz.
Posty: 1644
Rejestracja: 2004-06-08, 16:03
Lokalizacja: Łódź

Re: sockety problem

Post autor: Hannibal »

mina86 pisze:
Po pierwsze nie używaj aktywnego czekania na dane. Użyj "select" lub 'poll'.
Słusznie, że ostrzegłeś, że możesz się mylić, bo właśnie to czynisz. ;)
Było by miło jak byś określił gdzie dokładnie i dlaczego :).
[size=75]Hannibal@current@2.6.X[/size]
Awatar użytkownika
mina86
Moderator
Posty: 3343
Rejestracja: 2004-06-14, 21:58
Lokalizacja: Linux 5.x x86_64
Kontakt:

Re: sockety problem

Post autor: mina86 »

O ile nie został ustawiony tryb nieblokujący funkcja recv() czeka na nadejście danych zatem nie jest to aktywne oczekiwanie.
Zastrzegam sobie prawo nieanalizowania postów pisanych niepoprawną polszczyzną.
Post generated automatically by A.I. system code name ‘mina86’ in response to the previous one.
Awatar użytkownika
Hannibal
Moderator w st. spocz.
Posty: 1644
Rejestracja: 2004-06-08, 16:03
Lokalizacja: Łódź

Re: sockety problem

Post autor: Hannibal »

Faktycznie :wstyd:, ale jeżeli nie użyje recv to już się select przyda :mrgreen:.
[size=75]Hannibal@current@2.6.X[/size]
Awatar użytkownika
mina86
Moderator
Posty: 3343
Rejestracja: 2004-06-14, 21:58
Lokalizacja: Linux 5.x x86_64
Kontakt:

Re: sockety problem

Post autor: mina86 »

Hannibal pisze:ale jeżeli nie użyje recv to już się select przyda :mrgreen:.
Tzn. co innego miałby użyć? read(2) zachowuje się identycznie. fgets(3) to tylko obudowanie do read(2).
Zastrzegam sobie prawo nieanalizowania postów pisanych niepoprawną polszczyzną.
Post generated automatically by A.I. system code name ‘mina86’ in response to the previous one.
Awatar użytkownika
Hannibal
Moderator w st. spocz.
Posty: 1644
Rejestracja: 2004-06-08, 16:03
Lokalizacja: Łódź

Re: sockety problem

Post autor: Hannibal »

Nigdzie w dokumentacji nie jest napisane że read musi się blokować na sockecie. Może wracać z wynikiem "odczytano 0 bajtów". Ale doświadczenie jednak potwierdza twoją wersję :placze:, przynajmniej na Linuksie.

Kod: Zaznacz cały

#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>

int main() {
        int sc[2];
        int i = 1000;
        char buf[10];
        socketpair(AF_UNIX, SOCK_STREAM, 0, sc);
        if (fork()) {
                alarm(2);
                while(i--) {
                        printf("%li\n", read(sc[0], buf, 10));
                        usleep(1000);
                }
        }
        else {
                usleep(250000);
                write(sc[1], "01234", 5);
                usleep(250000);
                write(sc[1], "56789", 5);
        }
        return 0;
}
Daje wynik:

Kod: Zaznacz cały

$ ./a.out
5
5
Budzik
[size=75]Hannibal@current@2.6.X[/size]
Awatar użytkownika
mina86
Moderator
Posty: 3343
Rejestracja: 2004-06-14, 21:58
Lokalizacja: Linux 5.x x86_64
Kontakt:

Re: sockety problem

Post autor: mina86 »

Hannibal pisze:Nigdzie w dokumentacji nie jest napisane że read musi się blokować na sockecie.
Bo nie będzie blokować jeżeli deskryptor pliku ustawiony jest w tryb nieblokujący i dotyczy to nie tylko gniazd, ale też wszelkich innych tworów (np. plików).
Zastrzegam sobie prawo nieanalizowania postów pisanych niepoprawną polszczyzną.
Post generated automatically by A.I. system code name ‘mina86’ in response to the previous one.
infern
Użytkownik
Posty: 268
Rejestracja: 2006-10-26, 14:38
Lokalizacja: Wrocław

Re: sockety problem

Post autor: infern »

mina86 pisze:Co do czyszczenia bufora to oczywiście nie ma ono żadnego sensu
No jeżeli tego nie zrobię, a otrzymałem wcześniej wiadomość dłuższą, to przy następnej wiadomości krótszej mam śmieci, bo wcześniej znak końca znaków '\0' został przesunięty np na 14 pozycję a dostałem 10 znaków na przykład.
mina86 pisze:A jeśli chcesz odczytywać po linii to albo samemu zaimplementujesz wyszukiwanie końców linii i buforowanie, albo zastosujesz się do rady Hannibala. Osobiście preferuje to pierwsze podejście, ale to drugie może być prostsze.
Nie chcę implementować, bo jako marker znacznik, musiał bym stosować jakiś znak niestandardowy :/ Jeśli chodzi o sposób Hannibala to chodzi o nieobsługiwanie wiadomości za pomocą send,recv??
Pozdrawiam Tomek
Pajaczek
Użytkownik
Posty: 1439
Rejestracja: 2006-08-03, 13:16
Lokalizacja: Winny Gród

Re: sockety problem

Post autor: Pajaczek »

infern pisze:No jeżeli tego nie zrobię, a otrzymałem wcześniej wiadomość dłuższą, to przy następnej wiadomości krótszej mam śmieci, bo wcześniej znak końca znaków '\0' został przesunięty np na 14 pozycję a dostałem 10 znaków na przykład.
A od czego jest zwracana liczba odebranych danych, jak myślisz.

Kod: Zaznacz cały

Buffer[recvMsgSize]='\0';
Takie trudne? I miejsce... po sprawdzeniu czy nie jest równe 0 chyba najlepiej, jak myślisz?
Awatar użytkownika
Hannibal
Moderator w st. spocz.
Posty: 1644
Rejestracja: 2004-06-08, 16:03
Lokalizacja: Łódź

Re: sockety problem

Post autor: Hannibal »

mina86 pisze:
Hannibal pisze:Nigdzie w dokumentacji nie jest napisane że read musi się blokować na sockecie.
Bo nie będzie blokować jeżeli deskryptor pliku ustawiony jest w tryb nieblokujący i dotyczy to nie tylko gniazd, ale też wszelkich innych tworów (np. plików).
W tym wypadku chodziło mi raczej o zdanie z mana:
It is not an error if this number [zwracana wartość] is smaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of-file, or because we are reading from a pipe, or from a terminal), or because read() was interrupted by a signal.
Ale prawdopodobnie to tylko moja nadinterpretacja że dotyczy to również socketów "bez danych"...
infern pisze: Jeśli chodzi o sposób Hannibala to chodzi o nieobsługiwanie wiadomości za pomocą send,recv??
Chodzi o to że zamiast recv możesz użyć fgets (poprzedzonego fdopen).
[size=75]Hannibal@current@2.6.X[/size]
Awatar użytkownika
mina86
Moderator
Posty: 3343
Rejestracja: 2004-06-14, 21:58
Lokalizacja: Linux 5.x x86_64
Kontakt:

Re: sockety problem

Post autor: mina86 »

No jeżeli tego nie zrobię, a otrzymałem wcześniej wiadomość dłuższą, to przy następnej wiadomości krótszej mam śmieci, bo wcześniej znak końca znaków '\0' został przesunięty np na 14 pozycję a dostałem 10 znaków na przykład.
Bo źle wypisujesz dane, już o tym napisałem podając jak powinieneś to robić.
infern pisze:Nie chcę implementować, bo jako marker znacznik, musiał bym stosować jakiś znak niestandardowy :/
Nie rozumiem.
Pajaczek pisze:Buffer[recvMsgSize]='\0';
I błąd przepełnienia bufora gotowy.

Hannibal, fakt, że funkcja nie czeka, aż wszystkie żądane bajty będą dostępne i odebrany nie oznacza, że funkcja jest nieblokująca, czy też, że gdzieś stosowane jest aktywne oczekiwanie.
Ostatnio zmieniony 2009-10-17, 10:11 przez mina86, łącznie zmieniany 1 raz.
Zastrzegam sobie prawo nieanalizowania postów pisanych niepoprawną polszczyzną.
Post generated automatically by A.I. system code name ‘mina86’ in response to the previous one.
Pajaczek
Użytkownik
Posty: 1439
Rejestracja: 2006-08-03, 13:16
Lokalizacja: Winny Gród

Re: sockety problem

Post autor: Pajaczek »

mina86 pisze:
Pajaczek pisze: Buffer[recvMsgSize]='\0';
I błąd przepełnienia bufora gotowy.
Zakładasz, że Buffer będzie rozmiaru recvMsgSize, albo mniejszy. Inteligentny programista powinien wiedzieć co robi, i zarezerwować bufor co najmniej wielkości największej spodziewanej (możliwej) wiadomości +1, a wówczas jak sobie wyobrażasz przepełnienie bufora takim sposobem?

Oczywiście poprawniejszy byłby jeszcze wcześniej warunek

Kod: Zaznacz cały

if (recvMsgSize < 1) break; 
zamiast tego co tam jest. Trochę się komplikuje, jeśli chcemy wcześniej odczytać kod błędu, ale to też zaledwie jeden warunek if więcej.
Awatar użytkownika
mina86
Moderator
Posty: 3343
Rejestracja: 2004-06-14, 21:58
Lokalizacja: Linux 5.x x86_64
Kontakt:

Re: sockety problem

Post autor: mina86 »

Zakładasz, że Buffer będzie rozmiaru recvMsgSize, albo mniejszy. Inteligentny programista powinien wiedzieć co robi, i zarezerwować bufor co najmniej wielkości największej spodziewanej (możliwej) wiadomości +1, a wówczas jak sobie wyobrażasz przepełnienie bufora takim sposobem?
Cały kod funkcji jest dostępny zatem nie ma się co zastanawiać co by ktoś zrobił. Poprawne jest użycie metody write() obiektu std::ostream i nie ma się co zastanawiać nad czymkolwiek innym.
Zastrzegam sobie prawo nieanalizowania postów pisanych niepoprawną polszczyzną.
Post generated automatically by A.I. system code name ‘mina86’ in response to the previous one.
ODPOWIEDZ