2009-09-06 5 views
11

Czytałem przez Beej's Guide to Network Programming, aby uzyskać uchwyt połączeń TCP. W jednej z próbek kodu klienta dla prostych klienta strumienia TCP wygląda:Obsługa częściowego powrotu z recv() TCP w C

if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) { 
    perror("recv"); 
    exit(1); 
} 

buf[numbytes] = '\0'; 

printf("Client: received '%s'\n", buf); 

close(sockfd); 

mam ustawić bufor być mniejsza niż całkowita liczba bajtów, które wyślę. Nie jestem całkiem pewien, jak mogę uzyskać inne bajty. Czy muszę przechodzić w pętlę przez recv(), dopóki nie otrzymam '\0'?

* Uwaga po stronie serwera Wdrażam również jego funkcję sendall(), więc powinno być wysyłanie wszystkiego do klienta.

Zobacz także 6.1. A Simple Stream Server w przewodniku.

Odpowiedz

12

Tak, będziesz potrzebować wielu połączeń recv(), dopóki nie masz wszystkich danych.

Aby dowiedzieć się, kiedy to się dzieje, używanie statusu zwrotu z poziomu recv() nie jest dobre - informuje tylko o liczbie otrzymanych bajtów, a nie o liczbie bajtów, ponieważ niektóre z nich nadal mogą być przesyłane.

Lepiej jest, jeśli dane, które otrzymasz, w jakiś sposób kodują długość wszystkich danych. Odczytaj tyle danych, aż będziesz wiedział, jaka jest ich długość, a następnie czytaj je, dopóki nie otrzymasz danych length. Aby to zrobić, możliwe są różne podejścia; powszechnym jest utworzenie bufora wystarczająco dużego, aby przechowywać wszystkie dane, gdy tylko wiesz, jaka jest długość.

Innym podejściem jest użycie buforów o ustalonym rozmiarze i zawsze staram się otrzymywać min(missing, bufsize), zmniejszając po po każdym recv().

+5

Jeszcze innym sposobem jest użycie końcowy bajt ze specjalnym wartość (np. ETX), jeśli jesteś pewien, że ta wartość nie może pojawić się w komunikacie lub początek bajtu (STX) i bajt końcowy (ETX). Jest trudniejsze w obsłudze po stronie odbiornika, ale bardziej wytrzymałe. Jeśli coś poszło nie tak i niektóre dane utracone w transmisji, przepływ danych może być łatwo zsynchronizowany ze STX/ETX, ale w przypadku prefiksu długości całe piekło się rozpada. – qrdl

9

Pierwszą rzeczą, którą trzeba się nauczyć, gdy robi TCP/IP programowanie: 1 write/send połączenia może potrwać kilka recv połączeń do odbioru, a kilka Pisanie/wysyłanie połączeń może potrzebować tylko 1 recv wezwanie do odbioru. I wszystko pomiędzy.

Będziesz potrzebował pętli, dopóki nie masz wszystkich danych. Wartość zwracana recv() informuje, ile otrzymałeś danych. Jeśli chcesz po prostu otrzymywać wszystkie dane z połączenia TCP, możesz wykonać pętlę, aż recv() zwróci 0 - pod warunkiem, że drugi koniec zamknie połączenie TCP po zakończeniu wysyłania.

Jeśli wysyłasz rekordy/linie/pakiety/komendy lub coś podobnego, musisz utworzyć własny protokół przez TCP, co może być tak proste, jak "polecenia są ograniczone przez \n".

Prostym sposobem odczytywania/parsowania takiego polecenia byłoby odczytywanie 1 bajtu na raz, budowanie bufora z odebranymi bajtami i sprawdzanie bajtu \n za każdym razem. Odczyt 1 bajta jest niezwykle nieefektywny, więc powinieneś czytać większe fragmenty naraz.

Ponieważ TCP jest strumień zorientowany i nie świadczy zapis/granice wiadomość staje się nieco bardziej skomplikowane - Można by trzeba recv kawałek bajtów, należy sprawdzić w otrzymanym buforze o \n bajt, jeśli jest tam - dołącz bajty do wcześniej odebranych bajtów i wypisz ten komunikat. Następnie sprawdź pozostałą część bufora po \n - która może zawierać kolejną całą wiadomość lub tylko początek innej wiadomości.

1

Tak, masz do pętli nad recv() momentu otrzymania '\0' lub błąd zdarzyć (wartość ujemna) lub z recv0 z recv(). Pierwsza opcja: tylko jeśli to zero jest częścią twojego protokołu (serwer go wysyła). Jednak z twojego kodu wydaje się, że zero jest po prostu możliwe, aby użyć zawartości bufora jako ciągi znaków (po stronie klienta).

Czek o wartości zwracanej 0 z recv: oznacza to, że połączenie zostało zamknięte (może to być część swojego protokołu, że tak się dzieje.)

+0

Możesz otrzymać bajty o wartości "\ 0"; Problem polega na tym, że liczba zwróconych bajtów wynosi 0. –

+0

Dlaczego miałby otrzymać '\ 0'? Chyba że on wysłał? W pytaniu na ten temat nie ma dowodów. – EJP