2014-01-15 4 views
0

У меня проблема с компонентом TIdTCPServer. Я использую его для чтения данных, отправленных удаленным сервером.TIdTCPServer OnExecute работает в бесконечном цикле

Ниже приведен код, я использую:

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext); 
const 
    START_PACKET = #11; 
    END_PACKET = #10; 
var 
    IO : TIdIOHandler; 
    c : Char; 
    a : AnsiString; 
begin 
    a := ''; 
    IO := AContext.Connection.IOHandler; 

    while (IO.InputBuffer.Size > 0) do 
    begin 
    c := IO.ReadChar; 

    if c = START_PACKET then 
    begin 
     repeat 
     c := IO.ReadChar; //(TEncoding.ASCII); 
     a := a + c; 
     until (c = END_PACKET) or (IO.InputBufferIsEmpty); 
    end; 
    end; 

    if a <> '' then 
    begin 
    //let's send replay to server 
    IO.Write(CreateReply(a)); 

    //now we need to save what we received to database 
    //I use critical section 
    try 
     EnterCriticalSection(LockDB); 

     with DataModule2.results do 
     begin 
     Close; 
     Params[0].AsDateTime := Today; 
     Params[1].AsString := a; 
     ExecSQL; 
     end; 
    finally 
     LeaveCriticalSection(LockDB); 
    end; 
    end; 
end; 

Проблема заключается в том, что когда-то мой TIdTCPServer получает некоторые данные, которые он запускает бесконечный цикл и занимает 100% CPU.

Что я здесь делаю неправильно?

ответ

2

Одна из проблем заключается в том, что вы никогда не читаете какие-либо данные, поэтому InputBuffer всегда будет пустым, и поэтому a будет всегда пустым. Событие OnExecute зацикливается, поэтому вы ничего не делаете, чтобы периодически давать процессорные временные фрагменты.

Другая проблема заключается в том, что чтение и конкатенация char-by-char очень неэффективны и не учитывает, что SizeOf(Char) является 2 в Delphi 2009+ или что ReadChar() является Unicode-осведомленным.

Попробуйте вместо этого:

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext); 
const 
    START_PACKET = #11; 
    END_PACKET = #10; 
var 
    IO : TIdIOHandler; 
    a, buf : AnsiString; 
    buflen : Integer; 
    c : AnsiChar; 
begin 
    a := ''; 
    IO := AContext.Connection.IOHandler; 

    IO.WaitFor(START_PACKET); 

    // this is just one example of how to append characters using 
    // buffering. use whatever is more comfortable for you... 

    SetLength(buf, 1024); 
    buflen := 0; 

    repeat 
    c := AnsiChar(IO.ReadByte); 
    if buflen = Length(buf) then 
    begin 
     a := a + buf; 
     buflen := 0; 
    end; 
    buf[buflen+1] := c; 
    Inc(buflen); 
    until (c = END_PACKET) or (IO.InputBufferIsEmpty); 

    if buflen > 0 then 
    begin 
    SetLength(buf, buflen); 
    a := a + buf; 
    end; 
    buf := ''; 

    //let's send replay to server 
    IO.Write(CreateReply(a)); 

    //now we need to save what we received to database 
    //I use critical section 
    EnterCriticalSection(LockDB); 
    try 
    with DataModule2.results do 
    begin 
     Close; 
     Params[0].AsDateTime := Today; 
     Params[1].AsString := a; 
     ExecSQL; 
    end; 
    finally 
    LeaveCriticalSection(LockDB); 
    end; 
end; 

В качестве альтернативы:

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext); 
const 
    START_PACKET = #11; 
    END_PACKET = $#10; 
var 
    IO : TIdIOHandler; 
    a : AnsiString; 
    c : AnsiChar; 
    i : Integer; 
begin 
    IO := AContext.Connection.IOHandler; 

    IO.WaitFor(START_PACKET); 

    if IO.InputBufferIsEmpty then 
    begin 
    IO.CheckForDataOnSource(IdTimeoutDefault); 
    IO.CheckForDisconnect; 
    end; 

    i := IO.InputBuffer.IndexOf(END_PACKET); 
    if i = -1 then i := IO.InputBuffer.Size; 

    a := IO.ReadString(i); 

    if a <> '' then 
    begin 
    //let's send replay to server 
    IO.Write(CreateReply(a)); 

    //now we need to save what we received to database 
    //I use critical section 
    EnterCriticalSection(LockDB); 
    try 
     with DataModule2.results do 
     begin 
     Close; 
     Params[0].AsDateTime := Today; 
     Params[1].AsString := a; 
     ExecSQL; 
     end; 
    finally 
     LeaveCriticalSection(LockDB); 
    end; 
    end; 
end; 
+0

'пкт: = IO.ReadLn (END_PACKET, Indy8BitEncoding) + END_PACKET;' он будет читать все карман с сервера? Я использую D7 с «indy10.1.5_d7», который, как мне кажется, не имеет ReadLn (END_PACKET, Indy8BitEncoding), что мне делать в этой ситуации? Можно ли использовать только IO.ReadLn? –

+0

'ReadLn()' читает до тех пор, пока не будет указан указанный терминатор, тогда он вернет все, что есть до терминатора, и сбрасывает сам терминатор. В моем примере ваш исходный код сохранял терминатор в вашей 'AnsiString', добавьте часть' + END_PACKET'. Действительно ли вам нужно сохранить терминатор, зависит от вас. Что касается Indy 10.1.5, он еще не поддерживал Unicode, поэтому просто опустите часть «Indy8BitEncoding», но все равно передайте терминатор «END_PACKET» в «ReadLn()». Или обновите до последней версии 10.6.0 (которую вы должны рассмотреть, чтобы делать что-либо с 10.1.5, ОЧЕНЬ старое). –

+0

Программа никогда не пропускает эту строку кода: 'pkt: = IO.ReadLn (END_PACKET) + END_PACKET;' как будто 'END_PACKET' никогда не появляется. –

Смежные вопросы