2016-08-10 4 views
-1

WinSock2를 사용하고 있으며 모든 패키지를 한꺼번에 받고 싶습니다 (XML 파일). setsockopt를가 수신 버퍼의 크기를 설정하는 내 "주문"을 무시하는 것 때문에 는 지금은 아래와 같이 만들고있어,하지만 항상 실행에서 확인을 반환setsockopt 기능이 작동하지 않는 것 같습니다.

#define IP_BUF_SZ 2000000 
//... 
std::string sBuffer; 
long chrs_read = 0; 
int iBufSize = IP_BUF_SZ; 
//... 
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, sizeof(int)) == SOCKET_ERROR) 
    MessageBox("Unable to increase"); //never reachs here 

do 
{ 
    char* buf = (char*)MALLOCZ(IP_BUF_SZ + 1000); 
    chrs_read = recv(sockfd, buf, IP_BUF_SZ, 0); 

    if (chrs_read > 0) 
     sBuffer += std::string(buf); 

    FREE(buf); 
    buf = NULL; 
} 
while (chrs_read > 0); 

하지만 내가 좋아하는 일을하고 싶습니다 그 (한 번에) :

setsockopt(IP_STATUS[CHAN_EPIC].sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, sizeof(int); 
char* buf = (char*)MALLOCZ(IP_BUF_SZ + 1000); 
chrs_read = recv(IP_STATUS[CHAN_EPIC].sockfd, buf, IP_BUF_SZ, 0); 
sBuffer += std::string(buf); 

비고 : 내 파일이 IP_BUF_SZ이 크기보다 작다. 미리 감사드립니다.

+0

'recv' 함수의 반환 값을 확인하여'while' 루프의 모든 데이터를 수신 할 수 있으며 반환 값이'0 '이 될 때까지 읽기를 기대할 수 있습니다. – fnc12

답변

1

setsockopt(SO_RCVBUF)recv() 전화로 전체 메시지를 수신 할 수 있다고 보장하지 않습니다. 요청한 버퍼 크기를 얻지 못할 수도 있습니다. 실제 버퍼 크기를 검색하려면 getsockopt(SO_RCVBUF)을 사용해야합니다.

SO_RCVBUF은 발신자가 바이트를 읽는 것을 기다리는 블로킹을 시작하기 전에 소켓이 내부 버퍼에서 보유 할 수있는 바이트 수만을 제어합니다. 그러나 recv()은 요청한 바이트를 모두 반환한다는 것을 보장하지 않습니다 (사용자가 미리 메시지 길이를 모르기 때문에 플래그로 호출하지 않는 한이 상황에서는 권장하지 않습니다). recv()에 X 바이트를 읽도록 요청하면 Y 바이트 만 현재 사용할 수 있습니다. 여기서 Y < X, recv()은 X 바이트를 반환하고 X 바이트를 반환 할 때까지 기다리지 않습니다. 유일한 보장은 recv()이 적어도 1 바이트이고 이상 X 바이트가 아닌 을 반환한다는 것입니다. 따라서 내부 버퍼 크기를 어떻게 설정했는지에 관계없이 읽기 루프가 필요합니다.

SO_RCVBUF은 네트워크 I/O 최적화에 사용되는 단순한 옵션입니다. 코드의 읽기 논리에 실제로 의존하지 마십시오.

즉, 표시된 코드에 다른 문제가 있습니다.

루프의 반복마다 수신 버퍼를 할당하고 해제합니다. 그러지 마. 한 번 할당 한 다음 독서를 반복 한 다음 완제되면 버퍼를 비 웁니다.

recv()에서 실제로 묻는 것 이상으로 수신 버퍼를 과도하게 할당 할 필요가 없습니다. 또한 읽을 때 버퍼를 0으로 만들 필요가 없습니다. 이러한 단계는 오버 헤드가 낭비됩니다.

수신 버퍼를 std::string에 추가 할 때 반환 값이 recv() 인 것을 고려하지 않았습니다. 버퍼가 null로 종료 될 것으로 예상하는 std::string 생성자를 사용하고 있습니다. null-terminator를 제공하기 위해 버퍼를 0으로 만드는 것에 의존하고 있지만 데이터에 XML의 인코딩에 따라 고유 한 NUL 문자가 포함되어 있으면 std::string에 추가하는 내용이 잘립니다. recv()은 읽은 바이트 수를 반환합니다. std::string에 정확히 많은 바이트를 추가해야합니다.

#define IP_BUF_SZ 2000000 

//... 

std::string sBuffer; 
long chrs_read; 

//... 

int iBufSize = IP_BUF_SZ; 
int iBufVarSize = sizeof(iBufSize); 

if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, iBufVarSize) == SOCKET_ERROR) 
    MessageBox("Unable to set buffer size"); 
else if (getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, &iBufVarSize) == SOCKET_ERROR) 
{ 
    MessageBox("Unable to get buffer size"); 
    iBufSize = IP_BUF_SZ; 
} 

char* buf = (char*) malloc(iBufSize); 
if (!buf) 
    MessageBox("Unable to allocate buffer"); 
else 
{ 
    do 
    { 
     chrs_read = recv(sockfd, buf, iBufSize, 0); 
     if (chrs_read <= 0) 
     { 
      if (chrs_read == SOCKET_ERROR) 
       MessageBox("Unable to read message"); 
      break; 
     } 
     sBuffer.append(buf, chrs_read); 
    } 
    while (true); 

    free(buf); 
    buf = NULL; 
} 

참고이 논리가 소켓 연결을 끊을 때까지 읽거나 읽는 동안 오류가 발생되는 것을 :

이와 비슷한 더 많은 것을보십시오. 소켓 연결 당 하나의 메시지 만 있으면 OK입니다. 그러나 단일 연결에서 여러 XML 메시지를 보내려는 경우 더 이상 작동하지 않습니다. 하나의 메시지가 끝나는 곳과 다음 메시지가 시작되는 곳을 알 수 있도록 메시지 사이에 구분 기호를 사용해야합니다. 메시지의 길이를 지정하는 주요 헤더 일 수도 있고 메시지 끝에있는 고유 한 종결 자 시퀀스 일 수도 있습니다.

+0

도움을 주셔서 감사합니다. 그러나 왼쪽 질문이 있습니다. 내 서버 (본 코드)와 통신하기 위해 채널을 열고, 각 500ms마다 XML을 계속 전송합니다. 무슨 일이 일어나는가는 내 sochet이 여러 XML을 단 하나의 XML 만 받는다는 것입니다. 전송이 빠르기 때문입니다. 그래서, 짧게 - 내가 가지고 있어야합니다'chrs_read' 반환'-1'보다 적게하고 파일이 연결되어 있습니다. – cna

+1

@cna 내가 대답 한 마지막 단락에서 말했듯이 XML 메시지 사이에 구분 기호가 필요합니다. TCP에는 메시지 경계에 대한 개념이 없으므로이를 응용 프로그램 계층에서 처리해야합니다. 메시지의 적절한 끝에 도달 할 때까지'recv()'를 호출하고, 필요에 따라 메시지를 처리 ​​한 후, 다음 메시지의 적절한 끝에 도달 할 때까지 루프에서'recv()'를 호출한다. . 'recv()'는 그 자체로 모든 것을 할 수는 없지만, 당신 만의 논리를 필요로합니다. –

0

시스템이 제공하는만큼 큰 버퍼를 만들 수 있지만 할 수있는 방법이 없으면 TCP에서 모든 데이터를 한 번에받을 수 있습니다. 당신은 당신이 기대하는 모든 데이터를받을 때까지 반복해야한다.

귀하의 질문은 잘못된 가정을 기반으로합니다.