TsgcWebSocketClient › Eventos › OnQueueDrained
Disparado quando a fila de mensagens de saída deixa de conter mensagens e fica vazia.
property OnQueueDrained: TsgcWSQueueDrainedEvent;
// TsgcWSQueueDrainedEvent = procedure(Connection: TsgcWSConnection) of object
—
Só é disparado quando o enfileiramento de mensagens está ativo, ou seja, quando QueueOptions.Text.Level, QueueOptions.Binary.Level ou QueueOptions.Ping.Level está definido como algo diferente de qmNone. Sem uma fila não há nada para esvaziar e o evento nunca é disparado no modo qdmTransition padrão.
O manipulador é executado em linha na thread da conexão e deliberadamente não é encaminhado através de NotifyEvents, ao contrário de todos os outros eventos deste componente, portanto ele não deve tocar na interface do usuário. O desvio é intencional. Todo o valor do evento está em ser executado exatamente naquele ponto do laço da conexão, logo após a fila ser esvaziada e antes da próxima leitura, enquanto o socket ainda está ocioso, para que a aplicação possa liberar o próximo bloco sem esperar. Encaminhá-lo para a thread principal destruiria isso. Qualquer atualização de formulário, grade ou rótulo precisa ser encaminhada pela própria aplicação, com TThread.Queue, TThread.Synchronize ou uma mensagem enviada à thread principal.
As exceções levantadas dentro do manipulador são capturadas e descartadas para que o esvaziamento sempre seja concluído. Um manipulador que falha o faz silenciosamente, portanto envolva o corpo em seu próprio bloco try..except quando uma falha precisar ser reportada.
A propriedade QueueOptions.DrainedMode seleciona quando o evento é disparado. Com qdmTransition (padrão), ele é disparado uma vez por transição de não vazia para vazia, como descrito abaixo. Com qdmEveryPass, ele é disparado a cada passagem do laço da conexão, logo após o envio das filas, mesmo quando a fila já estava vazia e mesmo quando Text.Level, Binary.Level e Ping.Level são todos qmNone, de modo que não é necessário um nível de fila fictício. Neste modo o manipulador é um gancho por conexão na thread da conexão, por exemplo para enviar dados pendentes da aplicação com WriteData, de modo que toda gravação em uma conexão ocorra a partir da sua própria thread (com Binary.Level definido como qmNone, WriteData dentro do evento grava diretamente no socket). Assim como no modo de transição, ele é executado em linha e não segue NotifyEvents, portanto o manipulador deve ser rápido e não deve tocar na interface do usuário. Cada conexão copia as opções da fila quando se conecta, portanto defina DrainedMode antes de ativar o componente; as conexões já abertas mantêm o modo anterior.
No modo qdmTransition, o evento é disparado uma vez por transição de não vazia para vazia, não uma vez por passagem do laço da conexão. Ele não é um sinal de que uma rajada terminou. Se o esvaziamento acompanhar o ritmo de um produtor lento, cada mensagem pode gerar o seu próprio evento, porque cada mensagem é escrita antes de a seguinte ser enfileirada. O que o evento informa é que a fila desta conexão está vazia neste momento, nada mais.
O uso típico é o controle de fluxo. Quando o cliente transmite grandes volumes de dados, use o evento para liberar o próximo bloco, de modo que o produtor acompanhe a velocidade do socket em vez de encher a memória. Combine-o com a propriedade PendingCount de TsgcWSConnection, que informa quantas mensagens ainda estão enfileiradas para a conexão, para operar uma janela de créditos em vez de uma troca do tipo pare e espere.
A porta gerenciada .NET dispara o mesmo evento com o mesmo significado. A única diferença está na frequência com que ele chega. O esvaziamento do Delphi é executado uma vez por passagem do laço da conexão, com um intervalo de Options.ReadTimeOut milissegundos e 10 por padrão, portanto um fluxo lento de mensagens é agrupado em poucos eventos. O esvaziamento gerenciado é orientado a eventos e o mesmo fluxo lento pode gerar mais eventos. As rajadas se comportam de forma idêntica em ambos.
procedure OnQueueDrained(Connection: TsgcWSConnection);
begin
// runs on the connection thread, do not touch the user interface here
Connection.WriteData(GetNextChunk);
end;
// qdmEveryPass: runs on every pass of the connection loop
oClient.QueueOptions.DrainedMode := qdmEveryPass;
oClient.QueueOptions.Ping.Level := qmLevel1;
procedure OnQueueDrained(Connection: TsgcWSConnection);
begin
// runs on the connection thread, do not touch the user interface here
if Connection.Disconnected then
exit;
if HasPendingData then
Connection.WriteData(GetPendingData);
end;