Jak duży powinien być mój bufor recv podczas wywoływania recv w bibliotece gniazd

Mam kilka pytań dotyczących biblioteki socket w C. oto fragment kodu, do którego odniosę się w moich pytaniach.

char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);
  1. Jak zdecydować, jak duże zrobić recv_buffer? Używam 3000, ale to arbitralne.
  2. co się stanie jeśli recv() otrzyma pakiet większy niż mój bufor?
  3. Skąd mogę wiedzieć, czy otrzymałem całą wiadomość bez ponownego wywołania recv i czy będzie ona czekać w nieskończoność, gdy nie ma nic do odebrania?
  4. czy jest jakiś sposób na bufor nie ma ustalonej ilości miejsca, więc mogę dodawać do niego bez obawy, że zabraknie miejsca? może używając strcat do połączenia ostatniej odpowiedzi recv() z buforem?

Wiem, że to wiele pytań w jednym, ale byłbym bardzo wdzięczny za wszelkie odpowiedzi.

Author: Chen XinZhen, 2010-05-19

6 answers

Odpowiedzi na te pytania różnią się w zależności od tego, czy używasz gniazda strumieniowego (SOCK_STREAM) czy gniazda datagramowego (SOCK_DGRAM) - w TCP/IP pierwsze odpowiada TCP, a drugie UDP.

skąd wiesz, jak duży jest bufor przekazany do recv()?

  • SOCK_STREAM: to nie ma większego znaczenia. Jeśli twój protokół jest transakcyjny / interaktywny, po prostu wybierz rozmiar, który może pomieścić największą pojedynczą wiadomość / polecenie, które chcesz rozsądnie oczekiwać (3000 jest prawdopodobnie w porządku). Jeśli twój protokół przesyła dane zbiorcze, większe bufory mogą być bardziej wydajne - dobra zasada jest w przybliżeniu taka sama jak rozmiar bufora odbiorczego jądra gniazda (często około 256KB).

  • SOCK_DGRAM: Użyj bufora wystarczająco dużego, aby pomieścić największy pakiet, jaki protokół na poziomie aplikacji kiedykolwiek wysłał. Jeśli używasz UDP, to generalnie Twój protokół na poziomie aplikacji nie powinien wysyłać pakietów większych niż około 1400 bajtów, bo na pewno trzeba będzie je pofragmentować i złożyć ponownie.

co się stanie, jeśli recv uzyska pakiet większy niż bufor?

  • SOCK_STREAM: to pytanie nie ma sensu, ponieważ gniazda strumieniowe nie mają pojęcia o pakietach - są tylko ciągłym strumieniem bajtów. Jeśli do odczytania jest więcej bajtów niż miejsce na bufor, będą one ustawione w kolejce przez system operacyjny i dostępne do następnego połączenia do recv.

  • SOCK_DGRAM: nadmiar bajtów jest odrzucany.

Skąd mogę 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 prefiks długości (rozpoczynający każdą wiadomość od długości wiadomości) lub ogranicznik końca wiadomości (który może być tylko nową linią w protokole tekstowym). A po trzecie, rzadziej używane, opcja polega na zleceniu stałej wielkości dla 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 wywołanie recv zawsze Zwraca pojedynczy datagram.

czy istnieje sposób, aby bufor nie miał ustalonej ilości miejsca, aby móc go dodawać bez obawy, że zabraknie miejsca?

Nie. Możesz jednak spróbować zmienić rozmiar bufor używając realloc() (jeśli pierwotnie został przydzielony z malloc() lub calloc(), to znaczy).

 200
Author: caf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-29 02:23:09

Dla protokołów strumieniowych, takich jak TCP, można praktycznie ustawić bufor na dowolny rozmiar. Zaleca się jednak wspólne wartości, które są potęgami 2, takimi jak 4096 lub 8192.

Jeśli jest więcej danych, to jaki jest Twój bufor, zostanie on po prostu zapisany w jądrze do następnego wywołania recv.

Tak, możesz ciągle powiększać swój bufor. Można zrobić recv w środku bufora zaczynając od offsetu idx, można zrobić:
recv(socket, recv_buffer + idx, recv_buffer_size - idx, 0);
 15
Author: R Samuel Klatchko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-19 00:51:45

Jeśli masz gniazdo SOCK_STREAM, recv pobiera "do pierwszych 3000 bajtów" ze strumienia. Nie ma jasnych wskazówek, jak duży jest bufor: jedyny raz wiesz, jak duży jest strumień, jest wtedy, gdy wszystko jest zrobione; -).

Jeśli masz gniazdo SOCK_DGRAM, 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 -- część tego, dlaczego UDP nazywa się niewiarygodnym protokołem (wiem, że istnieją niezawodne protokoły datagramowe, ale nie są one zbyt popularne-nie mogłem wymienić jednego z rodziny TCP/IP, mimo że dobrze znam ten ostatni; -).

Aby dynamicznie rozwijać bufor, najpierw przydziel go za pomocą malloc i użyj realloc w razie potrzeby. Ale to nie pomoże z recv ze źródła UDP, niestety.

 13
Author: Alex Martelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-19 00:28:05

Dla gniazda SOCK_STREAM rozmiar bufora tak naprawdę nie ma znaczenia, ponieważ po prostu ciągniesz niektóre oczekujące bajty i możesz pobrać więcej w następnym wywołaniu. Po prostu wybierz rozmiar bufora, na który cię stać.

Dla SOCK_DGRAM gniazda, otrzymasz pasującą część wiadomości oczekującej, a reszta zostanie odrzucona. Rozmiar datagramu oczekującego można uzyskać za pomocą następującego ioctl:

#include <sys/ioctl.h>
int size;
ioctl(sockfd, FIONREAD, &size);

Alternatywnie możesz użyć znaczników MSG_PEEK i MSG_TRUNC wywołania recv(), aby uzyskać oczekiwanie rozmiar datagramu.

ssize_t size = recv(sockfd, buf, len, MSG_PEEK | MSG_TRUNC);

Wtedy możesz po prostu malloc() bufor i pobrać datagram.

 2
Author: smoku,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-09 18:49:19

Nie ma absolutnej odpowiedzi na twoje pytanie, ponieważ technologia zawsze wiąże się z konkretną implementacją. Zakładam, że komunikujesz się w UDP, Ponieważ rozmiar bufora przychodzącego nie powoduje problemów w komunikacji TCP.

Zgodnie z RFC 768, Rozmiar pakietu (wraz z nagłówkiem) dla UDP może wynosić od 8 do 65 515 bajtów. Tak więc rozmiar zabezpieczenia dla bufora przychodzącego będzie wynosił 65 507bytes (~64KB)

Jednak nie wszystkie duże pakiety mogą być prawidłowo kierowane przez sieć urządzenia, zobacz istniejącą dyskusję, aby uzyskać więcej informacji:

Jaki jest optymalny rozmiar pakietu UDP dla maksymalnej przepustowości?
jaki jest największy Bezpieczny Rozmiar pakietu UDP w Internecie

 1
Author: YeenFei,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:37

16KB jest w porządku; jeśli używasz gigabit ethernet, każdy pakiet może mieć rozmiar 9kb.

 -4
Author: Andrew McGregor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-19 00:46:48