소켓 라이브러리에서 recv를 호출 할 때 recv 버퍼의 크기는 얼마입니까?


129

C의 소켓 라이브러리에 대해 몇 가지 질문이 있습니다. 다음은 내 질문에서 참조 할 코드 스 니펫입니다.

char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);
  1. recv_buffer를 얼마나 크게 만들 수 있습니까? 3000을 사용하고 있지만 임의적입니다.
  2. recv()버퍼보다 큰 패킷을 받으면 어떻게됩니까 ?
  3. recv를 다시 호출하지 않고 전체 메시지를 받았는지, 수신 할 것이 없을 때 영원히 기다리도록하려면 어떻게해야합니까?
  4. 공간이 부족할 염려없이 버퍼에 계속 추가 할 수 있도록 고정 된 양의 공간이없는 버퍼를 만들 수있는 방법이 있습니까? 아마도 버퍼에 strcat대한 최신 recv()응답 을 연결하는 데 사용 합니까?

한 번에 많은 질문이 있다는 것을 알고 있지만 응답에 크게 감사드립니다.

답변:


230

이러한 질문에 대한 답변은 TCP / IP 내 에서 스트림 소켓 ( SOCK_STREAM) 또는 데이터 그램 소켓 ( SOCK_DGRAM)을 사용하는지에 따라 달라지며 , 전자는 TCP에 해당하고 후자는 UDP에 해당합니다.

버퍼가 얼마나 크게 전달되는지 어떻게 알 수 recv()있습니까?

  • SOCK_STREAM: 너무 중요하지 않습니다. 당신의 프로토콜이 트랜잭션 / 인터랙티브 프로토콜이라면, 당신이 예상 할 수있는 가장 큰 개별 메시지 / 명령을 담을 수있는 크기를 선택하십시오 (3000은 괜찮을 것입니다). 프로토콜이 대량 데이터를 전송하는 경우 더 큰 버퍼가 더 효율적일 수 있습니다. 일반적으로 소켓의 커널 수신 버퍼 크기와 비슷합니다 (종종 256kB 정도).

  • SOCK_DGRAM: 응용 프로그램 레벨 프로토콜이 전송하는 가장 큰 패킷을 보유 할 수있을 정도로 큰 버퍼를 사용하십시오. UDP를 사용하는 경우 일반적으로 응용 프로그램 수준 프로토콜은 약 1400 바이트보다 큰 패킷을 보내지 않아야합니다. 패킷은 반드시 조각화하고 다시 어셈블해야하기 때문입니다.

recv버퍼보다 큰 패킷을 가져 오면 어떻게됩니까 ?

  • SOCK_STREAM: 스트림 소켓에는 패킷 개념이 없으므로 연속 바이트 스트림이므로 질문이 실제로 의미가 없습니다. 버퍼에 여유 공간이있는 것보다 읽을 수있는 바이트가 더 많으면 OS에 의해 큐에 대기하고 다음에 호출 할 수 recv있습니다.

  • SOCK_DGRAM: 초과 바이트가 삭제됩니다.

전체 메시지를 받았는지 어떻게 알 수 있습니까?

  • SOCK_STREAM: 애플리케이션 레벨 프로토콜에 메시지 끝을 판별하는 방법을 빌드해야합니다. 일반적으로 길이 접두사 (메시지 길이로 각 메시지를 시작) 또는 메시지 끝 구분 기호 (예 : 텍스트 기반 프로토콜의 줄 바꿈) 일 수 있습니다. 덜 사용되는 세 번째 옵션은 각 메시지에 대해 고정 된 크기를 요구하는 것입니다. 이러한 옵션의 조합도 가능합니다 (예 : 길이 값을 포함하는 고정 크기 헤더).

  • SOCK_DGRAM: 단일 recv호출은 항상 단일 데이터 그램을 반환합니다.

고정 된 공간을 가지지 않고 버퍼를 만들 수있는 방법이 있습니까? 그래서 공간이 부족할 염려없이 계속 추가 할 수 있습니까?

그러나 버퍼를 사용하여 버퍼의 크기를 조정할 수 있습니다 realloc()(원래 malloc()또는로 할당 된 경우 calloc()).


1
사용중인 프로토콜의 메시지 끝에 "/ r / n / r / n"이 있습니다. 그리고 나는 while while 루프를 가지고 있습니다. 내가 recv라고 부르고 recv_buffer의 시작 부분에 메시지를 놓습니다. 내 while 문은 다음과 같습니다 while ((! (strstr (recv_buffer, "\ r \ n \ r \ n")); 내 질문은 하나의 recv가 "\ r \ n"을 얻을 수 있습니까? 다음 recv는 "\ r \ n"을 얻습니다. 그래서 나의 while 상태는 결코 실현되지 않습니까?
adhanlon

3
그렇습니다. 완전한 메시지가없는 경우 루프를 반복하고 다음 메시지에서 다음 메시지의 바이트를 recv부분 메시지 다음에 버퍼 에 채우면이 문제를 해결할 수 있습니다 . strstr()채워진 원시 버퍼 에는 사용하지 않아야합니다. 널 ( recv()null) 터미네이터가 포함되어 있다는 보장이 없으므로 strstr()충돌 이 발생할 수 있습니다 .
caf

3
UDP의 경우 1400 바이트 이상의 UDP 패킷을 보내는 데 아무런 문제가 없습니다. 조각화는 완벽하게 합법적이고 IP 프로토콜의 기본 부분입니다 (IPv6에서도 마찬가지이지만 초기 발신자는 항상 조각화를 수행해야 함). 64KB 버퍼를 사용하면 UDP의 경우 항상 저장됩니다. IP 패킷 (v4 또는 v6)의 크기가 64KB보다 크거나 (조각화되지 않은 경우에도) 헤더 IIRC도 포함되므로 데이터는 항상 64KB 미만이어야합니다.
Mecki

1
@caf recv ()를 호출 할 때마다 버퍼를 비워야합니까? 코드 루프를보고 데이터를 수집 한 다음 다시 반복하여 더 많은 데이터를 수집해야합니다. 그러나 버퍼가 가득 차면 버퍼에 할당 된 메모리 양을 쓰기로 인한 메모리 위반을 피하기 위해 비워 둘 필요가 없습니까?
Alex_Nabu

1
@Alex_Nabu : 공간이 남아있는 한 비워 둘 필요가 없으며 공간이 남아있는 것 recv()보다 더 많은 바이트를 쓰지 않아도 됩니다.
caf

16

TCP와 같은 스트리밍 프로토콜의 경우 거의 모든 크기로 버퍼를 설정할 수 있습니다. 즉, 4096 또는 8192와 같이 2의 거듭 제곱 인 공통 값이 권장됩니다.

더 많은 데이터가 있다면 버퍼가 무엇인지, 다음에 호출 할 때 커널에 저장됩니다 recv.

예, 버퍼를 계속 키울 수 있습니다. offset idx에서 시작하여 버퍼 중간에 recv를 수행 할 수 있습니다 .

recv(socket, recv_buffer + idx, recv_buffer_size - idx, 0);

6
2의 거듭 제곱은 여러 가지 방법으로 더 효율적일 수 있으며 강력하게 제안됩니다.
Yann Ramin

3
@theatrus를 정교하게 살펴보면 모듈러스 연산자를 비트 단위 및 마스크 (예 : x % 1024 == x & 1023)로 대체 할 수 있고 정수 나누기를 오른쪽 시프트 연산 (예 : x / 1024 = = x / 2 ^ 10 == x >> 10)
vicatcu

15

SOCK_STREAM소켓 이 있으면 recv스트림에서 "처음 3000 바이트까지"를 가져옵니다. 얼마나 큰 버퍼를 만드는지에 대한 명확한 지침은 없습니다.

당신이있는 경우 SOCK_DGRAM소켓을, 그리고 데이터 그램 버퍼보다 큰 경우, recv데이터 그램의 첫 부분, -1을 반환로 버퍼를 채우고 세트 EMSGSIZE에 errno를. 불행히도, 프로토콜이 UDP 인 경우, 이는 UDP가 신뢰할 수없는 프로토콜 이라고 불리는 이유 중 일부인 데이터 그램이 손실됨을 의미합니다. 후자를 잘 알고 있음에도 불구하고 TCP / IP 제품군에서 이름을 하나 ;-).

버퍼를 동적으로 늘리려면 처음에 버퍼를 할당하고 필요에 따라 malloc사용하십시오 realloc. 그러나 그것은 recvUDP 소스에서 도움이되지 않습니다 .


7
64KB 버퍼를 사용하면 UDP는 항상 최대 하나의 UDP 패킷을 반환하고 (여러 개가 소켓 버퍼에있는 경우에도) UDP 패킷이 64KB를 초과 할 수 없으므로 (IP 조각은 최대 64KB 일 수 있음) 절대적으로 안전하고 보장하므로 UDP 소켓에서 recv하는 동안 데이터가 손실되지 않습니다.
Mecki

7

들어 SOCK_STREAM그냥 기다리고 바이트의 일부를 당기는 당신은 다음의 호출에 더 검색 할 수 있기 때문에 소켓 버퍼 크기는별로 중요하지 않습니다. 당신이 감당할 수있는 버퍼 크기를 선택하십시오.

들어 SOCK_DGRAM소켓, 당신은 대기 메시지의 피팅 부분을 얻을 것이다 나머지는 삭제됩니다. 다음 ioctl을 사용하여 대기 데이터 그램 크기를 얻을 수 있습니다.

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

또는 호출 의 MSG_PEEKMSG_TRUNC플래그 recv()를 사용하여 대기 데이터 그램 크기를 얻을 수 있습니다.

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

MSG_PEEK대기 메시지를 엿볼 필요 가 있습니다. recv는 잘리지 않은 실제 크기를 반환합니다. MSG_TRUNC현재 버퍼를 오버플로하지 않아도 됩니다.

그런 다음 malloc(size)실제 버퍼와 recv()데이터 그램 만 가능합니다.


MSG_PEEK | MSG_TRUNC는 의미가 없습니다.
Lorne의 후작

3
MSG_PEEK가 대기 메시지를 들여다보고 (수신하지 않음) 크기를 얻으려면 (recv는 잘리지 않은 실제 크기를 리턴 함) 현재 버퍼를 오버 플로우하지 않으려면 MSG_TRUNC가 필요합니다. 크기가 확보되면 올바른 버퍼를 할당하고 대기 메시지를 수신 (피킹하지 않고 잘라 내지 않음)합니다.
smokku

@Alex Martelli는 64KB가 UDP 패킷의 최대 크기라고 말합니다. 64KB malloc()버퍼 MSG_TRUNC라면 불필요합니까?
mLstudent33

1
IP 프로토콜은 조각화를 지원하므로 데이터 그램이 단일 패킷보다 클 수 있습니다. 조각화되어 여러 패킷으로 전송됩니다. 또한 SOCK_DGRAM뿐만 아니라 UDP입니다.
smokku

1

기술은 항상 구현에 따라 달라지기 때문에 귀하의 질문에 대한 절대 대답은 없습니다. 들어오는 버퍼 크기가 TCP 통신에 문제를 일으키지 않기 때문에 UDP로 통신한다고 가정합니다.

RFC 768 에 따르면 UDP의 패킷 크기 (헤더 포함)는 8 ~ 65515 바이트 범위 일 수 있습니다. 따라서 수신 버퍼의 내결함성 크기는 65507 바이트 (~ 64KB)입니다.

그러나 네트워크 장치가 모든 대형 패킷을 올바르게 라우팅 할 수있는 것은 아닙니다. 자세한 내용은 기존 설명을 참조하십시오.

최대 처리량을위한 최적의 UDP 패킷 크기는 얼마입니까?
인터넷에서 가장 큰 안전한 UDP 패킷 크기는 무엇입니까


-4

16kb는 옳습니다. 기가비트 이더넷을 사용하는 경우 각 패킷의 크기는 9kb 일 수 있습니다.


3
TCP 소켓은 스트림입니다. 즉, recv는 여러 패킷에서 누적 된 데이터를 반환 할 수 있으므로 패킷 크기는 TCP와 전혀 관련이 없습니다. UDP의 경우 각 recv 호출은 최대 하나의 UDP 패킷을 반환합니다. 여기서 패킷 크기는 관련이 있지만 올바른 패킷 크기는 약 64KB입니다 .UDP 패킷은 필요할 경우 조각화 될 수 있으며 종종 조각화 될 수 있습니다. 그러나 IP 패킷은 조각화를하지 않아도 64KB를 초과 할 수 없으므로 UDP 소켓에서 recv는 최대 64KB를 반환 할 수 있습니다 (그리고 반환되지 않은 것은 현재 패킷에 대해 버려집니다!)
Mecki
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.