2010-07-25 2 views
2

오늘은 Delphi 2010과 함께 제공되는 Indy 10을 사용하여 이상한 행동에 직면했습니다. 여기에 문제는 다음과 같습니다클라이언트가 Indy에서 서버에 연결할 때 IOHandler.ReadStream이 스레드를 차단하는 이유는 무엇입니까?

한다고 가정 우리는 우리의 클라이언트의 IdTcpClient, 우리의 서버 응용 프로그램에서 IdTcpServer, 우리 IdTcpServer에 대한의 OnExecute 이벤트 핸들러에서이 코드를 가지고 : 이제

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext); 
var 
    AStream: TStringStream; 
    S: string; 
begin 
    AStream := TStringStream.Create; 
    try 
    AContext.Connection.IOHandler.ReadStream(AStream); 
    S := AStream.DataString; 
    finally 
    AStream.Free; 
    end; 
end; 

, 클라이언트를 TIdTcpClient.Connect를 사용하여 서버에 연결하려고 시도합니다. 서버에서 TIdTcpServer.OnExecute가 호출되고 실행이 AContext.Connection.IOHandler.ReadStream (AStream) 줄에 도달하면 OnExecute 이벤트 처리기 내에서 실행되는 스레드가 차단됩니다.

코드를 추적 할 때 ReadLongInt가 ReadStream 내부에서 호출되어 바이트 수를 계산할 때 문제가 발생합니다. ReadLongInt는 ReadBytes를 호출합니다. ReadBytes 안에는 FInputBuffer.Size가 0입니다. 루프에서 ReadFromSource가 호출되고 결국 WinSock2에서 "select"함수를 호출하는 TIdSocketListWindows.FDSelect에 실행이 도달하고 여기서 실행이 중지되고 해당 클라이언트 연결에서 아무 것도 수신되지 않습니다. AByteCount 및 AReadUntilDisconnect 매개 변수에 값을 부여하려고 시도했지만 동작을 변경하지 않았습니다.

ReadStream을 ReadLn으로 바꾸면 서버에 연결해도 코드 실행이 차단되지 않고 클라이언트에서 보낸 데이터가 서버에 의해 읽혀집니다.

코드에 문제가 있습니까? 아니면이 버그입니까?

감사

답변

5

문제는되지 ReadStream()에, 코드입니다. 그것은 설계된대로 행동합니다.

procedure ReadStream(AStream: TStream; AByteCount: TIdStreamSize = -1; AReadUntilDisconnect: Boolean = False); virtual; 

당신은 첫 번째 매개 변수의 값을 제공하고, 그래서 다른 두 개의 매개 변수가 기본값을 사용

그것은 입력을위한 3 개의 매개 변수를 받아들입니다. AByteCount 파라미터가 -1로 설정되고 AReadUntilDisconnect 매개 변수가 false로 설정되어

, ReadStream()는 제 4 바이트가 수신 된 것으로 간주하도록 설계 (또는 IOHandler.LargeStream 프로퍼티가 true로 설정되어있는 경우 8 바이트) 길이이다되고 이후에 실제 데이터가 뒤 따른다. 그래서 ReadStream()ReadLongInt()입니다. 이 경우 ReadStream() 읽기를 중지 할뿐만 아니라 데이터를 수신하기 전에 ReadStream()이 더 나은 메모리 관리를 위해 대상 TStream의 크기를 미리 조정할 수 있습니다.

클라이언트가 실제로 데이터보다 4 바이트 (또는 8 바이트) 길이 값을 보내지 않으면 ReadStream()은 여전히 ​​실제 데이터의 시작 바이트를 길이로 해석합니다. 일반적으로 데이터에 따라 다를 수는 없지만 항상 ReadLongInt() (또는 ReadInt64())은 큰 정수 값을 반환하므로 실제 도착하지 않을 엄청난 양의 데이터를 예상하므로 결과가 무의식적으로 차단됩니다 (또는 시간 제한이 발생할 때까지 IOHandler.ReadTimeout 속성이 무제한 시간 제한으로 설정된 경우).

ReadStream()을 효과적으로 사용하려면 미리 예상되는 데이터 양 (예 : AByteCount >= 0)을 들었거나 데이터를 보낸 후 보낸 사람의 연결을 끊도록 요청하여 읽을 시간을 알아야합니다 즉 : AReadUtilDisconnect = True). AByteCount = -1AReadUtilDisconnect = False의 조합은 길이가 스트리밍에서 직접 인코딩 될 때 특별한 경우입니다.이것은 주로 발신자가 이라는 AWriteByteCount 매개 변수를 True (기본값은 False)로 설정하여 호출 할 때 사용됩니다 (이에 국한되지 않음).

텍스트가 아닌 데이터를 처리 할 때 가능한 한 항상 실제 데이터보다 데이터 길이를 먼저 보내는 것이 좋습니다. 그것은 읽기 작업을 최적화합니다. ReadStream의

서로 다른 매개 변수 조합() 다음과 같은 논리로 해결 :

  1. AByteCount = -1, AReadUtilDisconnect = 거짓 : 그때까지 계속 읽고, 길이로 해석, 4/8 바이트를 읽어 그 길이는 받아 들여진다.

  2. AByteCount < -1, AReadUtilDisconnect = False : AReadUntilDisconnect가 True이면 연결이 끊어 질 때까지 계속 읽습니다.

  3. AByteCount> -1, AReadUtilDisconnect = False : 대상 TStream의 크기를 미리 지정하고 AByteCount 바이트 수를 수신 할 때까지 계속 읽습니다.

  4. AByteCount < = -1, AReadUtilDisconnect = True : 연결이 끊길 때까지 계속 읽습니다.

  5. AByteCount> -1, AReadUtilDisconnect = True : 대상 TStream의 크기를 미리 지정하고 연결이 끊길 때까지 계속 읽습니다.

는 클라이언트가 실제로 처음에 서버로 전송하는 데이터의 종류에 따라, 기회는 ReadStream() 데이터 것을 읽을 가능성이 최선의 선택이 아니라고합니다. IOHandler에는 다양한 종류의 판독 방법이 있습니다. 예를 들어 클라이언트가 구분 된 텍스트를 보내는 경우 (특히 IOHandler.WriteLn()과 함께 보내는 경우) ReadLn()을 선택하는 것이 좋습니다.

+0

설명해 주셔서 감사합니다. Indy 문서에서 AByteCount가 -1이고 AReadUntilDisconnect가 False이면 바이트 수는 IOHandler에서 정수로 읽히지 만 수신 된 데이터의 처음 4 또는 8 바이트에서 읽히지는 몰랐습니다. AReadUntilDisconnect가 False이고 AByteCount가 -1 일 때 ReadStream은 입력 버퍼에 무언가가있는 한 읽습니다. 어쨌든, 도와 주셔서 다시 한번 감사드립니다. – vcldeveloper

+2

IOHandler의 모든 읽기 방법은 InputBuffer에서만 데이터를 가져옵니다. 다른 메서드가 내부적으로 호출하는 ReadBytes() 메서드는 InputBuffer에 각 읽기 작업에 사용할 수있는 충분한 바이트가 있는지 확인합니다. InputBuffer에 이미 4 바이트가 있으면 ReadStream()은 그대로 4 바이트를받습니다. InputBuffer가 아직 4 바이트를 가지지 않는다면, ReadBytes()는 그것을 확인하고, 이후 ReadStream()은 InputBuffer로부터 그것을받습니다. 어느 쪽이든, 모든 데이터는 소켓에서 InputBuffer로 이동하여 필요에 따라 각 읽기 메소드로 이동합니다. –

관련 문제