Net-Base Iris

01.06.2026

Delphi Cliant WebSocket: nascáil daingean, stopáil glan, debugáil iontaofa

Bíonn cliant WebSocket Delphi go tapa „ar bhealach éigin ceangailte“ — ach sa chleachtas oibríochta tá athnascadh, heartbeats, stopáil shlán agus cumais dífhabhtaithe fíorthábhachtach. Le wrapper praiticiúil bunaithe ar System.Net.WebSockets (le fallback) agus le slisne cód don threading agus ...

01.06.2026

Ó théama an iris go cleachtas tionscadail

Leathanaigh seirbhíse agus teicniúla oiriúnacha don alt

Cén fáth go bhfuil Delphi WebSocket Client sa chleachtas níos mó ná „Connect“

Is féidir Delphi WebSocket Client a chur le chéile i nóiméid: URL, Connect, SendText, críochnaithe. I mbogearraí corparáideacha shaincheaptha agus i réitigh bhogearraí gar don phróiseas, áfach, bíonn fadhbanna go minic ach amháin i mbainistíocht an oibríocht: scarann an Reverse Proxy na nascanna idle, bíonn NAT-Timeouts gearra ag nascanna soghluaiste nó VPN, athraíonn deimhnithe, agus ag dúnadh an phróisis bíonn sé ag crochadh toisc go bhfuil Receive-Loop fós blocáilte. Chomh maith leis sin: is cainéal fadtéarmach, stáideola é WebSocket — mar sin tá rialacha eile i bhfeidhm ná le HTTP/REST clasaiceach (Request/Response, gearrshaolach).

Sá slisín foinsí seo níl sé faoi „Hello WebSocket“, ach faoi wrapper cliant atá oiriúnach don chleachtas le:

  • Start/Stop glan (gan crochadh le linn an dúnadh),
  • Receive-Loop le Cancellation (signáil scor) seachas „Thread kill“,
  • Reconnect le Backoff (athnasc smachtaithe),
  • Heartbeat mar mhúnla iarratais (toisc nach bhfuil Ping/Pong ar fáil i ngach áit),
  • Debug- agus Trace-Hooks a chabhraíonn i gcásanna tacaíochta.

Tá an cur i bhfeidhm bunaithe ar System.Net.WebSockets (Delphi RTL; WebSocket-Client-API le TClientWebSocket). Má tá an sraith RTL seo i leaganacha níos sine neamhsheolta nó ró-shrianta, bíonn fallback trí leabharlann (m.sh. ICS) go minic úsáideach — faoi sin thíos tá míniú.

Sceitse ailtireachta: wrapper seachas glaonna WebSocket scaipthe

Earráid choitianta in iarratais fásaithe Delphi: bíonn foirmeacha UI nó modúil seirbhíse „ag labhairt go díreach le WebSocket“ agus bíonn Timer, Threads agus láimhseáil eisceachtaí scaipthe i ngach áit. Is fearr bloc soiléir le Events sainmhínithe agus meaisín staid bheag.

Coincheapa go hachomair: Backoff ciallaíonn tréimhse feithimh a fhásann céim ar chéim tar éis earráidí (m.sh. 1s, 2s, 4s …), chun na freastalaithe agus an líonra a shábháil ó shraonadh. CancellationToken is signáil scor í ón domhan .NET; i Delphi níl patrún díreach comhionann ann, ach is féidir é a shamhlú le TEvent agus le bratach „StopRequested“. TThread.Queue pleanálann cód le cur i bhfeidhm ar an bpríomhshnáithe (UI), gan an Worker a bhac; Synchronize blocálann agus is minic é an chúis le Deadlocks i slite dúnadh.

Source-Schnipsel: Delphi WebSocket Client mit Stop, Reconnect und Message-Dispatch

Tá an cód seo ar dhearadh mar „bloic oibríochta“: rang ar féidir a úsáid i VCL/FMX nó i Windows- agus Windows- agus Linux-Services (de réir leagan/ardán Delphi). Is é an croílár snáithe oibre a choinníonn an Receive-Loop agus a thuairiscíonn trí Events san iarratas.

unit Net.WebSocketClientEx;

interface

uses
System.SysUtils,
System.Classes,
System.SyncObjs,
System.Generics.Collections,
System.Net.URLClient,
System.Net.WebSockets;

type
TWsLogLevel = (llDebug, llInfo, llWarn, llError);

TWsLogEvent = reference to procedure(Level: TWsLogLevel; const Msg: string);
TWsTextEvent = reference to procedure(const Text: string);
TWsStateEvent = reference to procedure(const State: string);

TDelphiWebSocketClient = class
private
FUrl: string;
FOnLog: TWsLogEvent;
FOnText: TWsTextEvent;
FOnState: TWsStateEvent;

FStopEvent: TEvent;
FWorker: TThread;

FMinBackoffMs: Integer;
FMaxBackoffMs: Integer;
FHeartbeatSec: Integer;

procedure Log(Level: TWsLogLevel; const Msg: string);
procedure State(const S: string);

procedure Run;
function NextBackoffMs(const Prev: Integer): Integer;
function NowUtcStr: string;
public
constructor Create(const AUrl: string);
destructor Destroy; override;

procedure Start;
procedure Stop(TimeoutMs: Cardinal = 5000);

property OnLog: TWsLogEvent read FOnLog write FOnLog;
property OnText: TWsTextEvent read FOnText write FOnText;
property OnState: TWsStateEvent read FOnState write FOnState;

property MinBackoffMs: Integer read FMinBackoffMs write FMinBackoffMs;
property MaxBackoffMs: Integer read FMaxBackoffMs write FMaxBackoffMs;
property HeartbeatSec: Integer read FHeartbeatSec write FHeartbeatSec;
end;

implementation

type
TBytesBuffer = record
Data: TBytes;
Len: Integer;
end;

{ TDelphiWebSocketClient }

constructor TDelphiWebSocketClient.Create(const AUrl: string);
begin
inherited Create;
FUrl := AUrl;
FStopEvent := TEvent.Create(nil, True, False, “);

FMinBackoffMs := 500;
FMaxBackoffMs := 15000;
FHeartbeatSec := 20; // App-Heartbeat: úsáideach i gcoinne idle-timeouts taobh thiar de proxies
end;

destructor TDelphiWebSocketClient.Destroy;
begin
Stop;
FStopEvent.Free;
inherited;
end;

procedure TDelphiWebSocketClient.Start;
begin
if Assigned(FWorker) then
Exit;

FStopEvent.ResetEvent;
FWorker := TThread.CreateAnonymousThread(
procedure
begin
Run;
end);
FWorker.FreeOnTerminate := False;
FWorker.Start;
end;

procedure TDelphiWebSocketClient.Stop(TimeoutMs: Cardinal);
var
W: TThread;
begin
FStopEvent.SetEvent;

W := FWorker;
if Assigned(W) then
begin
if W.WaitFor(TimeoutMs) = wrTimeout then
Log(llWarn, ‚Stop: Níor fhreagair an Worker laistigh den ama socraithe; féidearthacht blocála sa stac líonra‘);
FreeAndNil(FWorker);
end;
end;

procedure TDelphiWebSocketClient.Log(Level: TWsLogLevel; const Msg: string);
begin
if Assigned(FOnLog) then
TThread.Queue(nil,
procedure
begin
FOnLog(Level, NowUtcStr + ‚ ‚ + Msg);
end);
end;

procedure TDelphiWebSocketClient.State(const S: string);
begin
if Assigned(FOnState) then
TThread.Queue(nil,
procedure
begin
FOnState(S);
end);
end;

function TDelphiWebSocketClient.NowUtcStr: string;
begin
Result := FormatDateTime(‚yyyy-mm-dd“T“hh:nn:ss.zzz“Z“‚, TTimeZone.Local.ToUniversalTime(Now));
end;

function TDelphiWebSocketClient.NextBackoffMs(const Prev: Integer): Integer;
var
N: Integer;
begin
if Prev <= 0 then
Exit(FMinBackoffMs);

N := Prev * 2;
if N < FMinBackoffMs then N := FMinBackoffMs;
if N > FMaxBackoffMs then N := FMaxBackoffMs;
Result := N;
end;

procedure TDelphiWebSocketClient.Run;
var
WS: TClientWebSocket;
Backoff: Integer;
LastHeartbeat: UInt64;
Msg: string;
Buf: TBytes;
Received: TWebSocketReceiveResult;
SB: TStringBuilder;
WaitRes: TWaitResult;

function StopRequested: Boolean;
begin
Result := (FStopEvent.WaitFor(0) = wrSignaled);
end;

procedure SafeClose;
begin
try
if WS.State = TWebSocketState.Open then
WS.Close(TWebSocketCloseStatus.NormalClosure, ‚client shutdown‘);
except
on E: Exception do
Log(llDebug, ‚Close: ‚ + E.ClassName + ‚: ‚ + E.Message);
end;
end;

begin
Backoff := 0;
LastHeartbeat := 0;

while not StopRequested do
begin
WS := TClientWebSocket.Create;
try
State(‚connecting‘);
Log(llInfo, ‚Connect to ‚ + FUrl);

try
// Nóta: Tá TClientWebSocket.Connect sioncrónach agus d’fhéadfadh sé bac a chur ag brath ar DNS/TLS.
// Dá bhrí sin tá sé á rith anseo sa Worker.
WS.Connect(FUrl);
except
on E: Exception do
begin
State(‚connect_failed‘);
Log(llWarn, ‚Connect failed: ‚ + E.ClassName + ‚: ‚ + E.Message);
Backoff := NextBackoffMs(Backoff);
WaitRes := FStopEvent.WaitFor(Backoff);
if WaitRes = wrSignaled then Break;
Continue;
end;
end;

State(‚open‘);
Backoff := 0;

SetLength(Buf, 16 * 1024);
SB := TStringBuilder.Create;
try
while (WS.State = TWebSocketState.Open) and (not StopRequested) do
begin
// Heartbeat mar theachtaireacht an aip, toisc nach bhfuil Ping/Pong nochtaithe go soiléir i ngach leagan de Delphi.
if (FHeartbeatSec > 0) then
begin
if (LastHeartbeat = 0) or (TThread.GetTickCount64 – LastHeartbeat >= UInt64(FHeartbeatSec) * 1000) then
begin
try
WS.Send(‚ping‘);
LastHeartbeat := TThread.GetTickCount64;
Log(llDebug, ‚Heartbeat ping sent‘);
except
on E: Exception do
begin
Log(llWarn, ‚Heartbeat send failed: ‚ + E.Message);
Break;
end;
end;
end;
end;

// Receive: bunaithe ar frámaí, dá bhrí sin StringBuilder le haghaidh fragmentála.
try
Received := WS.Receive(Buf);
except
on E: Exception do
begin
Log(llWarn, ‚Receive failed: ‚ + E.ClassName + ‚: ‚ + E.Message);
Break;
end;
end;

case Received.Kind of
TWebSocketMessageKind.Text:
begin
SB.Append(TEncoding.UTF8.GetString(Buf, 0, Received.BytesReceived));
if Received.EndOfMessage then
begin
Msg := SB.ToString;
SB.Clear;

if Assigned(FOnText) then
TThread.Queue(nil,
procedure
begin
FOnText(Msg);
end);
end;
end;

TWebSocketMessageKind.Binary:
begin
// I go leor prótacal gnó tá Text/JSON mar chaighdeán.
// Is féidir frámaí binártha a choinneáil anseo mar an gcéanna nó iad a sheachadadh díreach.
Log(llDebug, ‚Binary frame received: ‚ + Received.BytesReceived.ToString + ‚ bytes‘);
end;

TWebSocketMessageKind.Close:
begin
Log(llInfo, ‚Server requested close‘);
Break;
end;
end;

// Mini-sleep chun an CPU a chosaint i lúb an-tapa.
// Ná bí ró-fhada, nó méadóidh sé moill freagartha.
TThread.Sleep(1);
end;
finally
SB.Free;
end;

SafeClose;
State(‚closed‘);

finally
WS.Free;
end;

if StopRequested then
Break;

// Reconnect nach sauberem Close oder nach Fehlern
Backoff := NextBackoffMs(Backoff);
Log(llInfo, ‚Reconnect in ‚ + Backoff.ToString + ‚ ms‘);
WaitRes := FStopEvent.WaitFor(Backoff);
if WaitRes = wrSignaled then
Break;
end;

State(’stopped‘);
Log(llInfo, ‚Worker stopped‘);
end;

end.

Cad a dhéanann an cód go ‚difriúil‘ de rún ná samplaí tipiciúla

  • Stop gan foréigean: In ionad snáitheanna a mharú, cuireann Stop Event i bhfeidhm. Críochnaíonn an Worker na lúbanna in áiteanna sainithe. Laghdaíonn sé seo greamaithe ag an ndúnadh agus seachnaíonn sé sceitheanna acmhainní sa stac Socket.
  • Queue seachas Synchronize: Téann logging agus events trí TThread.Queue go dtí an Mainthread. Tá sé seo tábhachtach má thagann Stop/Shutdown ón UI nó ó Service-Control-Handleranna. D’fhéadfadh Synchronize blocáil má tá an Mainthread ag fanacht faoi láthair.
  • Deighilt á mheas: Is féidir téacs WebSocket teacht i bhfrámaí roinnte. Dá bhrí sin, úsáidtear TStringBuilder agus seiceáiltear EndOfMessage.
  • Heartbeat mar phrótacal aip: I go leor socruithe dúnann Idle-Timeouts nascanna (Load Balancer, nginx, Cloud WAF). Is minic go mbíonn téacs ‘ping’ éadrom mar uirlis oibriúcháin níos éifeachtaí ná brath ar “TCP keepalive” nó ar API Ping/Pong nach bhfuil ar fáil i ngach áit.

Srianta agus botúin i mbainistíocht

1) DNS, TLS agus Proxy: D’fhéadfadh Connect blocáil

TClientWebSocket.Connect sioncrónach é. Ag brath ar réiteach DNS, handshake TLS, seiceáil deimhniúcháin nó timpeallacht proxy, d’fhéadfadh sé seo roinnt soicind a thógáil. Cuirtear an cód seo go deonach i Worker. Má theastaíonn timeouts dian uait freisin, caithfidh tú ar leibhéal API a sheiceáil an dtugann do leagan Delphi roghanna timeout ar fáil, nó cuirfidh tú Connect i snáithe ar leith agus cealófar é trí loighic phróisis. Tábhachtach: De ghnáth ciallaíonn “cealú” anseo an nasc a mharcáil mar bhriste agus Worker a athbhunú, ní oibríocht Socket a mharú láithreach.

2) Idle-Timeouts: cén fáth go mbíonn Heartbeat go minic riachtanach

I líonraí corparáideacha is minic a chríochnaítear WebSocket taobh thiar de Reverse Proxy (nginx, IIS ARR) nó Load Balancer. Dúnann go leor de na comhpháirteanna seo naisc nuair nach mbítear sonraí ag sreabhadh ar feadh tréimhsí fada. Ní bhíonn “TCP keepalive” i gcónaí cumraithe go gearr go leor (agus faoi Windows go minic bíonn sé i mbeagáin nóiméad seachas soicindí). Dá bhrí sin is réiteach cobhsaí é Heartbeat ar leibhéal an iarratais. Cinntigh go bhfuil an coincheap céanna ag an freastalaí agus ag an gcliant (m.sh. ‘ping’/‘pong’ mar théacs nó JSON).

3) Snáitheanna agus UI: Caithfidh imeachtaí fanacht scartha

Mura bhfuil próiseáil OnText éadrom (parsanú JSON, rochtain ar bhunachar sonraí le BDE-athsholáthar le ceangal dúchais, nuashonruithe UI), níor chóir di an Mainthread a bhacáil go hiomlán. Soláthraíonn an wrapper ach an teachtaireacht. Is patrún tipiciúil é: cuireann OnText an payload isteach i Queue (m.sh. TThreadedQueue<string>), agus próiseálann Worker ar leith í le backpressure (ie. fad teoranta don Queue). Cuireann sé seo cosc ar an UI ó greamú le linn ualach briste agus ar an nglacadh ó bheith as a riocht.

Debugáil: cad ba cheart duit logáil nuair a bhriseann sé „uair éigin“

Tá WebSockets clúiteach as “oibríonn siad laethanta, ansin stopann siad”. Gan logging tá sé beagnach dodhéanta an fhadhb a theorannú. Pointí logála úsáideacha:

  • Táimspointe ama (UTC), URL, agus aistriú stáit (connecting/open/closed).
  • Cúis dúnadh, más ar fáil (freastalaí ag iarraidh Close vs. locht líonra).
  • Earráidí seolta Heartbeat agus eisceachtanna faighte, lena n-áirítear cineál na eisceachta.
  • Roghnach: méideanna na teachtaireachtaí a fuarthas (ní an ábhar féin), chun pléascadh sonraí a bhrath.

Má chríochnaíonn tú trí TLS: seiceáil freisin an bhfuil athruithe deimhniúcháin (éag, Issuer nua) ag comhoiriúnú go hamúil le hearráidí. I dtimpeallachtaí déine is féidir go mbeidh boscaí proxy agus DPI (Deep Packet Inspection) mar chonraitheoirí féideartha.

Leaganacha: cathain a fhreastalaíonn System.Net.WebSockets — agus cathain nach bhfuil sé go leor

System.Net.WebSockets cuí do go leor cásanna comhtháthaithe, go háirithe nuair atá sé faoi théacs/JSON, ualaí measartha agus straitéisí athnasctha soiléire. Léireoidh teorainneacha iad de réir leagan Delphi agus sprioc-ardáin:

  • Tacaíocht Ping/Pong ar iarraidh nó theoranta: fanfaidh App-Heartbeat mar an patrún láidir.
  • Amanna teorann/cealaithe i Connect/Receive ar iarraidh: beidh ort an ailtireacht a thógáil ionas go mbeidh Worker atá greamaithe ina n-aonar agus go ndéanfar an feidhmchlár fós a chríochnú go glan (m.sh. trí Prozesswatchdog nó trí Worker-Instanzen scaraithe).
  • Ualaí ard nó sruthanna binéaire: sa chás sin is fiú coincheap frémála/maothlúcháin níos láidre (m.sh. ring buffer, Binary-Event ar leith, Message-Assembler le teorainneacha).

Maidir le cásanna Legacy (sean-ghinéirí Delphi, riachtanais TLS/Proxy an-sona), bíonn leabharlanna cosúil le ICS i roinnt tionscadal níos prágmataí. Ní hé „cén leabharlann“ an t-ábhar is tábhachtaí ná mar atá, ach gur chóir dúnadh, athnascadh agus inbhreathnaitheacht (loganna/méadrachtaí) a chóireáil mar ábhair phríomhthábhachtacha.

Conclúid: Is eilimint oibríochta é Delphi WebSocket Client — le teorainneacha soiléire

Oireann WebSocket go han-mhaith do imeachtaí push, stádas beo, tuairiscí meaisín nó próisis agus mar chainéal fillte do phortálacha agus do sheirbhísí. Díríonn an wrapper taispeántais ar na gnéithe a dhéanann difríocht i réitigh ghnó dhigiteacha: athnascadh rialaithe, heartbeat i gcoinne Idle-Timeouts, próiseáil téacs atá sábháilte i láthair fragmentí agus cosán Stop nach mbíonn greamaithe le linn deployment nó update.

Fanann teorainneacha: má theastaíonn uait ráthaíochtaí crua maidir le scaoileadh Connect/Receive i bhfuinneoga ama an-ghearra, nó má ritheann tú rátaí sonraí an-ard, beidh ort dul níos doimhne i n-amanna teorann, i n-airíonna ardáin agus, más gá, i stacanna malartacha. Do chuid is mó de shínteanna comhtháthaithe agus nua-aoisiú, is bunús láidir é cliant glan-chlúdaithe, go maith logáilte mar atá thuas, a is féidir a chomhtháthú i gcórais Delphi atá fásaithe.

Má tá tú ag cur eilimint den sórt seo i ailtireacht reatha (m.sh. ailtireacht Layer-3 le sraitheanna seirbhíse agus UI soiléire) nó má bhíonn ort debug a dhéanamh ar disconnects ócáideacha faoi choinníollacha réadúla, is féidir linn é sin a chinneadh leat go sainiúil: déan teagmháil linn.

Sna hábhair ghairmiúla, tá ról tábhachtach ag Heartbeat Ping/Pong freisin nuair is gá go n-oibreoidh comhtháthuithe, sreafaí sonraí agus forbairt leanúnach go cúramach le chéile.

Pléigh tionscadal nó tionscnamh nua-aoisiú le Net-Base.

Céim eile

Nuair a éiríonn an t-ábhar seo ina thionscadal fíor, ba chóir ailtireacht, an córas reatha agus an t-oibriú a mheas le chéile go luath.

Ní hamháin go dtacaímid le ceisteanna aonair, ach freisin nuair is gá ó shlisíní cód foinse, ó ábhair legacy nó ó smaointe portail tionscadal corparáideach iontaofa a fhorbairt.

  • Measúnítear an staid reatha, an stát sprioc agus na rioscaí teicniúla le chéile.
  • Ní chuirfear REST, rochtain ar shonraí, portalí agus Rollout siar mar iarmhairtí.
  • Feiceann sibh go luath cé acu an cosán atá inbhuanaithe ó thaobh eacnamaíochta agus oibríochta.

Roinn an post

Roinn an t-alt seo go díreach

Tá LinkedIn, X, XING, Facebook, WhatsApp agus ríomhphost ar fáil láithreach. Do Instagram ullmhaímid nasc agus téacs gairid láithreach.

Ríomhphost

Osclaítear Instagram i gcluaisín nua. Cóipeáiltear an nasc agus an téacs gairid roimh ré isteach sa ghearrthaisce.