2009-06-19 3 views
0

첫 번째 질문 :Indy 소켓 9에서 AccessViolation을받는 이유 IdTcpServer ServerExecute?

다음 루틴은 Indy 9 IdTcpServer.OnExecute 루틴의 올바른 구현입니까?

procedure TMyConnServer.ServerExecute(AContext: TIdPeerThread); 
var 
    buffSize: integer; 
    str:  string; 
begin 
    AContext.Connection.ReadFromStack(True, 600, False); 
    buffSize := AContext.Connection.InputBuffer.Size; 
    if (buffSize > 0) then 
    { Extract input buffer as string } 
    str := AContext.Connection.ReadString(buffSize); 

    { Notify connection object of received data } 
    if (AContext.Data <> nil) then 
    begin 
     TConnectionHandler(AContext.Data).Read(str); 
    end; 
    end; 
end; 

초 (실제로 더 중요한) 질문 :

지금 가끔 액세스 위반이 (주소 000000 읽기). 분명히 라인 :

AContext.Connection.ReadFromStack(True, 600, False); 

하지만 확인 AContext/연결/INPUTBUFFER/IOHandler = 전무는 전에 false 인 경우. 호출 후 (예외 발생 후) IOHandler는 nil입니다.

우리는 RAD 스튜디오/델파이 2007

답변

0

당신이 설명처럼 IOHandler이 전무가 될 수있는 유일한 방법 인 경우 분리 (라는 응용 프로그램에서 다른 스레드)에 작업자 스레드가 실행 중일 때 연결.

+0

글쎄, 네, 맞는 것 같아요. ServerExecute 함수가 중복되지 않기 때문에 (그렇습니까?) 단 하나의 CriticalSection을 사용하고 ServerExecute 내부에서 잠글 때 Disconnect를 수행 할 때 잠글 수 있습니까? – Tarnschaf

+0

TIdTCPServer는 멀티 스레드 구성 요소입니다. 각 클라이언트 연결은 자체 스레드에서 실행됩니다. 따라서 OnExecute 이벤트 처리기는 서로 다른 스레드 컨텍스트에서 동시에 여러 번 실행할 수 있습니다. –

+0

여러 스레드에서 읽거나 여러 스레드로 쓰지 않는 한 서버의 개별 클라이언트 연결에 대한 액세스를 잠글 필요가 없습니다 (일반적으로 잘못된 코드 디자인을 사용하는 것이 좋습니다). –

0

음을 사용하고, 내가 가지고있는 가장 간단한에 OnExecute 핸들러는 다음과 같이한다. (변명 대신 델파이 C++,하지만 당신은 아이디어를 얻을 수 있습니다.

void __fastcall MyPanel::DoTCPExecute(TIdPeerThread * AThread) 
{ 
    AnsiString text =AThread->Connection->ReadLn(); 
    // now do something with text 
} 

내가 볼 수있는 유일한 명백한 문제는 당신이 완전한있을 때 결정하기 위해 데이터의 "타이밍"을 사용하려는 것입니다 문자열이 있습니다. 이것은 TCP가있는 실제 no-no입니다. 문자열의 첫 번째 바이트 만 있거나 한 번에 여러 개의 문자열을 모두 가질 수 있습니다 .TCP에서는 각 "보내기"가 단일 "수신으로 끝나는 것은 아닙니다 "다른 끝에서

다른 방법으로 문자열을"구분 "해야합니다 Readln은 줄 바꿈 문자를 종결 자로 사용합니다 - 다른 방법은 각 데이터 묶음 앞에 길이 필드를 붙이는 것입니다. 나머지 데이터를 읽습니다.

+0

안녕하세요, 데이터에 줄 바꿈 구분 기호가 없으므로 ReadLn()이 옵션이 아닙니다. 데이터가 완전하지 않을 수도 있지만 (종종 그렇지 않습니다), 재 작성은 나중에 .Read (str) 메소드에서 수행됩니다. 사실 'string'데이터 유형은 원시 바이트를 전송하는 데 사용됩니다 (데이터가 나중에 필요하기 때문에). 그리고 예외 : ReadLn()은 내부적으로 다시 EAccessViolation으로 연결되는 ReadFromStack()을 호출 할 수 있습니다. – Tarnschaf

+0

데이터에 어떤 종류의 구분 기호가 있으면 해당 구분 기호를 ReadLn()에 전달할 수 있습니다. CR/LF 문자로 제한됩니다. 데이터가 다른 방법으로 구분되는 경우 (예 : 나머지 데이터를 읽는 방법을 지정하는 선행 헤더를 통해), 개별적으로 처리해야합니다. 특히 문자열 동작이 변경된 D2009로 업그레이드하는 경우 문자열을 원시 바이트 버퍼로 사용하는 것은 좋지 않습니다. 원시 바이트로 작업해야하는 경우 TBytes와 같은 실제 원시 바이트 버퍼를 사용하십시오. Indy는 raw 바이트를 읽고 쓸 수있는 메서드를 가지고 있습니다. –

0

코드는 그렇게 작동하지만, 나는 그것이 깨끗한 옵션을 생각하지 않습니다 :

if (AContext.Connection.Connected) then 
    begin 
    try 
     AContext.Connection.ReadFromStack(false, 1, false); 
    except on E: EAccessViolation do 
     // ignore 
    end; 
    end; 
    buffSize := AContext.Connection.InputBuffer.Size; 
+1

인디는 내부적으로 Connected() 호출을 처리합니다. Connected()는 InputBuffer에 아직 읽지 않은 데이터가있는 경우 소켓이 이미 닫혀있을지라도 True를 반환합니다 (이는 의도적으로 설계된 것입니다). 예외 처리기뿐만 아니라 Connected()에 대한 호출을 제거해야합니다 (TIdTCPServer가 올바른 소켓 관리에 필요한 오류를 처리 할 수 ​​있도록). 정상적으로 ReadFromStack()을 호출하고 오류를보고하도록합니다. –

관련 문제