Odpowiedzi na te pytania różnią się w zależności od tego, czy korzystasz z gniazda strumieniowego ( SOCK_STREAM
), czy gniazda datagramowego ( SOCK_DGRAM
) - w ramach protokołu TCP / IP pierwsze odpowiada TCP, a drugie UDP.
Skąd wiesz, do jakiej wielkości należy przekazać bufor recv()
?
SOCK_STREAM
: To naprawdę nie ma większego znaczenia. Jeśli twój protokół jest protokołem transakcyjnym / interaktywnym, po prostu wybierz rozmiar, który może pomieścić największą pojedynczą wiadomość / polecenie, jakiego można by się spodziewać (3000 jest prawdopodobnie w porządku). Jeśli twój protokół przesyła dane masowo, większe bufory mogą być bardziej wydajne - dobra zasada jest taka sama, jak rozmiar bufora odbioru jądra gniazda (często około 256kB).
SOCK_DGRAM
: Użyj wystarczająco dużego bufora, aby pomieścić największy pakiet, jaki kiedykolwiek wysłał Twój protokół na poziomie aplikacji. Jeśli używasz UDP, generalnie protokół na poziomie aplikacji nie powinien wysyłać pakietów większych niż około 1400 bajtów, ponieważ z pewnością będą musiały zostać pofragmentowane i ponownie złożone.
Co się stanie, jeśli recv
pakiet będzie większy niż bufor?
SOCK_STREAM
: Pytanie tak naprawdę nie ma sensu, ponieważ gniazda strumieniowe nie mają pojęcia pakietów - są one po prostu ciągłym strumieniem bajtów. Jeśli dostępnych jest więcej bajtów do odczytania, niż ma miejsce w buforze, zostaną one umieszczone w kolejce przez system operacyjny i dostępne do następnego wywołania recv
.
SOCK_DGRAM
: Nadmiarowe bajty są odrzucane.
Skąd mam wiedzieć, czy otrzymałem całą wiadomość?
SOCK_STREAM
: Musisz zbudować jakiś sposób określania końca wiadomości w protokole na poziomie aplikacji. Zwykle jest to albo przedrostek długości (rozpoczynający każdą wiadomość od jej długości) lub ogranicznik końca wiadomości (który może być na przykład znakiem nowej linii w protokole tekstowym). Trzecią, rzadziej używaną opcją jest ustalenie stałego rozmiaru każdej wiadomości. Możliwe są również kombinacje tych opcji - na przykład nagłówek o stałym rozmiarze, który zawiera wartość długości.
SOCK_DGRAM
: Pojedyncze recv
wywołanie zawsze zwraca pojedynczy datagram.
Czy istnieje sposób, aby bufor nie miał ustalonej ilości miejsca, aby móc dodawać do niego dane bez obawy, że zabraknie miejsca?
Nie. Możesz jednak spróbować zmienić rozmiar bufora za pomocą realloc()
(jeśli pierwotnie przydzielono go za pomocą malloc()
lub calloc()
).
recv
do bufora po częściowej wiadomości. Nie powinieneś używaćstrstr()
na surowym buforze wypełnionym przezrecv()
- nie ma gwarancji, że zawiera on terminator nul, więc może to spowodowaćstrstr()
awarię.recv()
aby zapisać więcej bajtów niż pozostało miejsca.W przypadku protokołów przesyłania strumieniowego, takich jak TCP, możesz prawie ustawić bufor na dowolny rozmiar. To powiedziawszy, zalecane są wspólne wartości, które są potęgami 2, takie jak 4096 lub 8192.
Jeśli jest więcej danych niż twój bufor, zostanie po prostu zapisany w jądrze do następnego wywołania
recv
.Tak, możesz dalej zwiększać swój bufor. Możesz zrobić recv do środka bufora, zaczynając od offsetu
idx
, zrobiłbyś:recv(socket, recv_buffer + idx, recv_buffer_size - idx, 0);
źródło
Jeśli masz
SOCK_STREAM
gniazdo,recv
pobiera po prostu „do pierwszych 3000 bajtów” ze strumienia. Nie ma jasnych wskazówek, jak duży jest bufor: jedyny moment, w którym wiesz, jak duży jest strumień, to koniec ;-).Jeśli masz
SOCK_DGRAM
gniazdo, a datagram jest większy niż bufor,recv
wypełnia bufor pierwszą częścią datagramu, zwraca -1 i ustawia errno na EMSGSIZE. Niestety, jeśli protokołem jest UDP, oznacza to utratę reszty datagramu - po części dlatego UDP nazywa się protokołem zawodnym (wiem, że istnieją niezawodne protokoły datagramowe, ale nie są one zbyt popularne - nie mogłem nazwij jeden z rodziny TCP / IP, pomimo tego, że dobrze go znam ;-).Aby dynamicznie powiększyć bufor, przydziel go początkowo za pomocą
malloc
i używajrealloc
w razie potrzeby. Ale to nie pomoże ci zerecv
źródła UDP, niestety.źródło
W przypadku
SOCK_STREAM
gniazda rozmiar bufora tak naprawdę nie ma znaczenia, ponieważ pobierasz tylko część oczekujących bajtów i możesz pobrać więcej w następnym wywołaniu. Po prostu wybierz dowolny rozmiar bufora, na jaki Cię stać.W przypadku
SOCK_DGRAM
gniazda otrzymasz pasującą część wiadomości oczekującej, a reszta zostanie odrzucona. Możesz uzyskać rozmiar oczekującego datagramu za pomocą następującego ioctl:#include <sys/ioctl.h> int size; ioctl(sockfd, FIONREAD, &size);
Alternatywnie można użyć
MSG_PEEK
iMSG_TRUNC
flagirecv()
wezwanie do uzyskania wielkości oczekujących datagramów.ssize_t size = recv(sockfd, buf, len, MSG_PEEK | MSG_TRUNC);
Musisz
MSG_PEEK
zajrzeć (nie odebrać) oczekującej wiadomości - recv zwraca rzeczywisty, nie obcięty rozmiar; i nie musiszMSG_TRUNC
przepełniać obecnego bufora.Wtedy możesz tylko
malloc(size)
prawdziwy bufor irecv()
datagram.źródło
malloc()
dla bufora 64KBMSG_TRUNC
jest to niepotrzebne?SOCK_DGRAM
to nie tylko UDP.Nie ma absolutnej odpowiedzi na twoje pytanie, ponieważ technologia zawsze musi być związana z konkretną implementacją. Zakładam, że komunikujesz się w protokole UDP, ponieważ rozmiar bufora przychodzącego nie powoduje problemów w komunikacji TCP.
Zgodnie z RFC 768 , rozmiar pakietu (łącznie z nagłówkiem) dla UDP może wynosić od 8 do 65 515 bajtów. Tak więc rozmiar odporny na awarie dla bufora przychodzącego wynosi 65 507 bajtów (~ 64 KB)
Jednak nie wszystkie duże pakiety mogą być prawidłowo trasowane przez urządzenia sieciowe. Więcej informacji można znaleźć w istniejącej dyskusji:
Jaki jest optymalny rozmiar pakietu UDP dla maksymalnej przepustowości?
Jaki jest największy rozmiar bezpiecznego pakietu UDP w Internecie
źródło
16kb ma rację; jeśli używasz gigabitowej sieci Ethernet, każdy pakiet może mieć rozmiar 9 kb.
źródło