2015-01-02 11 views
3

나는 TCP/IP IOCP 서버 응용 프로그램에서 작업 해왔다.IOCP를 사용하는 TCP/IP 서버. 수신 버퍼의 가끔 데이터 손상

필자는 성능 (TCP 처리량 테스트 유틸리티와 비슷해 보입니다)을 테스트했으며 데이터 무결성을 테스트했습니다. 여기에서 "이상한 점"이 있습니다.

초기 테스트에서 테스트 클라이언트가 1MB 블록의 데이터를 반복적으로 보내고 그 블록이 순차적으로 증가하는 정수 시퀀스 인 곳으로 지정하기로 결정했습니다. 내가받은 각 데이터 버퍼가 그 버퍼의 누락 된 데이터와 일치하는지, 다른 버퍼와는 독립적으로, 즉 스레드가 완료된 수신을 처리하는 순서를 걱정할 필요가 없다는 것을 의미한다는 것을 확인할 수 있다는 아이디어입니다. (확인하려면 버퍼에서 첫 번째 정수를 추출하고 앞으로 스캔하고 클라이언트가 보내는 최대 값을 만나면 예상 값을 0으로 재설정합니다. 각 데이터가 4의 배수인지 확인하는 검사도 있습니다. (4 바이트 정수이므로)).

가끔씩 버퍼 내에서 데이터가 누락 된 무작위 블록이있는 것처럼 보이면 값이 1 씩 올라가고 한꺼번에 건너 뛰게됩니다. 코드는 꽤 단순하고 많은 부분이 아닌 것처럼 보입니다. 나는 원래 Delphi에서 테스트를 작성했지만 이러한 문제가 발생하면 Visual Studio 2010 C++에서 버전을 다시 작성하고 동일한 문제 (또는 적어도 매우 유사)를 갖는 것으로 보입니다.

실제 시스템에는 분명히 더 많은 코드가 있지만 작업 스레드에서이 코드를 거의 다룰 수 있습니다. 완료된 수신을 처리하고 버퍼의 데이터를 확인한 다음 다시 게시합니다. 처음 연결을 수락 한 후에 두 개의 겹쳐진 구조를 만들고 각각에 1MB 버퍼를 할당 한 다음 각각에 대해 WSARecv를 호출합니다. 두 번에 실수로 같은 버퍼를 공유하지 않는다고 다시 확인했습니다. 그런 다음, 다음이를 다시 실행됩니다 무엇을 꽤 많이 있습니다 : 어쩌면

DWORD numberOfBytesTransferred = 0; 
ULONG_PTR completionKey = NULL; 
PMyOverlapped overlapped = nullptr; 

while (true) 
{ 
    auto queueResult = GetQueuedCompletionStatus(iocp, &numberOfBytesTransferred, &completionKey, (LPOVERLAPPED *)&overlapped, INFINITE); 
    if (queueResult) 
    { 
     switch (overlapped->operation) 
     { 
      case tsoRecv: 
      { 
       verifyReceivedData(overlapped, numberOfBytesTransferred); // Checks the data is a sequence of incremented integers 1 after the other with no gabs 
       overlapped->overlapped = OVERLAPPED(); // Reset the OVERLAPPED structure to defaults 

       DWORD flags = 0; 
       numberOfBytesTransferred = 0; 
       auto returnCode = WSARecv(socket, &(overlapped->buffer), 1, &numberOfBytesTransferred, &flags, (LPWSAOVERLAPPED) overlapped, nullptr); 
       break; 
      } 
      default:; 
     } 
    } 
} 

나는 위의 내 간단한 테스트에 오류 또는 추가 정보의 어떤 종류를 취급하고 있지 않다? 나는 원래 IOCP 클라이언트가 데이터를 보내고 있지만 Indy 블로킹 소켓을 사용하는 Delphi에서 또 하나의 매우 간단한 것을 작성했습니다. 기본적으로 연결된 코드 라인입니다.

while true do 
begin 
    IdTCPClient.IOHandler.WriteDirect(TIdBytes(BigData), Length(BigData)); 
end; 

는 또한 다른 비동기 소켓 구성 요소를 사용하여 다른 서버를 쓰고 나는 그것이 적어도 아직, 위의 않습니다 내 IOCP 예제로 수신 된 데이터에 문제가 감지 없었어요. 나는 더 많은 코드와 아마도 컴파일 할 버전을 게시 할 수 있지만, 내가 명백한 것을 놓친 경우를 대비해 위를 게시 할 것이라고 생각했다. 소켓 하나당 하나의 수신 및 하나의 전송을 사용하는 것이 좋다고 생각하지만 성능 향상을 위해 둘 이상을 게시하는 것이 타당하다는 것을 이해합니다.

+3

Hmmm ... '또한 각 데이터가 4의 배수인지 확인하는 체크가 있습니다 (4 바이트 정수이기 때문에.) 어떻게 각 버퍼가 4 바이트의 정확한 배수를 포함하는지 보장합니다. TCP 스트림? 당신이 그 체크 아웃을하는 것이 좋습니다. –

+0

당신은 맞습니다. numberOfBytesTransferred가 4의 배수인지 확인하고 그것이 발견되면 테스트를 중단하는 것이 보장됩니다. 나는 4의 배수가되는 버퍼를 일관되게 전달했다. (나는 로컬에서 테스트 중이며 클라이언트는 연속적인 청크를 보내고 있다고 가정한다.) 체크가 트리거 된 유일한 시간은 내가 디버거를 다시 시작한 후 다음 버퍼가 홀수 일 때가 있습니다. 정수가 유효한 범위를 벗어 났으므로 명확합니다. – David

답변

1

나는 이것이 해결되었다고 믿는다 - 내 가정과 코드의 대부분은 정확했지만 특정 소켓의 경우 여러 스레드에서 WSASend 또는 WSARead를 동시에 호출 할 수 없다. 특정 소켓에 대해 송수신에 대해 여러 개의 처리되지 않은 호출이있을 수 있지만이를 시작하기위한 실제 호출은 중요한 섹션 (또는 이와 유사한)으로 직렬화해야합니다. 이것은 약간의 오해였습니다. MSDN 문서에 대한 약간의 오해였습니다. 그럴 수 있다고 생각했지만, 몇 가지 추가 동기화없이 첫 번째 버퍼가 채워지는 것을 알지 못했을 것입니다. 호출이 한 번에 하나씩 이루어 지거나 버퍼 내에서 데이터가 손상 될 수있는 경우가 아니면 전혀 안전하지 않은 것으로 보입니다.

내가 변경 한 유일한 코드는 연결 당 중요 섹션을 추가하여 이들에 대한 호출을 보호하고 아무런 문제가 없다는 것입니다. WSASend와 WSARecv를 별도로 보호 할 수는 있지만 아직 테스트하지는 않았다고 생각합니다.

더 많은 코드 예제가 포함 된 here과 관련된 훨씬 심층적 인 질문을 올렸습니다.

+0

s uch는 CS 내부에서 전화를합니다. 이와 유사하게, WSARecv() 요청은 종종 CS에 시퀀스 번호를 추가하여 버퍼 구조체에 시퀀스 번호를 추가하므로 결국 처리하는 스레드가 순서가 잘못된 경우 버퍼를 다시 연합 할 수 있습니다. –

-1

루프를 제거하십시오. 이미받은 데이터를 확인하고 새로운 비동기 읽기를 예약합니다. 그러면 읽기가 완료 될 때이 코드를 다시 입력해야합니다. 루프가 완전히 잘못되었습니다.

+0

루프가 작업자 스레드 안에 있으며 절대적으로 필요하거나 스레드가 종료됩니다. 콜백 루틴이 IOCP가 아닌 언제 제공되는지 생각하고 있을까요? 일반적인 IOCP 작업자는 GetQueuedCompletionStatus를 반복하고 대기하며 대기열에서 제외 된 완료된 요청을 처리합니다. 이전에 IOCP를 수행했는지 확신 할 수 없지만 WSARead에 대한 이전 호출이 완료되면 방금 처리 된 것을 대체 할 다른 읽기 버퍼를 제공하기 위해 WSARead에 새로운 호출을해야합니다. 다시 호출하는 동일한 작업자 스레드에 의해 처리되거나 처리되지 않을 수 있습니다. 게시 한 답변을 참조하십시오. – David